From 59342a32cc6f4e89acba1e955dcaad5c9e37d915 Mon Sep 17 00:00:00 2001 From: rick Date: Tue, 16 Dec 2025 13:21:03 +0100 Subject: [PATCH 1/4] feat: implement integration tests for fibonacci example and cancel all goals. --- .../roslib/test/examples/fibonacci.example.ts | 214 +++++++++++++----- 1 file changed, 154 insertions(+), 60 deletions(-) diff --git a/packages/roslib/test/examples/fibonacci.example.ts b/packages/roslib/test/examples/fibonacci.example.ts index a4b636284..abd9220f2 100644 --- a/packages/roslib/test/examples/fibonacci.example.ts +++ b/packages/roslib/test/examples/fibonacci.example.ts @@ -1,65 +1,159 @@ import { describe, it, expect } from "vitest"; import * as ROSLIB from "../../src/RosLib.ts"; -// Noetic is the only version of ROS 1 we support, so we skip based on distro name -// instead of adding extra plumbing for ROS_VERSION. -describe.skipIf(process.env["ROS_DISTRO"] !== "noetic")( - "ROS 1 Fibonacci Example", - function () { - it( - "Fibonacci", - () => - new Promise((done) => { - const ros = new ROSLIB.Ros({ - url: "ws://localhost:9090", - }); - /* - * The ActionClient - * ---------------- - */ - - const fibonacciClient = new ROSLIB.ActionClient({ - ros: ros, - serverName: "/fibonacci", - actionName: "actionlib_tutorials/FibonacciAction", - }); - - // Create a goal. - const goal = new ROSLIB.Goal({ - actionClient: fibonacciClient, - goalMessage: { - order: 7, - }, - }); - - // Print out their output into the terminal. - const items = [ - { sequence: [0, 1, 1] }, - { sequence: [0, 1, 1, 2] }, - { sequence: [0, 1, 1, 2, 3] }, - { sequence: [0, 1, 1, 2, 3, 5] }, - { sequence: [0, 1, 1, 2, 3, 5, 8] }, - { sequence: [0, 1, 1, 2, 3, 5, 8, 13] }, - { sequence: [0, 1, 1, 2, 3, 5, 8, 13, 21] }, - ]; - goal.on("feedback", function (feedback) { - expect(feedback).to.eql(items.shift()); - }); - goal.on("result", function (result) { - expect(result).to.eql({ sequence: [0, 1, 1, 2, 3, 5, 8, 13, 21] }); +describe("Fibonacci Example", function () { + // Noetic is the only version of ROS 1 we support, so we skip based on distro name + // instead of adding extra plumbing for ROS_VERSION. + it.skipIf(process.env["ROS_DISTRO"] !== "noetic")( + "Fibonacci ROS 1", + () => + new Promise((done) => { + const ros = new ROSLIB.Ros({ + url: "ws://localhost:9090", + }); + /* + * The ActionClient + * ---------------- + */ + + const fibonacciClient = new ROSLIB.ActionClient({ + ros: ros, + serverName: "/fibonacci", + actionName: "actionlib_tutorials/FibonacciAction", + }); + + // Create a goal. + const goal = new ROSLIB.Goal({ + actionClient: fibonacciClient, + goalMessage: { + order: 7, + }, + }); + + // Print out their output into the terminal. + const items = [ + { sequence: [0, 1, 1] }, + { sequence: [0, 1, 1, 2] }, + { sequence: [0, 1, 1, 2, 3] }, + { sequence: [0, 1, 1, 2, 3, 5] }, + { sequence: [0, 1, 1, 2, 3, 5, 8] }, + { sequence: [0, 1, 1, 2, 3, 5, 8, 13] }, + { sequence: [0, 1, 1, 2, 3, 5, 8, 13, 21] }, + ]; + goal.on("feedback", function (feedback) { + expect(feedback).to.eql(items.shift()); + }); + goal.on("result", function (result) { + expect(result).to.eql({ sequence: [0, 1, 1, 2, 3, 5, 8, 13, 21] }); + done(); + }); + + /* + * Send the goal to the action server. + * The timeout is to allow rosbridge to properly subscribe all the + * Action topics - otherwise, the first feedback message might get lost + */ + setTimeout(function () { + goal.send(); + }, 100); + }), + 8000, + ); + + it( + "Fibonacci ROS 2", + () => + new Promise((done) => { + const ros = new ROSLIB.Ros({ + url: "ws://localhost:9090", + }); + /* + * The ActionClient + * ---------------- + */ + + const fibonacciAction = new ROSLIB.Action({ + ros, + name: "/fibonacci", + actionType: "action_tutorials_interfaces/action/Fibonacci", + }); + + const goal = { order: 8 }; + + // Print out their output into the terminal. + const items = [ + { sequence: [0, 1, 1] }, + { sequence: [0, 1, 1, 2] }, + { sequence: [0, 1, 1, 2, 3] }, + { sequence: [0, 1, 1, 2, 3, 5] }, + { sequence: [0, 1, 1, 2, 3, 5, 8] }, + { sequence: [0, 1, 1, 2, 3, 5, 8, 13] }, + { sequence: [0, 1, 1, 2, 3, 5, 8, 13, 21] }, + ]; + + /* + * Send the goal to the action server. + */ + fibonacciAction.sendGoal( + goal, + (result) => { + expect(result).to.eql({ + sequence: [0, 1, 1, 2, 3, 5, 8, 13, 21], + }); done(); - }); - - /* - * Send the goal to the action server. - * The timeout is to allow rosbridge to properly subscribe all the - * Action topics - otherwise, the first feedback message might get lost - */ - setTimeout(function () { - goal.send(); - }, 100); - }), - 8000, + }, + (feedback) => { + expect(feedback).to.eql(items.shift()); + }, + ); + }), + 8000, + ); + + it("Fibonacci ROS 2, cancel all goals", async () => { + const ros = new ROSLIB.Ros(); + await ros.connect("ws://localhost:9090"); + + let resultCalled = false; + let failedCalled = false; + + /* + * The Action + * ---------------- + */ + const fibonacciAction = new ROSLIB.Action({ + ros, + name: "/fibonacci", + actionType: "action_tutorials_interfaces/action/Fibonacci", + }); + + const goal = { order: 8 }; + + /* + * Send the goal to the action server. + */ + fibonacciAction.sendGoal( + goal, + // result callback. + () => { + resultCalled = true; + }, + // feedback callback. + undefined, + // failed callback + () => { + failedCalled = true; + }, ); - }, -); + + /* + * Cancel all goals. + */ + fibonacciAction.cancelAllGoals(); + + setTimeout(() => { + expect(failedCalled).toBe(true); + expect(resultCalled).toBe(false); + }, 500); // wait 500ms to be sure the server handled the cancel request. + }); +}); From 8ba1d3d03e10071d68b554ea8bcbec3f5de90541 Mon Sep 17 00:00:00 2001 From: rick Date: Tue, 16 Dec 2025 13:26:48 +0100 Subject: [PATCH 2/4] fix: skip ROS2 tests if you're not on ROS2 --- .../roslib/test/examples/fibonacci.example.ts | 97 ++++++++++--------- 1 file changed, 50 insertions(+), 47 deletions(-) diff --git a/packages/roslib/test/examples/fibonacci.example.ts b/packages/roslib/test/examples/fibonacci.example.ts index abd9220f2..ccb9b2d4b 100644 --- a/packages/roslib/test/examples/fibonacci.example.ts +++ b/packages/roslib/test/examples/fibonacci.example.ts @@ -60,7 +60,7 @@ describe("Fibonacci Example", function () { 8000, ); - it( + it.skipIf(process.env["ROS_VERSION"] !== "2")( "Fibonacci ROS 2", () => new Promise((done) => { @@ -110,50 +110,53 @@ describe("Fibonacci Example", function () { 8000, ); - it("Fibonacci ROS 2, cancel all goals", async () => { - const ros = new ROSLIB.Ros(); - await ros.connect("ws://localhost:9090"); - - let resultCalled = false; - let failedCalled = false; - - /* - * The Action - * ---------------- - */ - const fibonacciAction = new ROSLIB.Action({ - ros, - name: "/fibonacci", - actionType: "action_tutorials_interfaces/action/Fibonacci", - }); - - const goal = { order: 8 }; - - /* - * Send the goal to the action server. - */ - fibonacciAction.sendGoal( - goal, - // result callback. - () => { - resultCalled = true; - }, - // feedback callback. - undefined, - // failed callback - () => { - failedCalled = true; - }, - ); - - /* - * Cancel all goals. - */ - fibonacciAction.cancelAllGoals(); - - setTimeout(() => { - expect(failedCalled).toBe(true); - expect(resultCalled).toBe(false); - }, 500); // wait 500ms to be sure the server handled the cancel request. - }); + it.skipIf(process.env["ROS_VERSION"] !== "2")( + "Fibonacci ROS 2, cancel all goals", + async () => { + const ros = new ROSLIB.Ros(); + await ros.connect("ws://localhost:9090"); + + let resultCalled = false; + let failedCalled = false; + + /* + * The Action + * ---------------- + */ + const fibonacciAction = new ROSLIB.Action({ + ros, + name: "/fibonacci", + actionType: "action_tutorials_interfaces/action/Fibonacci", + }); + + const goal = { order: 8 }; + + /* + * Send the goal to the action server. + */ + fibonacciAction.sendGoal( + goal, + // result callback. + () => { + resultCalled = true; + }, + // feedback callback. + undefined, + // failed callback + () => { + failedCalled = true; + }, + ); + + /* + * Cancel all goals. + */ + fibonacciAction.cancelAllGoals(); + + setTimeout(() => { + expect(failedCalled).toBe(true); + expect(resultCalled).toBe(false); + }, 500); // wait 500ms to be sure the server handled the cancel request. + }, + ); }); From 7caa4908743644d3214729a18ad2dcd1b0ccb233 Mon Sep 17 00:00:00 2001 From: rick Date: Tue, 16 Dec 2025 13:47:16 +0100 Subject: [PATCH 3/4] feat: add CancaleAllGoals function --- packages/roslib/src/core/Action.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/roslib/src/core/Action.ts b/packages/roslib/src/core/Action.ts index 6944d89f7..adcc5c13e 100644 --- a/packages/roslib/src/core/Action.ts +++ b/packages/roslib/src/core/Action.ts @@ -134,6 +134,17 @@ export default class Action< }); } + /** + * Cancels all action goals. + */ + cancelAllGoals() { + this.ros.callOnConnection({ + op: "call_service", + service: `${this.name}/_action/cancel_goal`, + args: {}, + }); + } + /** * Advertise the action. This turns the Action object from a client * into a server. The callback will be called with every goal sent to this action. From 1028dedbc3d28aaa4bcc4533ac790ed1f27ef6f1 Mon Sep 17 00:00:00 2001 From: rick Date: Tue, 16 Dec 2025 14:23:47 +0100 Subject: [PATCH 4/4] fix: pipeline and move fibonacci items. --- .../roslib/test/examples/fibonacci.example.ts | 41 +++++++------------ 1 file changed, 15 insertions(+), 26 deletions(-) diff --git a/packages/roslib/test/examples/fibonacci.example.ts b/packages/roslib/test/examples/fibonacci.example.ts index ccb9b2d4b..dc6869919 100644 --- a/packages/roslib/test/examples/fibonacci.example.ts +++ b/packages/roslib/test/examples/fibonacci.example.ts @@ -1,6 +1,16 @@ import { describe, it, expect } from "vitest"; import * as ROSLIB from "../../src/RosLib.ts"; +const fibonacciItems = [ + { sequence: [0, 1, 1] }, + { sequence: [0, 1, 1, 2] }, + { sequence: [0, 1, 1, 2, 3] }, + { sequence: [0, 1, 1, 2, 3, 5] }, + { sequence: [0, 1, 1, 2, 3, 5, 8] }, + { sequence: [0, 1, 1, 2, 3, 5, 8, 13] }, + { sequence: [0, 1, 1, 2, 3, 5, 8, 13, 21] }, +]; + describe("Fibonacci Example", function () { // Noetic is the only version of ROS 1 we support, so we skip based on distro name // instead of adding extra plumbing for ROS_VERSION. @@ -30,18 +40,8 @@ describe("Fibonacci Example", function () { }, }); - // Print out their output into the terminal. - const items = [ - { sequence: [0, 1, 1] }, - { sequence: [0, 1, 1, 2] }, - { sequence: [0, 1, 1, 2, 3] }, - { sequence: [0, 1, 1, 2, 3, 5] }, - { sequence: [0, 1, 1, 2, 3, 5, 8] }, - { sequence: [0, 1, 1, 2, 3, 5, 8, 13] }, - { sequence: [0, 1, 1, 2, 3, 5, 8, 13, 21] }, - ]; goal.on("feedback", function (feedback) { - expect(feedback).to.eql(items.shift()); + expect(feedback).to.eql(fibonacciItems.shift()); }); goal.on("result", function (result) { expect(result).to.eql({ sequence: [0, 1, 1, 2, 3, 5, 8, 13, 21] }); @@ -60,7 +60,7 @@ describe("Fibonacci Example", function () { 8000, ); - it.skipIf(process.env["ROS_VERSION"] !== "2")( + it.skipIf(process.env["ROS_DISTRO"] !== "humble")( "Fibonacci ROS 2", () => new Promise((done) => { @@ -68,7 +68,7 @@ describe("Fibonacci Example", function () { url: "ws://localhost:9090", }); /* - * The ActionClient + * The Action * ---------------- */ @@ -80,17 +80,6 @@ describe("Fibonacci Example", function () { const goal = { order: 8 }; - // Print out their output into the terminal. - const items = [ - { sequence: [0, 1, 1] }, - { sequence: [0, 1, 1, 2] }, - { sequence: [0, 1, 1, 2, 3] }, - { sequence: [0, 1, 1, 2, 3, 5] }, - { sequence: [0, 1, 1, 2, 3, 5, 8] }, - { sequence: [0, 1, 1, 2, 3, 5, 8, 13] }, - { sequence: [0, 1, 1, 2, 3, 5, 8, 13, 21] }, - ]; - /* * Send the goal to the action server. */ @@ -103,14 +92,14 @@ describe("Fibonacci Example", function () { done(); }, (feedback) => { - expect(feedback).to.eql(items.shift()); + expect(feedback).to.eql(fibonacciItems.shift()); }, ); }), 8000, ); - it.skipIf(process.env["ROS_VERSION"] !== "2")( + it.skipIf(process.env["ROS_DISTRO"] !== "humble")( "Fibonacci ROS 2, cancel all goals", async () => { const ros = new ROSLIB.Ros();