diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 676de760..12a165a0 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -14,7 +14,7 @@ jobs: - uses: ros-tooling/setup-ros@0.0.25 with: required-ros-distributions: foxy - - uses: ros-tooling/action-ros-ci@0.0.19 + - uses: ros-tooling/action-ros-ci@master with: package-name: rosidl_generator_java rcljava_common rcljava target-ros2-distro: foxy diff --git a/rcljava/CMakeLists.txt b/rcljava/CMakeLists.txt index 7b31d515..6b65e4c8 100644 --- a/rcljava/CMakeLists.txt +++ b/rcljava/CMakeLists.txt @@ -188,6 +188,7 @@ ament_export_jars("share/${PROJECT_NAME}/java/${PROJECT_NAME}.jar") if(BUILD_TESTING) find_package(ament_lint_auto REQUIRED) find_package(std_msgs REQUIRED) + find_package(mockito_vendor REQUIRED) ament_lint_auto_find_test_dependencies() set(${PROJECT_NAME}_message_files @@ -243,6 +244,7 @@ if(BUILD_TESTING) # "src/test/java/org/ros2/rcljava/parameters/SyncParametersClientTest.java" "src/test/java/org/ros2/rcljava/publisher/PublisherTest.java" "src/test/java/org/ros2/rcljava/subscription/SubscriptionTest.java" + "src/test/java/org/ros2/rcljava/time/TimeSourceTest.java" "src/test/java/org/ros2/rcljava/timer/TimerTest.java" ) @@ -258,6 +260,7 @@ if(BUILD_TESTING) # "org.ros2.rcljava.parameters.SyncParametersClientTest" "org.ros2.rcljava.publisher.PublisherTest" "org.ros2.rcljava.subscription.SubscriptionTest" + "org.ros2.rcljava.time.TimeSourceTest" "org.ros2.rcljava.timer.TimerTest" ) @@ -328,6 +331,7 @@ if(BUILD_TESTING) "${builtin_interfaces_JARS}" "${rcl_interfaces_JARS}" "${rosgraph_msgs_JARS}" + "${mockito_vendor_JARS}" "${_${PROJECT_NAME}_jar_file}" "${_${PROJECT_NAME}_messages_jar_file}" APPEND_LIBRARY_DIRS diff --git a/rcljava/package.xml b/rcljava/package.xml index 9a7bcbb4..9b7de88e 100644 --- a/rcljava/package.xml +++ b/rcljava/package.xml @@ -40,6 +40,7 @@ ament_lint_auto ament_lint_common builtin_interfaces + mockito_vendor rcl_interfaces rcljava_common rmw_implementation_cmake diff --git a/rcljava/src/main/java/org/ros2/rcljava/time/TimeSource.java b/rcljava/src/main/java/org/ros2/rcljava/time/TimeSource.java index 44318c2d..c2d5d3c8 100644 --- a/rcljava/src/main/java/org/ros2/rcljava/time/TimeSource.java +++ b/rcljava/src/main/java/org/ros2/rcljava/time/TimeSource.java @@ -131,7 +131,7 @@ public rcl_interfaces.msg.SetParametersResult callback(List pa for (ParameterVariant param : parameters) { if (param.getName() == "use_sim_time") { if (param.getType() == ParameterType.PARAMETER_BOOL) { - this.timeSource.rosTimeIsActive = param.asBool(); + this.timeSource.setRosTimeIsActive(param.asBool()); } else { result.setSuccessful(false); result.setReason("'use_sim_time' parameter must be a boolean"); diff --git a/rcljava/src/test/java/org/ros2/rcljava/time/TimeSourceTest.java b/rcljava/src/test/java/org/ros2/rcljava/time/TimeSourceTest.java new file mode 100644 index 00000000..84958b86 --- /dev/null +++ b/rcljava/src/test/java/org/ros2/rcljava/time/TimeSourceTest.java @@ -0,0 +1,183 @@ +/* Copyright 2020 Open Source Robotics Foundation, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ros2.rcljava.time; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import org.junit.runner.RunWith; + +import static org.mockito.Mockito.*; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import org.ros2.rcljava.consumers.Consumer; +import org.ros2.rcljava.RCLJava; +import org.ros2.rcljava.node.Node; +import org.ros2.rcljava.parameters.ParameterVariant; +import org.ros2.rcljava.subscription.Subscription; +import org.ros2.rcljava.time.TimeSource; + +@RunWith(MockitoJUnitRunner.StrictStubs.class) +public class TimeSourceTest { + @Mock + private Node mockedNode; + + @Mock + private Clock mockedClock; + + @Mock + private Subscription mockSubscription; + + @BeforeClass + public static void setupOnce() throws Exception { + // Just to quiet down warnings + org.apache.log4j.BasicConfigurator.configure(); + + RCLJava.rclJavaInit(); + } + + @AfterClass + public static void tearDownOnce() { + RCLJava.shutdown(); + } + + @Test + public final void testEmptyConstructor() { + TimeSource timeSource = new TimeSource(); + assertFalse(timeSource.getRosTimeIsActive()); + } + + @Test + public final void testConstructorWithNode() { + when(mockedNode.getParameter("use_sim_time")).thenReturn(new ParameterVariant("use_sim_time", false)); + + TimeSource timeSource = new TimeSource(mockedNode); + assertFalse(timeSource.getRosTimeIsActive()); + } + + @Test + public final void testAttachNodeUseSimTimeFalse() { + when(mockedNode.getParameter("use_sim_time")).thenReturn(new ParameterVariant("use_sim_time", false)); + + TimeSource timeSource = new TimeSource(); + timeSource.attachNode(mockedNode); + assertFalse(timeSource.getRosTimeIsActive()); + } + + @Test + public final void testAttachNodeUseSimTimeTrue() { + when(mockedNode.getParameter("use_sim_time")).thenReturn(new ParameterVariant("use_sim_time", true)); + + TimeSource timeSource = new TimeSource(); + timeSource.attachNode(mockedNode); + assertTrue(timeSource.getRosTimeIsActive()); + } + + @Test + public final void testAttachNodeTwice() { + when(mockedNode.getParameter("use_sim_time")).thenReturn(new ParameterVariant("use_sim_time", true)); + + TimeSource timeSource = new TimeSource(); + timeSource.attachNode(mockedNode); + assertTrue(timeSource.getRosTimeIsActive()); + + // Attach the same node again + timeSource.attachNode(mockedNode); + assertTrue(timeSource.getRosTimeIsActive()); + } + + @Test + public final void testDetachNode() { + when(mockedNode.getParameter("use_sim_time")).thenReturn(new ParameterVariant("use_sim_time", true)); + + // Attaches node with ROS time active + TimeSource timeSource = new TimeSource(mockedNode); + assertTrue(timeSource.getRosTimeIsActive()); + + timeSource.detachNode(); + assertFalse(timeSource.getRosTimeIsActive()); + + // Calling detach again shouldn't change anything + timeSource.detachNode(); + assertFalse(timeSource.getRosTimeIsActive()); + } + + @Test + public final void testAttachClock() { + when(mockedClock.getClockType()).thenReturn(ClockType.ROS_TIME); + + TimeSource timeSource = new TimeSource(); + // Attaching a clock should notifiy the clock + timeSource.attachClock(mockedClock); + verify(mockedClock).setRosTimeIsActive(false); + + // Setting ROS time active should notify clock + timeSource.setRosTimeIsActive(true); + verify(mockedClock).setRosTimeIsActive(true); + } + + @Test(expected = IllegalArgumentException.class) + public final void testAttachClockInvalidType() { + TimeSource timeSource = new TimeSource(); + timeSource.attachClock(mockedClock); + } + + @Test + public final void testDetachClock() { + when(mockedClock.getClockType()).thenReturn(ClockType.ROS_TIME); + + TimeSource timeSource = new TimeSource(); + timeSource.attachClock(mockedClock); + timeSource.detachClock(mockedClock); + + // Setting ROS time active should not notify a detached clock + timeSource.setRosTimeIsActive(true); + verify(mockedClock, never()).setRosTimeIsActive(true); + } + + @Test + public final void testSetRosTimeIsActiveNoNode() { + TimeSource timeSource = new TimeSource(); + timeSource.setRosTimeIsActive(false); + assertFalse(timeSource.getRosTimeIsActive()); + timeSource.setRosTimeIsActive(true); + assertTrue(timeSource.getRosTimeIsActive()); + } + + @Test + public final void testSetRosTimeIsActiveWithNode() { + when(mockedNode.getParameter("use_sim_time")).thenReturn(new ParameterVariant("use_sim_time", false)); + when(mockedNode.createSubscription(eq(rosgraph_msgs.msg.Clock.class), anyString(), any(Consumer.class))) + .thenReturn(mockSubscription); + + TimeSource timeSource = new TimeSource(mockedNode); + timeSource.setRosTimeIsActive(false); + assertFalse(timeSource.getRosTimeIsActive()); + timeSource.setRosTimeIsActive(true); + assertTrue(timeSource.getRosTimeIsActive()); + // Expect subscription for the "/clock" topic when set active + verify(mockedNode).createSubscription(eq(rosgraph_msgs.msg.Clock.class), eq("/clock"), any(Consumer.class)); + timeSource.setRosTimeIsActive(false); + assertFalse(timeSource.getRosTimeIsActive()); + // Expect subscription removed when set not active + verify(mockedNode).removeSubscription(any(Subscription.class)); + } +} diff --git a/ros2_java_desktop.repos b/ros2_java_desktop.repos index 3a79af5d..b9772e22 100644 --- a/ros2_java_desktop.repos +++ b/ros2_java_desktop.repos @@ -19,6 +19,10 @@ repositories: type: git url: https://github.com/ros2/unique_identifier_msgs version: master + ros2_java/mockito_vendor: + type: git + url: https://github.com/ros2-java/mockito_vendor.git + version: main ros2_java/ros2_java: type: git url: https://github.com/osrf/ros2_java.git