UNPKG

node-osc

Version:

pyOSC inspired library for sending and receiving OSC messages

1,006 lines (850 loc) 27.5 kB
import { test } from 'tap'; import { encode, decode } from '../lib/osc.mjs'; test('osc: timetag encoding with non-number value', (t) => { // Test the else branch in writeTimeTag that writes zeros for non-number values const bundle = { oscType: 'bundle', timetag: 'immediate', // Non-number value elements: [ { oscType: 'message', address: '/test', args: [] } ] }; const buffer = encode(bundle); const decoded = decode(buffer); t.equal(decoded.oscType, 'bundle', 'should decode as bundle'); t.equal(decoded.timetag, 0, 'should decode timetag as 0 for immediate execution'); t.end(); }); test('osc: timetag encoding with zero value', (t) => { const bundle = { oscType: 'bundle', timetag: 0, elements: [ { oscType: 'message', address: '/test', args: [] } ] }; const buffer = encode(bundle); const decoded = decode(buffer); t.equal(decoded.timetag, 0, 'should encode 0 as immediate execution'); t.end(); }); test('osc: timetag encoding with numeric epoch value', (t) => { const bundle = { oscType: 'bundle', timetag: 42, elements: [ { oscType: 'message', address: '/test', args: [] } ] }; const buffer = encode(bundle); const decoded = decode(buffer); t.equal(decoded.timetag, 42, 'should preserve numeric timetag values'); t.end(); }); test('osc: timetag with immediate execution values', (t) => { // Test readTimeTag with seconds === 0 && fraction === 1 const bundle = { oscType: 'bundle', timetag: null, // This will trigger the non-number path elements: [ { oscType: 'message', address: '/test', args: [] } ] }; const buffer = encode(bundle); const decoded = decode(buffer); t.equal(decoded.timetag, 0, 'should handle immediate execution timetag'); t.end(); }); test('osc: argument encoding with unknown type', (t) => { // Test encodeArgument with unknown argument type to trigger line 122 const message = { oscType: 'message', address: '/test', args: [ { type: 'unknown', value: 'test' } ] }; t.throws(() => { encode(message); }, /Unknown argument type: unknown/, 'should throw error for unknown argument type'); t.end(); }); test('osc: argument encoding with boolean false', (t) => { // Test explicit boolean false encoding to cover lines 132-133 const message = { oscType: 'message', address: '/test', args: [ { type: 'boolean', value: false } ] }; const buffer = encode(message); const decoded = decode(buffer); t.equal(decoded.args[0].value, false, 'should encode and decode false boolean'); t.end(); }); test('osc: argument encoding with unsupported object', (t) => { // Test encodeArgument with unsupported object to trigger lines 139-142 const message = { oscType: 'message', address: '/test', args: [ { unsupported: 'object' } // Object without type/value properties ] }; t.throws(() => { encode(message); }, /Don't know how to encode argument/, 'should throw error for unsupported object'); t.end(); }); test('osc: argument encoding with undefined value', (t) => { // Test encodeArgument with undefined to trigger error case const message = { oscType: 'message', address: '/test', args: [undefined] }; t.throws(() => { encode(message); }, /Don't know how to encode argument/, 'should throw error for undefined argument'); t.end(); }); test('osc: argument decoding with unknown type tag', (t) => { // Test decodeArgument with unknown type tag to trigger line 161 // We need to manually create a buffer with an invalid type tag const addressPart = '/test\0\0\0'; const typeTagPart = ',X\0\0'; // X is not a valid OSC type tag const buffer = Buffer.from(addressPart + typeTagPart); t.throws(() => { decode(buffer); }, /I don't understand the argument code X/, 'should throw error for unknown type tag'); t.end(); }); test('osc: null argument encoding and decoding', (t) => { // Test null argument handling (N type tag) // Since our current implementation doesn't directly support null in encoding, // let's test that we can at least decode it if we manually create the buffer const addressPart = '/test\0\0\0'; const typeTagPart = ',N\0\0'; // N is null type tag const buffer = Buffer.from(addressPart + typeTagPart); const decoded = decode(buffer); t.equal(decoded.args[0].value, null, 'should decode null argument'); t.end(); }); test('osc: double type argument encoding', (t) => { // Test double type argument which should fall back to float const message = { oscType: 'message', address: '/test', args: [ { type: 'd', value: 3.14159 } ] }; const buffer = encode(message); const decoded = decode(buffer); t.ok(Math.abs(decoded.args[0].value - 3.14159) < 0.001, 'should encode double as float'); t.end(); }); test('osc: blob argument with Buffer', (t) => { // Test blob encoding with actual Buffer to ensure Buffer.isBuffer path is covered const testBuffer = Buffer.from('test data'); const message = { oscType: 'message', address: '/test', args: [testBuffer] // Direct Buffer without type wrapper }; const buffer = encode(message); const decoded = decode(buffer); t.ok(Buffer.isBuffer(decoded.args[0].value), 'should decode as Buffer'); t.equal(decoded.args[0].value.toString(), 'test data', 'should preserve blob content'); t.end(); }); test('osc: float number encoding', (t) => { // Test encoding of float numbers to cover lines 132-133 const message = { oscType: 'message', address: '/test', args: [3.14159] // Non-integer number should be encoded as float }; const buffer = encode(message); const decoded = decode(buffer); t.ok(typeof decoded.args[0].value === 'number', 'should decode as number'); t.ok(!Number.isInteger(decoded.args[0].value), 'should be float, not integer'); t.ok(Math.abs(decoded.args[0].value - 3.14159) < 0.001, 'should preserve float value'); t.end(); }); test('osc: explicit integer type encoding', (t) => { // Test explicit integer type to cover line 102 const message = { oscType: 'message', address: '/test', args: [ { type: 'i', value: 42 } ] }; const buffer = encode(message); const decoded = decode(buffer); t.equal(decoded.args[0].value, 42, 'should encode and decode integer'); t.end(); }); test('osc: explicit float type encoding', (t) => { // Test explicit float type to cover line 105 const message = { oscType: 'message', address: '/test', args: [ { type: 'f', value: 3.14 } ] }; const buffer = encode(message); const decoded = decode(buffer); t.ok(Math.abs(decoded.args[0].value - 3.14) < 0.001, 'should encode and decode float'); t.end(); }); test('osc: explicit string type encoding', (t) => { // Test explicit string type to cover line 108 const message = { oscType: 'message', address: '/test', args: [ { type: 's', value: 'hello' } ] }; const buffer = encode(message); const decoded = decode(buffer); t.equal(decoded.args[0].value, 'hello', 'should encode and decode string'); t.end(); }); test('osc: explicit blob type encoding', (t) => { // Test explicit blob type to cover line 111 const testData = Buffer.from('blob data'); const message = { oscType: 'message', address: '/test', args: [ { type: 'b', value: testData } ] }; const buffer = encode(message); const decoded = decode(buffer); t.ok(Buffer.isBuffer(decoded.args[0].value), 'should decode as Buffer'); t.equal(decoded.args[0].value.toString(), 'blob data', 'should preserve blob data'); t.end(); }); test('osc: explicit boolean true type encoding', (t) => { // Test explicit boolean true type to cover line 118 const message = { oscType: 'message', address: '/test', args: [ { type: 'T', value: true } ] }; const buffer = encode(message); const decoded = decode(buffer); t.equal(decoded.args[0].value, true, 'should encode and decode boolean true'); t.end(); }); test('osc: MIDI type encoding with Buffer', (t) => { // Test MIDI type with 4-byte Buffer const midiData = Buffer.from([0x01, 0x90, 0x3C, 0x7F]); // port 1, note on, middle C, velocity 127 const message = { oscType: 'message', address: '/midi', args: [ { type: 'm', value: midiData } ] }; const buffer = encode(message); const decoded = decode(buffer); t.ok(Buffer.isBuffer(decoded.args[0].value), 'should decode as Buffer'); t.equal(decoded.args[0].value.length, 4, 'should be 4 bytes'); t.equal(decoded.args[0].value[0], 0x01, 'port id should match'); t.equal(decoded.args[0].value[1], 0x90, 'status byte should match'); t.equal(decoded.args[0].value[2], 0x3C, 'data1 should match'); t.equal(decoded.args[0].value[3], 0x7F, 'data2 should match'); t.end(); }); test('osc: MIDI type encoding with object', (t) => { // Test MIDI type with object format const message = { oscType: 'message', address: '/midi', args: [ { type: 'midi', value: { port: 2, status: 0x80, data1: 0x40, data2: 0x00 } } ] }; const buffer = encode(message); const decoded = decode(buffer); t.ok(Buffer.isBuffer(decoded.args[0].value), 'should decode as Buffer'); t.equal(decoded.args[0].value[0], 2, 'port should match'); t.equal(decoded.args[0].value[1], 0x80, 'status should match'); t.equal(decoded.args[0].value[2], 0x40, 'data1 should match'); t.equal(decoded.args[0].value[3], 0x00, 'data2 should match'); t.end(); }); test('osc: MIDI type with invalid buffer length', (t) => { // Test MIDI type with wrong buffer length const message = { oscType: 'message', address: '/midi', args: [ { type: 'm', value: Buffer.from([0x90, 0x3C]) // Only 2 bytes } ] }; t.throws(() => { encode(message); }, /MIDI message must be exactly 4 bytes/, 'should throw error for wrong buffer length'); t.end(); }); test('osc: MIDI type with invalid value type', (t) => { // Test MIDI type with invalid value const message = { oscType: 'message', address: '/midi', args: [ { type: 'm', value: 'invalid' } ] }; t.throws(() => { encode(message); }, /MIDI value must be a 4-byte Buffer or object/, 'should throw error for invalid value type'); t.end(); }); test('osc: MIDI type with partial object', (t) => { // Test MIDI type with object having only some properties (should default others to 0) const message = { oscType: 'message', address: '/midi', args: [ { type: 'm', value: { status: 0x90, data1: 0x3C // port and data2 should default to 0 } } ] }; const buffer = encode(message); const decoded = decode(buffer); t.equal(decoded.args[0].value[0], 0, 'port should default to 0'); t.equal(decoded.args[0].value[1], 0x90, 'status should match'); t.equal(decoded.args[0].value[2], 0x3C, 'data1 should match'); t.equal(decoded.args[0].value[3], 0, 'data2 should default to 0'); t.end(); }); test('osc: MIDI type decoding with insufficient buffer data', (t) => { // Test the error case in readMidi when buffer doesn't have enough bytes // This manually crafts a malformed OSC buffer with MIDI type tag but insufficient data // Create a minimal OSC message buffer with MIDI type but truncated data // OSC Format: address + typetags + arguments // Address: "/m" (padded to 4 bytes) const address = Buffer.from('/m\0\0', 'ascii'); // 4 bytes // Type tags: ",m" (padded to 4 bytes) const typeTags = Buffer.from(',m\0\0', 'ascii'); // 4 bytes // MIDI data: only 2 bytes instead of required 4 const insufficientMidiData = Buffer.from([0x90, 0x3C]); // Only 2 bytes, need 4 // Combine into malformed OSC buffer const malformedBuffer = Buffer.concat([address, typeTags, insufficientMidiData]); t.throws(() => { decode(malformedBuffer); }, /Not enough bytes for MIDI message/, 'should throw error when MIDI data is truncated'); t.end(); }); test('osc: MIDI type with falsy status and data1 values', (t) => { // Test MIDI type with object having undefined/falsy status and data1 (should default to 0) const message = { oscType: 'message', address: '/midi', args: [ { type: 'm', value: { port: 5, data2: 0x40 // status and data1 are undefined, should default to 0 } } ] }; const buffer = encode(message); const decoded = decode(buffer); t.equal(decoded.args[0].value[0], 5, 'port should match'); t.equal(decoded.args[0].value[1], 0, 'status should default to 0'); t.equal(decoded.args[0].value[2], 0, 'data1 should default to 0'); t.equal(decoded.args[0].value[3], 0x40, 'data2 should match'); t.end(); }); test('osc: MIDI type with explicit zero status and data1 values', (t) => { // Test MIDI type with object having explicit 0 values for status and data1 const message = { oscType: 'message', address: '/midi', args: [ { type: 'm', value: { port: 3, status: 0, data1: 0, data2: 0x60 } } ] }; const buffer = encode(message); const decoded = decode(buffer); t.equal(decoded.args[0].value[0], 3, 'port should match'); t.equal(decoded.args[0].value[1], 0, 'status should be 0'); t.equal(decoded.args[0].value[2], 0, 'data1 should be 0'); t.equal(decoded.args[0].value[3], 0x60, 'data2 should match'); t.end(); }); test('osc: timetag encoding with numeric value and fractions', (t) => { // Test writeTimeTag with actual numeric timetag (lines 70-74) const bundle = { oscType: 'bundle', timetag: 1234567890.5, // Numeric value with fractional part elements: [ { oscType: 'message', address: '/test', args: [] } ] }; const buffer = encode(bundle); const decoded = decode(buffer); t.equal(decoded.oscType, 'bundle', 'should decode as bundle'); t.ok(Math.abs(decoded.timetag - 1234567890.5) < 0.01, 'should preserve numeric timetag with fractions'); t.end(); }); test('osc: timetag decoding with actual timestamp', (t) => { // Test readTimeTag with non-zero, non-immediate values (lines 92-96) // We encode a bundle with a real timestamp, then decode it const bundle = { oscType: 'bundle', timetag: 1609459200.25, // A real timestamp: 2021-01-01 00:00:00.25 elements: [ { oscType: 'message', address: '/timestamp', args: [{ type: 'i', value: 123 }] } ] }; const buffer = encode(bundle); const decoded = decode(buffer); t.equal(decoded.oscType, 'bundle', 'should decode as bundle'); t.ok(decoded.timetag > 0, 'timetag should be positive'); t.ok(Math.abs(decoded.timetag - 1609459200.25) < 0.01, 'should preserve timestamp value'); t.end(); }); test('osc: inferred integer encoding from raw number', (t) => { // Test line 167: encoding raw integer without type wrapper const message = { oscType: 'message', address: '/test', args: [42] // Raw integer, not wrapped }; const buffer = encode(message); const decoded = decode(buffer); t.equal(decoded.args[0].value, 42, 'should encode and decode raw integer'); t.end(); }); test('osc: inferred float encoding from raw number', (t) => { // Test line 169: encoding raw float without type wrapper const message = { oscType: 'message', address: '/test', args: [3.14159] // Raw float, not wrapped }; const buffer = encode(message); const decoded = decode(buffer); t.ok(Math.abs(decoded.args[0].value - 3.14159) < 0.001, 'should encode and decode raw float'); t.end(); }); test('osc: inferred string encoding from raw string', (t) => { // Test line 172: encoding raw string without type wrapper const message = { oscType: 'message', address: '/test', args: ['hello world'] // Raw string, not wrapped }; const buffer = encode(message); const decoded = decode(buffer); t.equal(decoded.args[0].value, 'hello world', 'should encode and decode raw string'); t.end(); }); test('osc: inferred boolean true encoding from raw boolean', (t) => { // Test line 174 (true branch): encoding raw boolean true const message = { oscType: 'message', address: '/test', args: [true] // Raw boolean true, not wrapped }; const buffer = encode(message); const decoded = decode(buffer); t.equal(decoded.args[0].value, true, 'should encode and decode raw boolean true'); t.end(); }); test('osc: inferred boolean false encoding from raw boolean', (t) => { // Test line 174 (false branch): encoding raw boolean false const message = { oscType: 'message', address: '/test', args: [false] // Raw boolean false, not wrapped }; const buffer = encode(message); const decoded = decode(buffer); t.equal(decoded.args[0].value, false, 'should encode and decode raw boolean false'); t.end(); }); test('osc: bundle with only message elements (no nested bundles)', (t) => { // Test line 252: encoding message elements in bundle (else branch) const bundle = { oscType: 'bundle', timetag: 0, elements: [ { oscType: 'message', address: '/msg1', args: [{ type: 'i', value: 1 }] }, { oscType: 'message', address: '/msg2', args: [{ type: 'i', value: 2 }] } ] }; const buffer = encode(bundle); const decoded = decode(buffer); t.equal(decoded.oscType, 'bundle', 'should decode as bundle'); t.equal(decoded.elements.length, 2, 'should have 2 elements'); t.equal(decoded.elements[0].oscType, 'message', 'first element should be message'); t.equal(decoded.elements[1].oscType, 'message', 'second element should be message'); t.end(); }); test('osc: malformed packet with missing comma in type tags', (t) => { // Test lines 292-293: decoding malformed packet without comma in type tags const addressBuf = Buffer.from('/test\0\0\0', 'utf8'); const malformedTypeTagsBuf = Buffer.from('iXX\0', 'utf8'); // Missing leading comma const buffer = Buffer.concat([addressBuf, malformedTypeTagsBuf]); t.throws(() => { decode(buffer); }, /Malformed Packet/, 'should throw on malformed type tags'); t.end(); }); test('osc: bundle with nested bundle element', (t) => { // Test line 252: encoding bundle elements in bundle (if branch) const innerBundle = { oscType: 'bundle', timetag: 0, elements: [ { oscType: 'message', address: '/inner', args: [{ type: 'i', value: 99 }] } ] }; const outerBundle = { oscType: 'bundle', timetag: 0, elements: [ { oscType: 'message', address: '/outer', args: [{ type: 's', value: 'test' }] }, innerBundle ] }; const buffer = encode(outerBundle); const decoded = decode(buffer); t.equal(decoded.oscType, 'bundle', 'should decode as bundle'); t.equal(decoded.elements.length, 2, 'should have 2 elements'); t.equal(decoded.elements[0].oscType, 'message', 'first element should be message'); t.equal(decoded.elements[1].oscType, 'bundle', 'second element should be bundle'); t.equal(decoded.elements[1].elements[0].address, '/inner', 'nested bundle should have correct message'); t.end(); }); test('osc: explicit integer type name', (t) => { const message = { oscType: 'message', address: '/test', args: [{ type: 'integer', value: 999 }] }; const buffer = encode(message); const decoded = decode(buffer); t.equal(decoded.args[0].value, 999); t.end(); }); test('osc: explicit float type name', (t) => { const message = { oscType: 'message', address: '/test', args: [{ type: 'float', value: 1.414 }] }; const buffer = encode(message); const decoded = decode(buffer); t.ok(Math.abs(decoded.args[0].value - 1.414) < 0.001); t.end(); }); test('osc: explicit string type name', (t) => { const message = { oscType: 'message', address: '/test', args: [{ type: 'string', value: 'test string' }] }; const buffer = encode(message); const decoded = decode(buffer); t.equal(decoded.args[0].value, 'test string'); t.end(); }); test('osc: explicit blob type name', (t) => { const message = { oscType: 'message', address: '/test', args: [{ type: 'blob', value: Buffer.from([0xAA, 0xBB]) }] }; const buffer = encode(message); const decoded = decode(buffer); t.ok(Buffer.isBuffer(decoded.args[0].value)); t.same(decoded.args[0].value, Buffer.from([0xAA, 0xBB])); t.end(); }); test('osc: explicit double type name', (t) => { const message = { oscType: 'message', address: '/test', args: [{ type: 'double', value: 2.718281828 }] }; const buffer = encode(message); const decoded = decode(buffer); t.ok(Math.abs(decoded.args[0].value - 2.718281828) < 0.001); t.end(); }); test('osc: malformed packet with missing string terminator', (t) => { const buffer = Buffer.from('/test', 'utf8'); t.throws(() => { decode(buffer); }, /Malformed Packet: Missing null terminator for string/, 'should throw on unterminated string'); t.end(); }); test('osc: malformed packet with truncated int32 argument', (t) => { const address = Buffer.from('/i\0\0', 'ascii'); const typeTags = Buffer.from(',i\0\0', 'ascii'); const truncated = Buffer.from([0x00, 0x01]); const buffer = Buffer.concat([address, typeTags, truncated]); t.throws(() => { decode(buffer); }, /Malformed Packet: Not enough bytes for int32/, 'should throw on truncated int32'); t.end(); }); test('osc: malformed packet with truncated float32 argument', (t) => { const address = Buffer.from('/f\0\0', 'ascii'); const typeTags = Buffer.from(',f\0\0', 'ascii'); const truncated = Buffer.from([0x3f, 0x80, 0x00]); const buffer = Buffer.concat([address, typeTags, truncated]); t.throws(() => { decode(buffer); }, /Malformed Packet: Not enough bytes for float32/, 'should throw on truncated float32'); t.end(); }); test('osc: malformed packet with invalid blob length', (t) => { const address = Buffer.from('/b\0\0', 'ascii'); const typeTags = Buffer.from(',b\0\0', 'ascii'); const length = Buffer.alloc(4); length.writeInt32BE(-1, 0); const buffer = Buffer.concat([address, typeTags, length]); t.throws(() => { decode(buffer); }, /Malformed Packet: Invalid blob length/, 'should throw on negative blob length'); t.end(); }); test('osc: malformed packet with truncated blob data', (t) => { const address = Buffer.from('/b\0\0', 'ascii'); const typeTags = Buffer.from(',b\0\0', 'ascii'); const length = Buffer.alloc(4); length.writeInt32BE(4, 0); const data = Buffer.from([0x01, 0x02]); const buffer = Buffer.concat([address, typeTags, length, data]); t.throws(() => { decode(buffer); }, /Malformed Packet: Not enough bytes for blob/, 'should throw on truncated blob data'); t.end(); }); test('osc: malformed packet with missing blob padding', (t) => { const address = Buffer.from('/b\0\0', 'ascii'); const typeTags = Buffer.from(',b\0\0', 'ascii'); const length = Buffer.alloc(4); length.writeInt32BE(3, 0); const data = Buffer.from([0x01, 0x02, 0x03]); const buffer = Buffer.concat([address, typeTags, length, data]); t.throws(() => { decode(buffer); }, /Malformed Packet: Not enough bytes for blob padding/, 'should throw on missing blob padding'); t.end(); }); test('osc: malformed bundle with truncated timetag', (t) => { const bundleHeader = Buffer.from('#bundle\0', 'ascii'); const timetag = Buffer.alloc(4); const buffer = Buffer.concat([bundleHeader, timetag]); t.throws(() => { decode(buffer); }, /Malformed Packet: Not enough bytes for timetag/, 'should throw on truncated timetag'); t.end(); }); test('osc: malformed bundle with invalid element size', (t) => { const bundleHeader = Buffer.from('#bundle\0', 'ascii'); const timetag = Buffer.alloc(8); const size = Buffer.alloc(4); size.writeInt32BE(0, 0); const buffer = Buffer.concat([bundleHeader, timetag, size]); t.throws(() => { decode(buffer); }, /Malformed Packet/, 'should throw on invalid bundle element size'); t.end(); }); test('osc: malformed bundle with oversized element size', (t) => { const bundleHeader = Buffer.from('#bundle\0', 'ascii'); const timetag = Buffer.alloc(8); const size = Buffer.alloc(4); size.writeInt32BE(12, 0); const buffer = Buffer.concat([bundleHeader, timetag, size, Buffer.from([0x01, 0x02])]); t.throws(() => { decode(buffer); }, /Malformed Packet/, 'should throw on oversized bundle element size'); t.end(); }); test('osc: blob padding when length is multiple of 4', (t) => { // Test writeBlob line 52: padding === 4 branch (should use 0) const message = { oscType: 'message', address: '/test', args: [{ type: 'b', value: Buffer.from([1, 2, 3, 4]) }] // length 4 }; const buffer = encode(message); const decoded = decode(buffer); t.same(decoded.args[0].value, Buffer.from([1, 2, 3, 4])); t.end(); }); test('osc: blob padding when length is not multiple of 4', (t) => { // Test writeBlob line 52: padding !== 4 branch (should use padding value) const message = { oscType: 'message', address: '/test', args: [{ type: 'b', value: Buffer.from([1, 2, 3]) }] // length 3 }; const buffer = encode(message); const decoded = decode(buffer); t.same(decoded.args[0].value, Buffer.from([1, 2, 3])); t.end(); }); test('osc: boolean type true value', (t) => { // Test boolean case ternary: true branch const message = { oscType: 'message', address: '/test', args: [{ type: 'boolean', value: true }] }; const buffer = encode(message); const decoded = decode(buffer); t.equal(decoded.args[0].value, true); t.end(); }); test('osc: boolean type false value', (t) => { // Test boolean case ternary: false branch const message = { oscType: 'message', address: '/test', args: [{ type: 'boolean', value: false }] }; const buffer = encode(message); const decoded = decode(buffer); t.equal(decoded.args[0].value, false); t.end(); }); test('osc: explicit T type', (t) => { const message = { oscType: 'message', address: '/test', args: [{ type: 'T', value: true }] }; const buffer = encode(message); const decoded = decode(buffer); t.equal(decoded.args[0].value, true); t.end(); }); test('osc: explicit F type', (t) => { const message = { oscType: 'message', address: '/test', args: [{ type: 'F', value: false }] }; const buffer = encode(message); const decoded = decode(buffer); t.equal(decoded.args[0].value, false); t.end(); });