diff --git a/rosjava_helpers/build.gradle b/rosjava_helpers/build.gradle index 977be790d2eca35cad329c393a76d2eaa956db6c..6768b0f37984a6725c97e7f43b1f388dd9cfab05 100644 --- a/rosjava_helpers/build.gradle +++ b/rosjava_helpers/build.gradle @@ -1,6 +1,6 @@ -apply plugin: 'java' - dependencies { compile project(':rosjava') compile 'org.yaml:snakeyaml:[1.17, 1.18)' -} \ No newline at end of file + testCompile 'junit:junit:4.8.2' + testCompile project(':rosjava').sourceSets.test.output +} diff --git a/rosjava_helpers/src/main/java/org/ros/helpers/ParameterLoaderNode.java b/rosjava_helpers/src/main/java/org/ros/helpers/ParameterLoaderNode.java index 25fcdd647e4573ddb8936f08975a80c5e296329c..3e8f8a4bf819da68fd8b343757fb9d249fc48639 100644 --- a/rosjava_helpers/src/main/java/org/ros/helpers/ParameterLoaderNode.java +++ b/rosjava_helpers/src/main/java/org/ros/helpers/ParameterLoaderNode.java @@ -16,6 +16,8 @@ package org.ros.helpers; +import com.google.common.base.Preconditions; + import org.apache.commons.logging.Log; import org.ros.namespace.GraphName; import org.ros.node.AbstractNodeMain; @@ -47,36 +49,39 @@ public class ParameterLoaderNode extends AbstractNodeMain { * Default constructor * @param resources Array of resources with their respective namespace to load. */ - public ParameterLoaderNode(ArrayList<Resource> resources) { + public ParameterLoaderNode(List<Resource> resources) { + Preconditions.checkNotNull(resources); for (Resource r : resources) { + Preconditions.checkNotNull(r.inputStream); addSingleYmlInput(r.inputStream, r.namespace == null ? "" : r.namespace); } } private void addSingleYmlInput(InputStream ymlInputStream, String namespace) { - this.params.add(new LoadedResource((new Yaml()).load(ymlInputStream), namespace)); + Object loadedYaml = new Yaml().load(ymlInputStream); + if (loadedYaml != null && loadedYaml instanceof Map<?, ?>) { + this.params.add(new LoadedResource(Map.class.cast(loadedYaml), namespace)); + } } - @SuppressWarnings("unchecked") - private void addParams(ParameterTree parameterTree, String namespace, Map<String, Object> params) { - for (Map.Entry<String, Object> e : params.entrySet()) { - String fullKeyName = namespace + "/" + e.getKey(); + private void addParams(ParameterTree parameterTree, String namespace, Map<?, ?> params) { + for (Map.Entry<?, ?> e : params.entrySet()) { + String fullKeyName = namespace + "/" + e.getKey().toString(); if (log != null) { - log.info("Loading parameter " + fullKeyName + " \nValue = " + e.getValue()); + log.debug("Loading parameter " + fullKeyName + " \nValue = " + e.getValue()); } - if (e.getValue() instanceof String) { - parameterTree.set(fullKeyName, (String)e.getValue()); + parameterTree.set(fullKeyName, String.class.cast(e.getValue())); } else if (e.getValue() instanceof Integer) { - parameterTree.set(fullKeyName, (Integer)e.getValue()); + parameterTree.set(fullKeyName, Integer.class.cast(e.getValue())); } else if (e.getValue() instanceof Double) { - parameterTree.set(fullKeyName, (Double)e.getValue()); + parameterTree.set(fullKeyName, Double.class.cast(e.getValue())); } else if (e.getValue() instanceof Map) { - parameterTree.set(fullKeyName, (Map)e.getValue()); + parameterTree.set(fullKeyName, Map.class.cast(e.getValue())); } else if (e.getValue() instanceof Boolean) { - parameterTree.set(fullKeyName, (Boolean)e.getValue()); + parameterTree.set(fullKeyName, Boolean.class.cast(e.getValue())); } else if (e.getValue() instanceof List) { - parameterTree.set(fullKeyName, (List)e.getValue()); + parameterTree.set(fullKeyName, List.class.cast(e.getValue())); } else if (log != null) { log.debug("I don't know what type parameter " + fullKeyName + " is. Value = " + e.getValue()); log.debug("Class name is: " + e.getValue().getClass().getName()); @@ -95,18 +100,16 @@ public class ParameterLoaderNode extends AbstractNodeMain { @Override public void onStart(ConnectedNode connectedNode) { - if (params != null) { - ParameterTree parameterTree = connectedNode.getParameterTree(); - log = connectedNode.getLog(); - - // TODO: For some reason, setting the / param when using a rosjava master doesn't work - // It does work fine with an external master, and also setting other params of any type - for (LoadedResource r : params) { - addParams(parameterTree, r.namespace, r.resource); - } + ParameterTree parameterTree = connectedNode.getParameterTree(); + log = connectedNode.getLog(); - connectedNode.shutdown(); + // TODO: For some reason, setting the / param when using a rosjava master doesn't work + // It does work fine with an external master, and also setting other params of any type + for (LoadedResource r : params) { + addParams(parameterTree, r.namespace, r.resource); } + + connectedNode.shutdown(); } /** @@ -128,11 +131,11 @@ public class ParameterLoaderNode extends AbstractNodeMain { * keep the code simple. */ private class LoadedResource { - public Map<String, Object> resource; - public String namespace; + private Map<?, ?> resource; + private String namespace; - LoadedResource(Object resource, String namespace) { - this.resource = (Map<String, Object>) resource; + LoadedResource(Map resource, String namespace) { + this.resource = resource; this.namespace = namespace; } } diff --git a/rosjava_helpers/src/test/java/org/ros/helpers/ParameterLoaderNodeTest.java b/rosjava_helpers/src/test/java/org/ros/helpers/ParameterLoaderNodeTest.java new file mode 100644 index 0000000000000000000000000000000000000000..42592027098c55a3e82787d43c599ba32e92b99c --- /dev/null +++ b/rosjava_helpers/src/test/java/org/ros/helpers/ParameterLoaderNodeTest.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2018 Ekumen, 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.ros.helpers; + + +import org.apache.commons.logging.Log; +import org.junit.Before; +import org.junit.Test; +import org.ros.RosTest; +import org.ros.exception.ParameterNotFoundException; +import org.ros.namespace.GraphName; +import org.ros.node.AbstractNodeMain; +import org.ros.node.ConnectedNode; +import org.ros.node.DefaultNodeListener; +import org.ros.node.Node; +import org.ros.node.NodeListener; +import org.ros.node.parameter.ParameterTree; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + + +/** + * @author jubeira@ekumenlabs.com (Juan I. Ubeira) + */ +public class ParameterLoaderNodeTest extends RosTest { + + private ParameterTree parameters; + private Log log; + + @Before + public void setup() throws InterruptedException { + final CountDownLatch latch = new CountDownLatch(1); + nodeMainExecutor.execute(new AbstractNodeMain() { + @Override + public GraphName getDefaultNodeName() { + return GraphName.of("node_name"); + } + + @Override + public void onStart(ConnectedNode connectedNode) { + parameters = connectedNode.getParameterTree(); + log = connectedNode.getLog(); + latch.countDown(); + } + }, nodeConfiguration); + assertTrue(latch.await(1, TimeUnit.SECONDS)); + } + + @Test + public void testParameterLoad() throws InterruptedException { + final String namespace = "foo"; + List<ParameterLoaderNode.Resource> resourceList = new ArrayList<ParameterLoaderNode.Resource>() {{ + add(new ParameterLoaderNode.Resource(getClass().getResourceAsStream("/parameters.yaml"), "")); + add(new ParameterLoaderNode.Resource(getClass().getResourceAsStream("/parameters.yaml"), namespace)); + }}; + ParameterLoaderNode parameterLoaderNode = new ParameterLoaderNode(resourceList); + + final CountDownLatch parameterNodeLatch = new CountDownLatch(1); + nodeMainExecutor.execute(parameterLoaderNode, nodeConfiguration, new ArrayList<NodeListener>() {{ + add(new DefaultNodeListener() { + @Override + public void onShutdown(Node node) { + parameterNodeLatch.countDown(); + } + }); + }}); + + assertTrue(parameterNodeLatch.await(1, TimeUnit.SECONDS)); + + try { + // Without namespace. + assertEquals("bar", parameters.getString("/string_param")); + assertEquals(1823, parameters.getInteger("/int_param")); + assertEquals(1.74, parameters.getDouble("/double_param"), 0.001); + assertEquals(false, parameters.getBoolean("/boolean_param")); + List<?> list = parameters.getList("/list_param"); + assertEquals("Hello", list.get(0)); + assertEquals(1, list.get(1)); + assertEquals(2.3, list.get(2)); + assertEquals(true, list.get(3)); + + // With namespace. + assertEquals("bar", parameters.getString(namespace + "/string_param")); + assertEquals(1823, parameters.getInteger(namespace + "/int_param")); + assertEquals(1.74, parameters.getDouble(namespace + "/double_param"), 0.001); + assertEquals(false, parameters.getBoolean(namespace + "/boolean_param")); + list = parameters.getList(namespace + "/list_param"); + assertEquals("Hello", list.get(0)); + assertEquals(1, list.get(1)); + assertEquals(2.3, list.get(2)); + assertEquals(true, list.get(3)); + } catch (ParameterNotFoundException e) { + log.error("Error: " + e.getMessage()); + fail(); + } + } + + @Test + public void testEmptyYaml() throws InterruptedException { + List<ParameterLoaderNode.Resource> resourceList = new ArrayList<ParameterLoaderNode.Resource>() {{ + add(new ParameterLoaderNode.Resource(getClass().getResourceAsStream("/empty.yaml"), "")); + }}; + ParameterLoaderNode parameterLoaderNode = new ParameterLoaderNode(resourceList); + + final CountDownLatch parameterNodeLatch = new CountDownLatch(1); + nodeMainExecutor.execute(parameterLoaderNode, nodeConfiguration, new ArrayList<NodeListener>() {{ + add(new DefaultNodeListener() { + @Override + public void onShutdown(Node node) { + parameterNodeLatch.countDown(); + } + }); + }}); + + // No exceptions shall be thrown on node execution, and it should shut down properly. + assertTrue(parameterNodeLatch.await(1, TimeUnit.SECONDS)); + } +} diff --git a/rosjava_helpers/src/test/resources/empty.yaml b/rosjava_helpers/src/test/resources/empty.yaml new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/rosjava_helpers/src/test/resources/parameters.yaml b/rosjava_helpers/src/test/resources/parameters.yaml new file mode 100644 index 0000000000000000000000000000000000000000..4c2c5faf0f1f6e0e2e3ce95f00453b48a5f21df4 --- /dev/null +++ b/rosjava_helpers/src/test/resources/parameters.yaml @@ -0,0 +1,9 @@ + string_param: "bar" + int_param: 1823 + double_param: 1.74 + boolean_param: false + list_param: + - "Hello" + - 1 + - 2.3 + - true \ No newline at end of file