@@ -1154,12 +1154,137 @@ describe('Tests with a testOutput and testInput', () => {
11541154 // the same restriction on 64-bit integers - their only Number type is a double
11551155 // precision floating point value, meaning they cannot accurately represent
11561156 // integers large than 2^53.
1157- // These tests check that via the type-agnostic getter and setter, and getString
1158- // and setString can be used to workaround this limitation.
1157+ // Due to this, there are restrictions on how 64-bit numbers (uint64 and int64)
1158+ // can be communicated, the following verify that the behaviour is as follows:
1159+ // - The getNumber and setNumber operations throw an error if used with a value
1160+ // outside of their supported range:
1161+ // - Max |value| for setNumber is 2^53 - 1
1162+ // - Max |value| for getNumber is 2^53
1163+ // - The type-agnostic setter can be used with numbers outside of this range,
1164+ // if they are supplied as strings (note in Python we also accept numbers).
1165+ // - The type-agnostic getter can be used with numbers outside of the range.
1166+ // If the value is <= 2^53 it will be returned as a Number, otherwise as a
1167+ // string.
1168+ // - The getString and setString operations can be used on all number types
1169+ // and has no restriction on size.
1170+ // - The setFromJson operation can be used to set large integers, they must
1171+ // be supplied as strings (otherwise they would be corrupted by JavaScript)
1172+ // - the getJSON operation should not be used to obtain large integers, the
1173+ // largest integer it can be used with is the same as getNumber (2^53), however
1174+ // we have no way of detecting if a value larger than this is being retrieved,
1175+ // so no error will be thrown otherwise.
11591176 describe ( 'Tests with 64-bit integers' , ( ) => {
1177+ it ( 'getNumber throws an error if value is out of range' , async ( ) => {
1178+ // Highest value retrievable is 2^53, so set 1 higher. We set via Json
1179+ // to work around the limitation with setNumber (could also use setString,
1180+ // or setAnyValue)
1181+ testOutput . instance . setFromJson ( {
1182+ my_uint64 : '9007199254740993' ,
1183+ my_int64 : '9007199254740993'
1184+ } )
1185+ testOutput . write ( )
1186+ try {
1187+ await testInput . wait ( testExpectSuccessTimeout )
1188+ } catch ( err ) {
1189+ console . log ( 'Error caught: ' + err )
1190+ expect ( false ) . to . deep . equals ( true )
1191+ }
1192+ testInput . take ( )
1193+
1194+ // The values of the 64-bit integers is too large to retrieve with getNumber
1195+ expect ( ( ) => {
1196+ testInput . samples . get ( 0 ) . getNumber ( 'my_uint64' )
1197+ } ) . to . throw ( rti . DDSError )
1198+ expect ( ( ) => {
1199+ testInput . samples . get ( 0 ) . getNumber ( 'my_int64' )
1200+ } ) . to . throw ( rti . DDSError )
1201+
1202+ // Also check the most negative value
1203+ testOutput . instance . setFromJson ( {
1204+ my_int64 : '-9007199254740993'
1205+ } )
1206+ testOutput . write ( )
1207+ try {
1208+ await testInput . wait ( testExpectSuccessTimeout )
1209+ } catch ( err ) {
1210+ console . log ( 'Error caught: ' + err )
1211+ expect ( false ) . to . deep . equals ( true )
1212+ }
1213+ testInput . take ( )
1214+ expect ( ( ) => {
1215+ testInput . samples . get ( 0 ) . getNumber ( 'my_int64' )
1216+ } ) . to . throw ( rti . DDSError )
1217+ } )
1218+
1219+ // Check that the getNumber API can handle values stated in documentation
1220+ it ( 'getNumber can retrieve values up to 2^53' , async ( ) => {
1221+ testOutput . instance . setFromJson ( {
1222+ my_uint64 : '9007199254740992' ,
1223+ my_int64 : '-9007199254740992'
1224+ } )
1225+ testOutput . write ( )
1226+ try {
1227+ await testInput . wait ( testExpectSuccessTimeout )
1228+ } catch ( err ) {
1229+ console . log ( 'Error caught: ' + err )
1230+ expect ( false ) . to . deep . equals ( true )
1231+ }
1232+ testInput . take ( )
1233+
1234+ // Obtain the values and confirm they are correct
1235+ const obtainedUint64 = testInput . samples . get ( 0 ) . getNumber ( 'my_uint64' )
1236+ const obtainedInt64 = testInput . samples . get ( 0 ) . getNumber ( 'my_int64' )
1237+ expect ( obtainedUint64 ) . to . deep . equals ( Number . MAX_SAFE_INTEGER + 1 )
1238+ expect ( obtainedInt64 ) . to . deep . equals ( Number . MIN_SAFE_INTEGER - 1 )
1239+ } )
1240+
1241+ // Check that setNumber throws an error if value is too large
1242+ it ( 'setNumber throws an error if value out of range' , ( ) => {
1243+ // Max value for set is 2^53 - 1, anything larger will throw an error
1244+ expect ( ( ) => {
1245+ testOutput . instance . setNumber ( 'my_uint64' , Number . MAX_SAFE_INTEGER + 1 )
1246+ } ) . to . throw ( rti . DDSError )
1247+ expect ( ( ) => {
1248+ testOutput . instance . setNumber ( 'my_int64' , Number . MAX_SAFE_INTEGER + 1 )
1249+ } ) . to . throw ( rti . DDSError )
1250+ expect ( ( ) => {
1251+ testOutput . instance . setNumber ( 'my_int64' , Number . MIN_SAFE_INTEGER - 1 )
1252+ } ) . to . throw ( rti . DDSError )
1253+ } )
1254+
1255+ // Check that setNumber can handle the values stated in the documentation
1256+ it ( 'setNumber can set values up to 2^53 - 1' , async ( ) => {
1257+ // setNumber can set up to 2^53 - 1 (which is === Number.MAX_SAFE_INTEGER)
1258+ testOutput . instance . setNumber ( 'my_uint64' , Number . MAX_SAFE_INTEGER )
1259+ testOutput . instance . setNumber ( 'my_int64' , Number . MAX_SAFE_INTEGER )
1260+ testOutput . write ( )
1261+ try {
1262+ await testInput . wait ( testExpectSuccessTimeout )
1263+ } catch ( err ) {
1264+ console . log ( 'Error caught: ' + err )
1265+ expect ( false ) . to . deep . equals ( true )
1266+ }
1267+ testInput . take ( )
1268+ // Confirm that the values are correct and not corrupted
1269+ expect ( testInput . samples . get ( 0 ) . getNumber ( 'my_uint64' ) ) . to . deep . equals ( Number . MAX_SAFE_INTEGER )
1270+ expect ( testInput . samples . get ( 0 ) . getNumber ( 'my_int64' ) ) . to . deep . equals ( Number . MAX_SAFE_INTEGER )
1271+
1272+ // Also do same test with minimum value
1273+ testOutput . instance . setNumber ( 'my_int64' , Number . MIN_SAFE_INTEGER )
1274+ testOutput . write ( )
1275+ try {
1276+ await testInput . wait ( testExpectSuccessTimeout )
1277+ } catch ( err ) {
1278+ console . log ( 'Error caught: ' + err )
1279+ expect ( false ) . to . deep . equals ( true )
1280+ }
1281+ testInput . take ( )
1282+ expect ( testInput . samples . get ( 0 ) . getNumber ( 'my_int64' ) ) . to . deep . equals ( Number . MIN_SAFE_INTEGER )
1283+ } )
1284+
11601285 it ( 'Can communicate large 64-bit numbers using getString and setString' , async ( ) => {
1161- testOutput . instance . setString ( 'my_uint64' , '18446744073709551615 ' )
1162- testOutput . instance . setString ( 'my_int64' , '9223372036854775807 ' )
1286+ testOutput . instance . setString ( 'my_uint64' , '9007199254740993 ' )
1287+ testOutput . instance . setString ( 'my_int64' , '-9007199254740993 ' )
11631288 testOutput . write ( )
11641289 try {
11651290 await testInput . wait ( testExpectSuccessTimeout )
@@ -1168,14 +1293,16 @@ describe('Tests with a testOutput and testInput', () => {
11681293 expect ( false ) . to . deep . equals ( true )
11691294 }
11701295 testInput . take ( )
1171- expect ( testInput . samples . get ( 0 ) . getString ( 'my_uint64' ) ) . to . deep . equals ( '18446744073709551615 ' )
1172- expect ( testInput . samples . get ( 0 ) . getString ( 'my_int64' ) ) . to . deep . equals ( '9223372036854775807 ' )
1296+ expect ( testInput . samples . get ( 0 ) . getString ( 'my_uint64' ) ) . to . deep . equals ( '9007199254740993 ' )
1297+ expect ( testInput . samples . get ( 0 ) . getString ( 'my_int64' ) ) . to . deep . equals ( '-9007199254740993 ' )
11731298 } )
11741299
11751300 it ( '64-bit values larger than 2^53 are returned as strings by get' , async ( ) => {
1176- const maxInt64 = '9223372036854775807'
1177- const maxUint64 = '18446744073709551615'
1178- testOutput . instance . setFromJson ( { my_int64 : maxInt64 , my_uint64 : maxUint64 } )
1301+ const largeIntAsString = '9007199254740993'
1302+ testOutput . instance . setFromJson ( {
1303+ my_int64 : largeIntAsString ,
1304+ my_uint64 : largeIntAsString
1305+ } )
11791306 testOutput . write ( )
11801307 try {
11811308 await testInput . wait ( testExpectSuccessTimeout )
@@ -1185,13 +1312,16 @@ describe('Tests with a testOutput and testInput', () => {
11851312 }
11861313 testInput . take ( )
11871314 expect ( testInput . samples . get ( 0 ) . get ( 'my_uint64' ) ) . to . be . a . string
1188- expect ( testInput . samples . get ( 0 ) . get ( 'my_uint64' ) ) . to . deep . equals ( maxUint64 )
1189- expect ( testInput . samples . get ( 0 ) . get ( 'my_int64' ) ) . to . deep . equals ( maxInt64 )
1315+ expect ( testInput . samples . get ( 0 ) . get ( 'my_uint64' ) ) . to . deep . equals ( largeIntAsString )
11901316 expect ( testInput . samples . get ( 0 ) . get ( 'my_int64' ) ) . to . be . a . string
1317+ expect ( testInput . samples . get ( 0 ) . get ( 'my_int64' ) ) . to . deep . equals ( largeIntAsString )
11911318 } )
11921319
11931320 it ( '64-bit values smaller or equal to 2^53 are returned as numbers by get' , async ( ) => {
1194- testOutput . instance . setFromJson ( { my_int64 : 123456 , my_uint64 : 123456 } )
1321+ testOutput . instance . setFromJson ( {
1322+ my_uint64 : Number . MAX_SAFE_INTEGER ,
1323+ my_int64 : Number . MIN_SAFE_INTEGER
1324+ } )
11951325 testOutput . write ( )
11961326 try {
11971327 await testInput . wait ( testExpectSuccessTimeout )
@@ -1200,13 +1330,16 @@ describe('Tests with a testOutput and testInput', () => {
12001330 expect ( false ) . to . deep . equals ( true )
12011331 }
12021332 testInput . take ( )
1203- expect ( testInput . samples . get ( 0 ) . get ( 'my_uint64' ) ) . to . deep . equals ( 123456 )
1333+ expect ( testInput . samples . get ( 0 ) . get ( 'my_uint64' ) ) . to . deep . equals ( Number . MAX_SAFE_INTEGER )
12041334 expect ( testInput . samples . get ( 0 ) . get ( 'my_uint64' ) ) . to . be . a ( 'number' )
1205- expect ( testInput . samples . get ( 0 ) . get ( 'my_int64' ) ) . to . deep . equals ( 123456 )
1335+ expect ( testInput . samples . get ( 0 ) . get ( 'my_int64' ) ) . to . deep . equals ( Number . MIN_SAFE_INTEGER )
12061336 expect ( testInput . samples . get ( 0 ) . get ( 'my_int64' ) ) . to . be . a ( 'number' )
12071337 } )
12081338
1209- it ( 'Can communicate large 64-bit numbers using type-agnostic getters and setters' , async ( ) => {
1339+ it ( 'Can set large 64-bit numbers using type-agnostic setter' , async ( ) => {
1340+ // Any integer value can be set via the type-agnostic setter when supplied
1341+ // as a string (this differs from Python, where you could also supply it as
1342+ // an int))
12101343 testOutput . instance . set ( 'my_uint64' , '18446744073709551615' )
12111344 testOutput . instance . set ( 'my_int64' , '9223372036854775807' )
12121345 testOutput . write ( )
@@ -1217,9 +1350,34 @@ describe('Tests with a testOutput and testInput', () => {
12171350 expect ( false ) . to . deep . equals ( true )
12181351 }
12191352 testInput . take ( )
1353+ // The values will be returned as strings since they are > 2^53
12201354 expect ( testInput . samples . get ( 0 ) . get ( 'my_uint64' ) ) . to . deep . equals ( '18446744073709551615' )
12211355 expect ( testInput . samples . get ( 0 ) . get ( 'my_int64' ) ) . to . deep . equals ( '9223372036854775807' )
12221356 } )
1357+
1358+ it ( 'The JSON getter cannot handle large integers' , async ( ) => {
1359+ // Provided the values are supplied as strings to the JSON object, there should
1360+ // be no restriction on the size of the integer
1361+ const jsonTx = {
1362+ my_uint64 : '18446744073709551615' ,
1363+ my_int64 : '9223372036854775807'
1364+ }
1365+ testOutput . instance . setFromJson ( jsonTx )
1366+ testOutput . write ( )
1367+ try {
1368+ await testInput . wait ( testExpectSuccessTimeout )
1369+ } catch ( err ) {
1370+ console . log ( 'Error caught: ' + err )
1371+ expect ( false ) . to . deep . equals ( true )
1372+ }
1373+ testInput . take ( )
1374+
1375+ // The JSON.parse() call done in getFromJSON will result in the
1376+ // values > Number.MAX_SAFE_INT being corrupted. We cannot detect this.
1377+ const jsonRx = testInput . samples . get ( 0 ) . getJson ( )
1378+ expect ( jsonRx . my_int64 ) . to . not . deep . equal ( jsonTx . my_int64 )
1379+ expect ( jsonRx . my_uint64 ) . to . not . deep . equal ( jsonTx . my_uint64 )
1380+ } )
12231381 } )
12241382} )
12251383
0 commit comments