diff --git a/src/engine/comment.js b/src/engine/comment.js index 34bbcabd5..ac644e770 100644 --- a/src/engine/comment.js +++ b/src/engine/comment.js @@ -4,7 +4,6 @@ */ const uid = require('../util/uid'); -const Cast = require('../util/cast'); const xmlEscape = require('../util/xml-escape'); class Comment { @@ -23,8 +22,8 @@ class Comment { this.text = text; this.x = x; this.y = y; - this.width = Math.max(Cast.toNumber(width), Comment.MIN_WIDTH); - this.height = Math.max(Cast.toNumber(height), Comment.MIN_HEIGHT); + this.width = Math.max(Number(width), Comment.MIN_WIDTH); + this.height = Math.max(Number(height), Comment.MIN_HEIGHT); this.minimized = minimized || false; this.blockId = null; } diff --git a/src/util/cast.js b/src/util/cast.js index 604100ec8..14089cd9f 100644 --- a/src/util/cast.js +++ b/src/util/cast.js @@ -38,7 +38,6 @@ class Cast { } return value; } - const n = Number(value); if (_NumberIsNaN(n)) { // Scratch treats NaN as 0, when needed as a number. @@ -144,9 +143,15 @@ class Cast { } return 0; } + // Handle the special case of Infinity + if ( + (n1 === Infinity && n2 === Infinity) || + (n1 === -Infinity && n2 === -Infinity) + ) { + return 0; + } // Compare as numbers. return n1 - n2; - } /** diff --git a/test/unit/blocks_data_infinity.js b/test/unit/blocks_data_infinity.js new file mode 100644 index 000000000..b97a3ff49 --- /dev/null +++ b/test/unit/blocks_data_infinity.js @@ -0,0 +1,114 @@ +const test = require('tap').test; +const Data = require('../../src/blocks/scratch3_data'); + +const blocks = new Data(); + +const lists = {}; +const util = { + target: { + lookupOrCreateList (id, name) { + if (!(name in lists)) { + lists[name] = {value: []}; + } + return lists[name]; + } + } +}; + +test('List with postive infinity primitive contains postive infinity', t => { + lists.list = {value: [Infinity]}; + let args = {ITEM: Infinity, LIST: {name: 'list'}}; + let contains = blocks.listContainsItem(args, util); + t.strictEqual(contains, true, '[Infinity] contains Infinity'); + + lists.list = {value: [Infinity]}; + args = {ITEM: 'Infinity', LIST: {name: 'list'}}; + contains = blocks.listContainsItem(args, util); + t.strictEqual(contains, true, '[Infinity] contains "Infinity"'); + + lists.list = {value: [Infinity]}; + args = {ITEM: 'INFINITY', LIST: {name: 'list'}}; + contains = blocks.listContainsItem(args, util); + t.strictEqual(contains, true, '[Infinity] contains "INFINITY"'); + + lists.list = {value: ['Infinity']}; + args = {ITEM: Infinity, LIST: {name: 'list'}}; + contains = blocks.listContainsItem(args, util); + t.strictEqual(contains, true, '["Infinity"] contains Infinity'); + + lists.list = {value: ['Infinity']}; + args = {ITEM: 'Infinity', LIST: {name: 'list'}}; + contains = blocks.listContainsItem(args, util); + t.strictEqual(contains, true, '["Infinity"] contains "Infinity"'); + + lists.list = {value: ['Infinity']}; + args = {ITEM: 'INFINITY', LIST: {name: 'list'}}; + contains = blocks.listContainsItem(args, util); + t.strictEqual(contains, true, '["Infinity"] contains "INFINITY"'); + + lists.list = {value: ['INFINITY']}; + args = {ITEM: Infinity, LIST: {name: 'list'}}; + contains = blocks.listContainsItem(args, util); + t.strictEqual(contains, true, '["INFINITY"] contains Infinity'); + + lists.list = {value: ['INFINITY']}; + args = {ITEM: 'Infinity', LIST: {name: 'list'}}; + contains = blocks.listContainsItem(args, util); + t.strictEqual(contains, true, '["INFINITY"] contains "Infinity"'); + + lists.list = {value: ['INFINITY']}; + args = {ITEM: 'INFINITY', LIST: {name: 'list'}}; + contains = blocks.listContainsItem(args, util); + t.strictEqual(contains, true, '["INFINITY"] contains "INFINITY"'); + + t.end(); +}); + +test('List with negative infinity primitive contains negative infinity', t => { + lists.list = {value: [-Infinity]}; + let args = {ITEM: -Infinity, LIST: {name: 'list'}}; + let contains = blocks.listContainsItem(args, util); + t.strictEqual(contains, true, '[-Infinity] contains -Infinity'); + + lists.list = {value: [-Infinity]}; + args = {ITEM: '-Infinity', LIST: {name: 'list'}}; + contains = blocks.listContainsItem(args, util); + t.strictEqual(contains, true, '[-Infinity] contains "-Infinity"'); + + lists.list = {value: [-Infinity]}; + args = {ITEM: '-INFINITY', LIST: {name: 'list'}}; + contains = blocks.listContainsItem(args, util); + t.strictEqual(contains, true, '[-Infinity] contains "-INFINITY"'); + + lists.list = {value: ['-Infinity']}; + args = {ITEM: -Infinity, LIST: {name: 'list'}}; + contains = blocks.listContainsItem(args, util); + t.strictEqual(contains, true, '["-Infinity"] contains -Infinity'); + + lists.list = {value: ['-Infinity']}; + args = {ITEM: '-Infinity', LIST: {name: 'list'}}; + contains = blocks.listContainsItem(args, util); + t.strictEqual(contains, true, '["-Infinity"] contains "-Infinity"'); + + lists.list = {value: ['-Infinity']}; + args = {ITEM: '-INFINITY', LIST: {name: 'list'}}; + contains = blocks.listContainsItem(args, util); + t.strictEqual(contains, true, '["-Infinity"] contains "-INFINITY"'); + + lists.list = {value: ['-INFINITY']}; + args = {ITEM: -Infinity, LIST: {name: 'list'}}; + contains = blocks.listContainsItem(args, util); + t.strictEqual(contains, true, '["-INFINITY"] contains -Infinity'); + + lists.list = {value: ['-INFINITY']}; + args = {ITEM: '-Infinity', LIST: {name: 'list'}}; + contains = blocks.listContainsItem(args, util); + t.strictEqual(contains, true, '["-INFINITY"] contains "-Infinity"'); + + lists.list = {value: ['-INFINITY']}; + args = {ITEM: '-INFINITY', LIST: {name: 'list'}}; + contains = blocks.listContainsItem(args, util); + t.strictEqual(contains, true, '["-INFINITY"] contains "-INFINITY"'); + + t.end(); +}); diff --git a/test/unit/blocks_operators.js b/test/unit/blocks_operators.js index 6584bec67..5dbe082e9 100644 --- a/test/unit/blocks_operators.js +++ b/test/unit/blocks_operators.js @@ -28,7 +28,6 @@ test('multiply', t => { test('divide', t => { t.strictEqual(blocks.divide({NUM1: '2', NUM2: '2'}), 1); - t.strictEqual(blocks.divide({NUM1: '1', NUM2: '0'}), Infinity); // @todo t.ok(isNaN(blocks.divide({NUM1: 'foo', NUM2: 'bar'}))); // @todo t.end(); }); diff --git a/test/unit/blocks_operators_infinity.js b/test/unit/blocks_operators_infinity.js new file mode 100644 index 000000000..96771ab98 --- /dev/null +++ b/test/unit/blocks_operators_infinity.js @@ -0,0 +1,327 @@ +const test = require('tap').test; +const Operators = require('../../src/blocks/scratch3_operators'); + +const blocks = new Operators(null); + +test('divide: (1) / (0) = Infinity', t => { + t.strictEqual( + blocks.divide({NUM1: '1', NUM2: '0'}), Infinity, '1 / 0 = Infinity' + ); + + t.end(); +}); + +test('divide: division with Infinity', t => { + t.strictEqual( + blocks.divide({NUM1: 'Infinity', NUM2: 111}), Infinity, '"Infinity" / 111 = Infinity' + ); + t.strictEqual( + blocks.divide({NUM1: 'INFINITY', NUM2: 222}), 0, '"INFINITY" / 222 = 0' + ); + t.strictEqual( + blocks.divide({NUM1: Infinity, NUM2: 333}), Infinity, 'Infinity / 333 = Infinity' + ); + + t.strictEqual( + blocks.divide({NUM1: 111, NUM2: 'Infinity'}), 0, '111 / "Infinity" = 0' + ); + t.strictEqual( + blocks.divide({NUM1: 222, NUM2: 'INFINITY'}), Infinity, '222 / "INFINITY" = Infinity' + ); + t.strictEqual( + blocks.divide({NUM1: 333, NUM2: Infinity}), 0, '333 / Infinity = 0' + ); + + t.strictEqual( + blocks.divide({NUM1: '-Infinity', NUM2: 111}), -Infinity, '"-Infinity" / 111 = -Infinity' + ); + t.strictEqual( + blocks.divide({NUM1: '-INFINITY', NUM2: 222}), 0, '"-INFINITY" / 222 = 0' + ); + t.strictEqual( + blocks.divide({NUM1: -Infinity, NUM2: 333}), -Infinity, '-Infinity / 333 = -Infinity' + ); + + t.strictEqual( + blocks.divide({NUM1: 111, NUM2: '-Infinity'}), 0, '111 / "-Infinity" = 0' + ); + t.strictEqual( + blocks.divide({NUM1: 222, NUM2: '-INFINITY'}), Infinity, '222 / "-INFINITY" = Infinity' + ); + t.strictEqual( + blocks.divide({NUM1: 333, NUM2: -Infinity}), 0, '333 / -Infinity = 0' + ); + + t.end(); +}); + +test('multiply: multiply Infinity with numbers', t => { + t.strictEqual( + blocks.multiply({NUM1: 'Infinity', NUM2: 111}), Infinity, '"Infinity" * 111 = Infinity' + ); + t.strictEqual( + blocks.multiply({NUM1: 'INFINITY', NUM2: 222}), 0, '"INFINITY" * 222 = 0' + ); + t.strictEqual( + blocks.multiply({NUM1: Infinity, NUM2: 333}), Infinity, 'Infinity * 333 = Infinity' + ); + t.strictEqual( + blocks.multiply({NUM1: '-Infinity', NUM2: 111}), -Infinity, '"-Infinity" * 111 = -Infinity' + ); + t.strictEqual( + blocks.multiply({NUM1: '-INFINITY', NUM2: 222}), 0, '"-INFINITY" * 222 = 0' + ); + t.strictEqual( + blocks.multiply({NUM1: -Infinity, NUM2: 333}), -Infinity, '-Infinity * 333 = -Infinity' + ); + t.strictEqual( + blocks.multiply({NUM1: -Infinity, NUM2: Infinity}), -Infinity, '-Infinity * Infinity = -Infinity' + ); + t.strictEqual( + Number.isNaN(blocks.multiply({NUM1: Infinity, NUM2: 0})), true, 'Infinity * 0 = NaN' + ); + + t.end(); +}); + +test('add: add Infinity to a number', t => { + + t.strictEqual( + blocks.add({NUM1: 'Infinity', NUM2: 111}), Infinity, '"Infinity" + 111 = Infinity' + ); + t.strictEqual( + blocks.add({NUM1: 'INFINITY', NUM2: 222}), 222, '"INFINITY" + 222 = 222' + ); + t.strictEqual( + blocks.add({NUM1: Infinity, NUM2: 333}), Infinity, 'Infinity + 333 = Infinity' + ); + t.strictEqual( + blocks.add({NUM1: '-Infinity', NUM2: 111}), -Infinity, '"-Infinity" + 111 = -Infinity' + ); + t.strictEqual( + blocks.add({NUM1: '-INFINITY', NUM2: 222}), 222, '"-INFINITY" + 222 = 222' + ); + t.strictEqual( + blocks.add({NUM1: -Infinity, NUM2: 333}), -Infinity, '-Infinity + 333 = -Infinity' + ); + t.strictEqual( + Number.isNaN(blocks.add({NUM1: -Infinity, NUM2: Infinity})), true, '-Infinity + Infinity = NaN' + ); + + t.end(); +}); + +test('subtract: subtract Infinity with a number', t => { + + t.strictEqual( + blocks.subtract({NUM1: 'Infinity', NUM2: 111}), Infinity, '"Infinity" - 111 = Infinity' + ); + t.strictEqual( + blocks.subtract({NUM1: 'INFINITY', NUM2: 222}), -222, '"INFINITY" - 222 = -222' + ); + t.strictEqual( + blocks.subtract({NUM1: Infinity, NUM2: 333}), Infinity, 'Infinity - 333 = Infinity' + ); + t.strictEqual( + blocks.subtract({NUM1: 111, NUM2: 'Infinity'}), -Infinity, '111 - "Infinity" = -Infinity' + ); + t.strictEqual( + blocks.subtract({NUM1: 222, NUM2: 'INFINITY'}), 222, '222 - "INFINITY" = 222' + ); + t.strictEqual( + blocks.subtract({NUM1: 333, NUM2: Infinity}), -Infinity, '333 - Infinity = -Infinity' + ); + t.strictEqual( + Number.isNaN(blocks.subtract({NUM1: Infinity, NUM2: Infinity})), true, 'Infinity - Infinity = NaN' + ); + + t.end(); +}); + +test('equals: compare string infinity and numeric Infinity', t => { + + t.strictEqual( + blocks.equals({OPERAND1: 'Infinity', OPERAND2: 'INFINITY'}), true, '"Infinity" = "INFINITY"' + ); + t.strictEqual( + blocks.equals({OPERAND1: 'INFINITY', OPERAND2: 'Infinity'}), true, '"INFINITY" = "Infinity"' + ); + t.strictEqual( + blocks.equals({OPERAND1: 'Infinity', OPERAND2: 'Infinity'}), true, '"Infinity" = "Infinity"' + ); + t.strictEqual( + blocks.equals({OPERAND1: 'INFINITY', OPERAND2: 'INFINITY'}), true, '"INFINITY" = "INFINITY"' + ); + t.strictEqual( + blocks.equals({OPERAND1: 'INFINITY', OPERAND2: 'infinity'}), true, '"INFINITY" = "infinity"' + ); + + t.strictEqual( + blocks.equals({OPERAND1: Infinity, OPERAND2: Infinity}), true, 'Infinity = Infinity' + ); + t.strictEqual( + blocks.equals({OPERAND1: 'Infinity', OPERAND2: Infinity}), true, '"Infinity" = Infinity' + ); + t.strictEqual( + blocks.equals({OPERAND1: 'INFINITY', OPERAND2: Infinity}), true, '"INFINITY" = Infinity' + ); + t.strictEqual( + blocks.equals({OPERAND1: Infinity, OPERAND2: 'Infinity'}), true, 'Infinity = "Infinity"' + ); + t.strictEqual( + blocks.equals({OPERAND1: Infinity, OPERAND2: 'INFINITY'}), true, 'Infinity = "INFINITY' + ); + + t.end(); +}); + +test('equals: compare string negative infinity and numeric negative Infinity', t => { + + t.strictEqual( + blocks.equals({OPERAND1: '-Infinity', OPERAND2: '-INFINITY'}), true, '"-Infinity" = "-INFINITY"' + ); + t.strictEqual( + blocks.equals({OPERAND1: '-INFINITY', OPERAND2: '-Infinity'}), true, '"-INFINITY" = "-Infinity"' + ); + t.strictEqual( + blocks.equals({OPERAND1: '-Infinity', OPERAND2: '-Infinity'}), true, '"-Infinity" = "-Infinity"' + ); + t.strictEqual( + blocks.equals({OPERAND1: '-INFINITY', OPERAND2: '-INFINITY'}), true, '"-INFINITY" = "-INFINITY"' + ); + t.strictEqual( + blocks.equals({OPERAND1: '-INFINITY', OPERAND2: '-infinity'}), true, '"-INFINITY" = "-infinity"' + ); + + t.strictEqual( + blocks.equals({OPERAND1: -Infinity, OPERAND2: -Infinity}), true, '-Infinity = -Infinity' + ); + t.strictEqual( + blocks.equals({OPERAND1: '-Infinity', OPERAND2: -Infinity}), true, '"-Infinity" = -Infinity' + ); + t.strictEqual( + blocks.equals({OPERAND1: '-INFINITY', OPERAND2: -Infinity}), true, '"-INFINITY" = -Infinity' + ); + t.strictEqual( + blocks.equals({OPERAND1: -Infinity, OPERAND2: '-Infinity'}), true, '-Infinity = "-Infinity"' + ); + t.strictEqual( + blocks.equals({OPERAND1: -Infinity, OPERAND2: '-INFINITY'}), true, '-Infinity = "-INFINITY' + ); + + t.end(); +}); + + +test('equals: compare negative to postive string and numeric Infinity', t => { + t.strictEqual( + blocks.equals({OPERAND1: '-Infinity', OPERAND2: 'Infinity'}), false, '"-Infinity" != "Infinity"' + ); + t.strictEqual( + blocks.equals({OPERAND1: '-Infinity', OPERAND2: 'INFINITY'}), false, '"-infinity" != "INFINITY"' + ); + t.strictEqual( + blocks.equals({OPERAND1: '-INFINITY', OPERAND2: 'Infinity'}), false, '"-INFINITY" != "Infinity"' + ); + t.strictEqual( + blocks.equals({OPERAND1: '-INFINITY', OPERAND2: 'INFINITY'}), false, '"-INFINITY" != "INFINITY"' + ); + + t.strictEqual( + blocks.equals({OPERAND1: '-Infinity', OPERAND2: Infinity}), false, '"-Infinity" != Infinity' + ); + t.strictEqual( + blocks.equals({OPERAND1: '-INFINITY', OPERAND2: Infinity}), false, '"-INFINITY" != Infinity' + ); + t.strictEqual( + blocks.equals({OPERAND1: 'Infinity', OPERAND2: -Infinity}), false, '"Infinity" != -Infinity' + ); + t.strictEqual( + blocks.equals({OPERAND1: 'INFINITY', OPERAND2: -Infinity}), false, '"INFINITY" != -Infinity' + ); + + t.strictEqual( + blocks.equals({OPERAND1: Infinity, OPERAND2: -Infinity}), false, 'Infinity != -Infinity' + ); + + t.end(); +}); + +test('less than: compare string infinity and numeric Infinity', t => { + + t.strictEqual( + blocks.lt({OPERAND1: 'Infinity', OPERAND2: 'INFINITY'}), false, '"Infinity" !< "INFINITY"' + ); + t.strictEqual( + blocks.lt({OPERAND1: 'INFINITY', OPERAND2: Infinity}), false, '"INFINITY" !< "Infinity"' + ); + + t.strictEqual( + blocks.lt({OPERAND1: '-INFINITY', OPERAND2: 'INFINITY'}), true, '"-Infinity" < "INFINITY"' + ); + t.strictEqual( + blocks.lt({OPERAND1: -Infinity, OPERAND2: 'INFINITY'}), true, '-Infinity < "INFINITY"' + ); + + + t.strictEqual( + blocks.lt({OPERAND1: 'Infinity', OPERAND2: 111}), false, '"Infinity" !< 111' + ); + t.strictEqual( + blocks.lt({OPERAND1: 'INFINITY', OPERAND2: 222}), false, '"INFINITY" !< 222' + ); + t.strictEqual( + blocks.lt({OPERAND1: Infinity, OPERAND2: 333}), false, 'Infinity !< 333' + ); + + t.strictEqual( + blocks.lt({OPERAND1: 111, OPERAND2: 'Infinity'}), true, '111 < "Infinity"' + ); + t.strictEqual( + blocks.lt({OPERAND1: 222, OPERAND2: 'INFINITY'}), true, '222 < "INFINITY"' + ); + t.strictEqual( + blocks.lt({OPERAND1: 333, OPERAND2: Infinity}), true, '333 < Infinity' + ); + + t.end(); +}); + +test('more than: compare string infinity and numeric Infinity', t => { + + t.strictEqual( + blocks.gt({OPERAND1: 'Infinity', OPERAND2: 'INFINITY'}), false, '"Infinity" !> "INFINITY"' + ); + t.strictEqual( + blocks.gt({OPERAND1: 'INFINITY', OPERAND2: Infinity}), false, '"INFINITY" !> "Infinity"' + ); + + t.strictEqual( + blocks.gt({OPERAND1: 'INFINITY', OPERAND2: '-INFINITY'}), true, '"Infinity" < "-INFINITY"' + ); + t.strictEqual( + blocks.gt({OPERAND1: Infinity, OPERAND2: '-INFINITY'}), true, 'Infinity < "-INFINITY"' + ); + + t.strictEqual( + blocks.gt({OPERAND1: 'Infinity', OPERAND2: 111}), true, '"Infinity" > 111' + ); + t.strictEqual( + blocks.gt({OPERAND1: 'INFINITY', OPERAND2: 222}), true, '"INFINITY" > 222' + ); + t.strictEqual( + blocks.gt({OPERAND1: Infinity, OPERAND2: 333}), true, 'Infinity > 333' + ); + + t.strictEqual( + blocks.gt({OPERAND1: 111, OPERAND2: 'Infinity'}), false, '111 !> "Infinity"' + ); + t.strictEqual( + blocks.gt({OPERAND1: 222, OPERAND2: 'INFINITY'}), false, '222 !> "INFINITY"' + ); + t.strictEqual( + blocks.gt({OPERAND1: 333, OPERAND2: Infinity}), false, '333 !> Infinity' + ); + + t.end(); +});