Skip to content

Commit cd52254

Browse files
authored
liveliness, keyexpr, serialization, querier tests added. Bug in querier fixed: parameters were not passed (#199)
* liveliness test added, vscode library resolving fixed * tests for keyexpr, liveliness, serialization added * fixed bug: querier parameters were not passed, test for querier added * run_test function which prints test name added * update run_test calls to use function references instead of names * delay for flaky test added * avoid vscode errors in analyzing zenoh-ts import
1 parent 5f21533 commit cd52254

File tree

13 files changed

+674
-27
lines changed

13 files changed

+674
-27
lines changed

zenoh-plugin-remote-api/src/handle_control_message.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,7 @@ pub(crate) async fn handle_control_message(
471471
ControlMsg::QuerierGet {
472472
get_id,
473473
querier_id,
474+
parameters,
474475
encoding,
475476
payload,
476477
attachment,
@@ -501,6 +502,7 @@ pub(crate) async fn handle_control_message(
501502
add_if_some!(encoding, get_builder);
502503
add_if_some!(payload, get_builder);
503504
add_if_some!(attachment, get_builder);
505+
add_if_some!(parameters, get_builder);
504506

505507
let ws_tx = state_map.websocket_tx.clone();
506508
let finish_msg = RemoteAPIMsg::Control(ControlMsg::GetFinished { id: get_id });

zenoh-plugin-remote-api/src/interface/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,8 @@ pub enum ControlMsg {
352352
querier_id: Uuid,
353353
get_id: Uuid,
354354
#[ts(type = "string | undefined")]
355+
parameters: Option<String>,
356+
#[ts(type = "string | undefined")]
355357
encoding: Option<String>,
356358
#[ts(type = "string | undefined")]
357359
payload: Option<B64String>,

zenoh-ts/examples/deno/tsconfig.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212
"resolveJsonModule": true,
1313
"isolatedModules": true,
1414
"noEmit": true,
15+
"paths": {
16+
"@eclipse-zenoh/zenoh-ts": ["../../src/index.ts"],
17+
"@eclipse-zenoh/zenoh-ts/*": ["../../src/*"]
18+
},
1519

1620
/* Linting */
1721
"strict": true,

zenoh-ts/scripts/start.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ else
3232
echo
3333
echo "Available options:"
3434
echo
35-
echo "yarn start test [test-name] | ALL"
35+
echo "yarn start test test-name|ALL [DAEMON]"
3636
echo "yarn start example deno [example-name]"
3737
echo "yarn start example browser"
3838
echo

zenoh-ts/src/remote_api/interface/ControlMsg.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@ import type { HandlerChannel } from "./HandlerChannel.js";
44
import type { LivelinessMsg } from "./LivelinessMsg.js";
55
import type { OwnedKeyExprWrapper } from "./OwnedKeyExprWrapper.js";
66

7-
export type ControlMsg = "OpenSession" | "CloseSession" | { "Session": string } | { "NewTimestamp": string } | "SessionInfo" | { "Get": { key_expr: OwnedKeyExprWrapper, parameters: string | null, handler: HandlerChannel, id: string, consolidation: number | undefined, timeout: number | undefined, congestion_control: number | undefined, priority: number | undefined, target: number | undefined, express: boolean | undefined, encoding: string | undefined, payload: string | undefined, attachment: string | undefined, } } | { "GetFinished": { id: string, } } | { "Put": { key_expr: OwnedKeyExprWrapper, payload: B64String, encoding: string | undefined, congestion_control: number | undefined, priority: number | undefined, express: boolean | undefined, attachment: string | undefined, timestamp: string | undefined, } } | { "Delete": { key_expr: OwnedKeyExprWrapper, congestion_control: number | undefined, priority: number | undefined, express: boolean | undefined, attachment: string | undefined, timestamp: string | undefined, } } | { "DeclareSubscriber": { key_expr: OwnedKeyExprWrapper, handler: HandlerChannel, id: string, } } | { "Subscriber": string } | { "UndeclareSubscriber": string } | { "DeclarePublisher": { key_expr: OwnedKeyExprWrapper, encoding: string | undefined, congestion_control: number | undefined, priority: number | undefined, reliability: number | undefined, express: boolean | undefined, id: string, } } | { "UndeclarePublisher": string } | { "DeclareQueryable": { key_expr: OwnedKeyExprWrapper, id: string, complete: boolean, handler: HandlerChannel, } } | { "UndeclareQueryable": string } | { "DeclareQuerier": { id: string, key_expr: OwnedKeyExprWrapper, target: number | undefined, timeout: number | undefined, accept_replies: number | undefined, allowed_destination: number | undefined, congestion_control: number | undefined, priority: number | undefined, consolidation: number | undefined, express: boolean | undefined, } } | { "UndeclareQuerier": string } | { "QuerierGet": { querier_id: string, get_id: string, encoding: string | undefined, payload: string | undefined, attachment: string | undefined, handler: HandlerChannel, } } | { "Liveliness": LivelinessMsg };
7+
export type ControlMsg = "OpenSession" | "CloseSession" | { "Session": string } | { "NewTimestamp": string } | "SessionInfo" | { "Get": { key_expr: OwnedKeyExprWrapper, parameters: string | null, handler: HandlerChannel, id: string, consolidation: number | undefined, timeout: number | undefined, congestion_control: number | undefined, priority: number | undefined, target: number | undefined, express: boolean | undefined, encoding: string | undefined, payload: string | undefined, attachment: string | undefined, } } | { "GetFinished": { id: string, } } | { "Put": { key_expr: OwnedKeyExprWrapper, payload: B64String, encoding: string | undefined, congestion_control: number | undefined, priority: number | undefined, express: boolean | undefined, attachment: string | undefined, timestamp: string | undefined, } } | { "Delete": { key_expr: OwnedKeyExprWrapper, congestion_control: number | undefined, priority: number | undefined, express: boolean | undefined, attachment: string | undefined, timestamp: string | undefined, } } | { "DeclareSubscriber": { key_expr: OwnedKeyExprWrapper, handler: HandlerChannel, id: string, } } | { "Subscriber": string } | { "UndeclareSubscriber": string } | { "DeclarePublisher": { key_expr: OwnedKeyExprWrapper, encoding: string | undefined, congestion_control: number | undefined, priority: number | undefined, reliability: number | undefined, express: boolean | undefined, id: string, } } | { "UndeclarePublisher": string } | { "DeclareQueryable": { key_expr: OwnedKeyExprWrapper, id: string, complete: boolean, handler: HandlerChannel, } } | { "UndeclareQueryable": string } | { "DeclareQuerier": { id: string, key_expr: OwnedKeyExprWrapper, target: number | undefined, timeout: number | undefined, accept_replies: number | undefined, allowed_destination: number | undefined, congestion_control: number | undefined, priority: number | undefined, consolidation: number | undefined, express: boolean | undefined, } } | { "UndeclareQuerier": string } | { "QuerierGet": { querier_id: string, get_id: string, parameters: string | undefined, encoding: string | undefined, payload: string | undefined, attachment: string | undefined, handler: HandlerChannel, } } | { "Liveliness": LivelinessMsg };

zenoh-ts/src/remote_api/querier.ts

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -42,33 +42,34 @@ export class RemoteQuerier {
4242
}
4343

4444
get(
45-
_handler_type: HandlerChannel,
46-
_encoding?: string,
47-
_parameters?: string,
48-
_attachment?: Array<number>,
49-
_payload?: Array<number>,
45+
handler_type: HandlerChannel,
46+
encoding?: string,
47+
parameters?: string,
48+
attachment?: Array<number>,
49+
payload?: Array<number>,
5050
): SimpleChannel<ReplyWS> {
5151
let get_id = uuidv4();
5252
let channel: SimpleChannel<ReplyWS> = new SimpleChannel<ReplyWS>();
5353
this.session_ref.get_receiver.set(get_id, channel);
5454

55-
let payload = undefined;
56-
if (_payload != undefined) {
57-
payload = b64_str_from_bytes(new Uint8Array(_payload))
55+
let payload_str = undefined;
56+
if (payload != undefined) {
57+
payload_str = b64_str_from_bytes(new Uint8Array(payload))
5858
}
59-
let attachment = undefined;
60-
if (_attachment != undefined) {
61-
attachment = b64_str_from_bytes(new Uint8Array(_attachment))
59+
let attachment_str = undefined;
60+
if (attachment != undefined) {
61+
attachment_str = b64_str_from_bytes(new Uint8Array(attachment))
6262
}
6363

6464
let control_msg: ControlMsg = {
6565
QuerierGet: {
6666
querier_id: this.querier_id as string,
6767
get_id: get_id,
68-
encoding: _encoding,
69-
payload: payload,
70-
attachment: attachment,
71-
handler: _handler_type
68+
parameters: parameters,
69+
encoding: encoding,
70+
payload: payload_str,
71+
attachment: attachment_str,
72+
handler: handler_type
7273
}
7374
};
7475

zenoh-ts/tests/src/common/assertions.ts

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,45 @@ export function assert(condition: boolean, message: string): void {
1818
}
1919
}
2020

21+
function deepEqual(actual: any, expected: any): boolean {
22+
if (actual === expected) return true;
23+
24+
if (actual instanceof Map && expected instanceof Map) {
25+
if (actual.size !== expected.size) return false;
26+
for (const [key, value] of actual) {
27+
if (!expected.has(key)) return false;
28+
if (!deepEqual(value, expected.get(key))) return false;
29+
}
30+
return true;
31+
}
32+
33+
if (Array.isArray(actual) && Array.isArray(expected)) {
34+
if (actual.length !== expected.length) return false;
35+
return actual.every((val, idx) => deepEqual(val, expected[idx]));
36+
}
37+
38+
if (actual instanceof Float32Array && expected instanceof Float32Array ||
39+
actual instanceof Float64Array && expected instanceof Float64Array) {
40+
if (actual.length !== expected.length) return false;
41+
return actual.every((val, idx) => Math.abs(val - expected[idx]) < 0.0001);
42+
}
43+
44+
if (typeof actual === 'object' && actual !== null &&
45+
typeof expected === 'object' && expected !== null) {
46+
return Object.keys(actual).length === Object.keys(expected).length &&
47+
Object.keys(actual).every(key => deepEqual(actual[key], expected[key]));
48+
}
49+
50+
return false;
51+
}
52+
2153
export function assert_eq<T>(actual: T, expected: T, message: string): void {
22-
assert(actual === expected, `${message}: expected ${expected}, but got ${actual}`);
54+
if (!deepEqual(actual, expected)) {
55+
throw new Error(`${message}: expected ${expected}, but got ${actual}`);
56+
}
57+
}
58+
59+
export async function run_test(fn: () => Promise<void>): Promise<void> {
60+
console.warn(`Test function: ${fn.name}`);
61+
await fn();
2362
}

zenoh-ts/tests/src/z_api_pub_sub.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@
1313
//
1414

1515
import { Config, Session, Subscriber, Sample } from "@eclipse-zenoh/zenoh-ts";
16-
import { assert, assert_eq } from "./common/assertions.ts";
16+
import { assert, assert_eq, run_test } from "./common/assertions.ts";
1717

18-
export async function putSubTest() {
18+
async function putSubTest() {
1919
// Open two sessions
2020
const session1 = await Session.open(new Config("ws/127.0.0.1:10000"));
2121
const session2 = await Session.open(new Config("ws/127.0.0.1:10000"));
@@ -57,4 +57,4 @@ export async function putSubTest() {
5757
await session2.close();
5858
}
5959

60-
putSubTest();
60+
await run_test(putSubTest);

zenoh-ts/tests/src/z_api_queryable_get.ts

Lines changed: 161 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@
1212
// ZettaScale Zenoh Team, <zenoh@zettascale.tech>
1313
//
1414

15-
import { Config, Session, Query, Reply, KeyExpr, Receiver, ZBytes, Selector, ReplyError, Parameters, Sample } from "@eclipse-zenoh/zenoh-ts";
16-
import { assert_eq } from "./common/assertions.ts";
15+
import { Config, Session, Query, Reply, KeyExpr, Receiver, ZBytes, Selector, ReplyError, Parameters, Sample, QueryTarget } from "@eclipse-zenoh/zenoh-ts";
16+
import { assert_eq, run_test } from "./common/assertions.ts";
1717

18-
async function queryableGetCallbackTest() {
18+
async function queryableSessionGetCallbackTest() {
1919
const ke_queryable = new KeyExpr("zenoh/test/*");
2020
const ke_get = new KeyExpr("zenoh/test/1");
2121
const queries: Query[] = [];
@@ -84,7 +84,7 @@ async function queryableGetCallbackTest() {
8484
await session2.close();
8585
}
8686

87-
async function queryableGetChannelTest() {
87+
async function queryableSessionGetChannelTest() {
8888
const ke = new KeyExpr("zenoh/test/*");
8989
const selector = new KeyExpr("zenoh/test/1");
9090

@@ -147,5 +147,160 @@ async function queryableGetChannelTest() {
147147
await session2.close();
148148
}
149149

150-
await queryableGetCallbackTest();
151-
await queryableGetChannelTest();
150+
async function queriableQuerierGetChannelTest() {
151+
const ke = new KeyExpr("zenoh/test/*");
152+
const selector = new KeyExpr("zenoh/test/1");
153+
154+
const session1 = await Session.open(new Config("ws/127.0.0.1:10000"));
155+
const session2 = await Session.open(new Config("ws/127.0.0.1:10000"));
156+
157+
const queryable = session1.declare_queryable(ke, { complete: true });
158+
159+
// sleep for 1 second to ensure the queryable is ready
160+
await new Promise((resolve) => setTimeout(resolve, 1000));
161+
162+
const querier = session2.declare_querier(selector, {
163+
target: QueryTarget.BestMatching
164+
});
165+
166+
// First query with ok parameters
167+
const receiver1 = await querier.get(new Parameters("p=ok"), { payload: "1" });
168+
let query = await queryable.receive();
169+
assert_eq(query instanceof Query, true, "Expected query to be an instance of Query");
170+
if (query instanceof Query) {
171+
assert_eq(query.key_expr().toString(), selector.toString(), "Key mismatch");
172+
assert_eq(query.parameters().toString(), "p=ok", "Parameters mismatch");
173+
assert_eq(query.payload()?.to_string(), "1", "Payload mismatch");
174+
query.reply(query.key_expr(), "1");
175+
}
176+
177+
// sleep to ensure the reply is processed
178+
await new Promise((resolve) => setTimeout(resolve, 100));
179+
180+
if (receiver1) {
181+
let reply = await receiver1.receive();
182+
assert_eq(reply instanceof Reply, true, "Expected reply to be an instance of Reply");
183+
if (reply instanceof Reply) {
184+
const result = reply.result();
185+
assert_eq(result instanceof ReplyError, false, "Reply should be OK");
186+
if (!(result instanceof ReplyError)) {
187+
assert_eq(result.payload().to_string(), "1", "Reply payload mismatch");
188+
}
189+
}
190+
}
191+
192+
// Second query using the same querier with error parameters
193+
const receiver2 = await querier.get(new Parameters("p=err"), { payload: "2" });
194+
query = await queryable.receive();
195+
assert_eq(query instanceof Query, true, "Expected query to be an instance of Query");
196+
if (query instanceof Query) {
197+
assert_eq(query.key_expr().toString(), selector.toString(), "Key mismatch");
198+
assert_eq(query.parameters().toString(), "p=err", "Parameters mismatch");
199+
assert_eq(query.payload()?.to_string(), "2", "Payload mismatch");
200+
query.reply_err("err");
201+
}
202+
203+
// sleep to ensure the reply is processed
204+
await new Promise((resolve) => setTimeout(resolve, 100));
205+
206+
if (receiver2) {
207+
let reply = await receiver2.receive();
208+
assert_eq(reply instanceof Reply, true, "Expected reply to be an instance of Reply");
209+
if (reply instanceof Reply) {
210+
const result = reply.result();
211+
assert_eq(result instanceof ReplyError, true, "Reply should be an error");
212+
if (result instanceof ReplyError) {
213+
assert_eq(result.payload().to_string(), "err", "Error payload mismatch");
214+
}
215+
}
216+
}
217+
218+
querier.undeclare();
219+
await queryable.undeclare();
220+
await session1.close();
221+
await session2.close();
222+
}
223+
224+
async function queriableQuerierGetCallbackTest() {
225+
const ke = new KeyExpr("zenoh/test/*");
226+
const selector = new KeyExpr("zenoh/test/1");
227+
const queries: Query[] = [];
228+
const replies: Reply[] = [];
229+
230+
const session1 = await Session.open(new Config("ws/127.0.0.1:10000"));
231+
const session2 = await Session.open(new Config("ws/127.0.0.1:10000"));
232+
233+
const queryable = session1.declare_queryable(ke, { complete: true });
234+
235+
// sleep for 1 second to ensure the queryable is ready
236+
await new Promise((resolve) => setTimeout(resolve, 1000));
237+
238+
const querier = session2.declare_querier(selector, {
239+
target: QueryTarget.BestMatching
240+
});
241+
242+
const handler = async (reply: Reply) => { replies.push(reply); };
243+
244+
// First query with ok parameters
245+
querier.get(new Parameters("p=ok"), {
246+
payload: "1",
247+
handler: handler
248+
});
249+
250+
// sleep to ensure the request is handled
251+
await new Promise((resolve) => setTimeout(resolve, 100));
252+
253+
let query = await queryable.receive();
254+
assert_eq(query instanceof Query, true, "Expected query to be an instance of Query");
255+
if (query instanceof Query) {
256+
queries.push(query);
257+
assert_eq(query.key_expr().toString(), selector.toString(), "Key mismatch");
258+
assert_eq(query.parameters().toString(), "p=ok", "Parameters mismatch");
259+
assert_eq(query.payload()?.to_string(), "1", "Payload mismatch");
260+
query.reply(query.key_expr(), "1");
261+
}
262+
263+
// Second query using the same querier with error parameters
264+
querier.get(new Parameters("p=err"), {
265+
payload: "2",
266+
handler: handler
267+
});
268+
269+
// sleep to ensure the request is handled
270+
await new Promise((resolve) => setTimeout(resolve, 100));
271+
272+
query = await queryable.receive();
273+
assert_eq(query instanceof Query, true, "Expected query to be an instance of Query");
274+
if (query instanceof Query) {
275+
queries.push(query);
276+
assert_eq(query.key_expr().toString(), selector.toString(), "Key mismatch");
277+
assert_eq(query.parameters().toString(), "p=err", "Parameters mismatch");
278+
assert_eq(query.payload()?.to_string(), "2", "Payload mismatch");
279+
query.reply_err("err");
280+
}
281+
282+
// sleep to ensure replies are processed
283+
await new Promise((resolve) => setTimeout(resolve, 100));
284+
285+
assert_eq(queries.length, 2, "Queries received");
286+
assert_eq(replies.length, 2, "Replies received");
287+
288+
assert_eq(replies[0].result() instanceof Sample, true, "Reply 0 should be Sample");
289+
if (!(replies[0].result() instanceof ReplyError)) {
290+
assert_eq(replies[0].result().payload().to_string(), "1", "Reply payload mismatch");
291+
}
292+
assert_eq(replies[1].result() instanceof ReplyError, true, "Reply 1 should be ReplyError");
293+
if (replies[1].result() instanceof ReplyError) {
294+
assert_eq(replies[1].result().payload().to_string(), "err", "Error payload mismatch");
295+
}
296+
297+
querier.undeclare();
298+
await queryable.undeclare();
299+
await session1.close();
300+
await session2.close();
301+
}
302+
303+
await run_test(queryableSessionGetCallbackTest);
304+
await run_test(queryableSessionGetChannelTest);
305+
await run_test(queriableQuerierGetChannelTest);
306+
await run_test(queriableQuerierGetCallbackTest);

0 commit comments

Comments
 (0)