Skip to content

tests: the OSC type tags a QML script asks for, on the wire - #2322

Closed
jcelerier wants to merge 1 commit into
masterfrom
fix/qml-osc-typetags
Closed

jcelerier wants to merge 1 commit into
masterfrom
fix/qml-osc-typetags

Conversation

@jcelerier

Copy link
Copy Markdown
Member

Draft: depends on ossia/libossia#957. The submodule here points at that PR's branch commit (74fa1a3); it must be re-pointed at libossia master once #957 lands, and this PR cannot go green before then.

The bug

Every number a QML script puts in an OSC message goes on the wire as a float, so there is no way to send the int source index SpatGRIS's /spat/serv grammar requires. ossia::qt::value_from_js has a lone numeric branch and QJSValue::toNumber() is a double. The shipped Spatial Protocol Mapper, which builds that message in QML, emits (oscdump, released package):

/spat/serv sffffff "deg" 1.000000 -90.000000 0.000000 1.000000 0.400000 0.600000

score's own C++ SpatGRIS protocol gets this right — SpatGrisSourceOffsetTest.cpp asserts IsInt32() on that argument — so the QML path is the odd one out.

libossia#957 adds the opt-in third argument osc(address, values, typetags), the type tag string of liblo's oscsend. It is opt-in rather than inferred from the value because a JavaScript number carries no integer-ness: inferring would make the shape of the packet depend on the data (siiiiff for a source on an axis, sifffff a moment later), which is worse than being consistently wrong. The reasoning is in that PR.

This PR

The score-side regression test, because this is where the Mapper + loopback-socket harness lives.

  • tests/testdata/protocols-qml/test_osc_typetags.qml — a Mapper that sends the /spat/serv shape with "sifffff", one argument per supported tag ("ifsTF"), a message with no tag string at all, and a tag string shorter than the argument list.
  • MapperDatagramProtocolsTest.cpp — asserts the exact datagram bytes against hand-written OSC fixtures (address and type tag strings padded to four bytes, network-order IEEE-754 payloads), so nothing libossia encodes is its own oracle, and reads the type tag string back out of the packet with its own four-byte-padding arithmetic.

The untyped case is there to pin the default: it must stay ,f byte for byte, or every script and stored document already out there changes meaning.

Failing without the fix

With the submodule back at libossia master, QML resolves osc(address, values, typetags) to the two-argument overload and silently drops the third argument — i.e. exactly the shipped behaviour:

MapperDatagramProtocolsTest.cpp:343: FAILED:
  CHECK( oscTypeTags(spat) == ",sifffff" )
with expansion:
  { ',', 's', 'f', 'f', 'f', 'f', 'f', 'f' }  ==  ",sifffff"

... index byte-compared as 3f800000 (= 1.0f) where 00000001 was wanted

test cases:  26 |  25 passed | 1 failed
assertions: 425 | 419 passed | 6 failed

Exactly the 6 typed assertions fail; the untyped ones pass in both builds, and so do the other 25 cases in the binary.

Wire evidence

oscdump — an independent liblo decoder — reading the datagrams this Mapper actually emitted:

/test/all      ifsTF    1 2.500000 "hi" #T #F
/test/partial  if       3 4.000000
/spat/serv     sifffff  "deg" 1 -90.000000 0.000000 1.000000 0.400000 0.600000
/test/untyped  f        7.000000

🤖 Generated with Claude Code

https://claude.ai/code/session_019QYyvhRXvSMSRQPaq12Teq

Companion to ossia/libossia#957, which adds the three-argument
`osc(address, values, typetags)` to the QML sockets. Every number a
script put in an OSC message went out as a float, so there was no way to
send the int source index SpatGRIS's /spat/serv grammar requires: the
shipped Spatial Protocol Mapper emits it as "sffffff" with the index as
1.000000.

The fixture sends the /spat/serv shape with "sifffff", one argument per
supported tag, a message with no tag string at all, and a tag string
shorter than the argument list. The test asserts the exact datagram
bytes against hand-written OSC fixtures -- address and type tag strings
padded to four bytes, network-order IEEE-754 payloads -- so nothing
libossia encodes is its own oracle, and reads the type tag string back
out of the packet with its own four-byte-padding arithmetic.

The untyped case is there to pin the default: it must stay "f", byte for
byte, or every script and document already out there changes meaning.

Submodule: 3rdparty/libossia moves to the branch commit. To be re-pointed
at master once the libossia PR lands.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019QYyvhRXvSMSRQPaq12Teq
@jcelerier
jcelerier force-pushed the fix/qml-osc-typetags branch from 2e69f77 to a76778e Compare October 7, 2026 13:00
@jcelerier

Copy link
Copy Markdown
Member Author

Closing in favour of #2323 for the consolidated engine branch.

The branch fix/qml-osc-typetags is kept, not deleted: it holds the ready-to-land score half of S5 (commit a76778e — the test_osc_typetags.qml fixture, the byte-exact wire assertions in MapperDatagramProtocolsTest.cpp, and the 3rdparty/libossia gitlink).

It is deliberately not on fix/engine-2026-10 yet: the test cannot pass without ossia/libossia#957, and putting an unmerged submodule sha on the shared branch would break every other checkout. Once #957 merges, re-point the gitlink at libossia master and cherry-pick a76778e onto fix/engine-2026-10.

@jcelerier

Copy link
Copy Markdown
Member Author

Recovered: the test from this PR is now in #2330 as 3348a8fd54, with the submodule pinned to d94955801 on libossia master (the commit ossia/libossia#957 merged as) instead of to the pull request branch — which is what made this one unmergeable.

@jcelerier
jcelerier deleted the fix/qml-osc-typetags branch October 11, 2026 15:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant