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