From 1b44841034d358d7dfaed0e08cd71a5f4bec129e Mon Sep 17 00:00:00 2001 From: Valerie R Young <valerie@bocoup.com> Date: Fri, 9 Nov 2018 16:56:07 -0500 Subject: [PATCH 1/8] Add unit tests for treatment of infinity primatives --- test/unit/blocks_data_infinity.js | 114 +++++++++ test/unit/blocks_operators.js | 1 - test/unit/blocks_operators_infinity.js | 328 +++++++++++++++++++++++++ 3 files changed, 442 insertions(+), 1 deletion(-) create mode 100644 test/unit/blocks_data_infinity.js create mode 100644 test/unit/blocks_operators_infinity.js 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..aff138301 --- /dev/null +++ b/test/unit/blocks_operators_infinity.js @@ -0,0 +1,328 @@ +const test = require('tap').test; +const Operators = require('../../src/blocks/scratch3_operators'); +const Data = require('../../src/blocks/scratch3_data'); + +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}), Infinity, '"INFINITY" / 222 = Infinity' + ); + 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'}), 0, '222 / "INFINITY" = 0' + ); + 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}), -Infinity, '"-INFINITY" / 222 = -Infinity' + ); + 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'}), 0, '222 / "-INFINITY" = 0' + ); + 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}), Infinity, '"INFINITY" * 222 = Infinity' + ); + 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}), -Infinity, '"-INFINITY" * 222 = -Infinity' + ); + 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}), Infinity, '"INFINITY" + 222 = Infinity' + ); + 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}), -Infinity, '"-INFINITY" + 222 = -Infinity' + ); + 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}), Infinity, '"INFINITY" - 222 = Infinity' + ); + 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'}), -Infinity, '222 - "INFINITY" = -Infinity' + ); + 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(); +}); From 43c5d5c05342703f2db326ccaddc052806bb3072 Mon Sep 17 00:00:00 2001 From: Valerie R Young <valerie@bocoup.com> Date: Tue, 13 Nov 2018 13:00:20 -0500 Subject: [PATCH 2/8] Correct infinity comparisons and toNumber --- src/util/cast.js | 47 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/src/util/cast.js b/src/util/cast.js index 604100ec8..6cd2162cb 100644 --- a/src/util/cast.js +++ b/src/util/cast.js @@ -30,6 +30,7 @@ class Cast { static toNumber (value) { // If value is already a number we don't need to coerce it with // Number(). + if (typeof value === 'number') { // Scratch treats NaN as 0, when needed as a number. // E.g., 0 + NaN -> 0. @@ -38,8 +39,14 @@ class Cast { } return value; } - - const n = Number(value); + let n; + if (Cast.isInfinity(value)) { + n = Infinity; + } else if (Cast.isNegativeInfinity(value)) { + n = -Infinity; + } else { + n = Number(value); + } if (_NumberIsNaN(n)) { // Scratch treats NaN as 0, when needed as a number. // E.g., 0 + NaN -> 0. @@ -117,6 +124,24 @@ class Cast { return val === null || (typeof val === 'string' && val.trim().length === 0); } + /** + * Determine if a Scratch argument is infinity. + * @param {*} val value to check. + * @return {boolean} True if the argument is any capitalization of infinity. + */ + static isInfinity (val) { + return val === Infinity || (typeof val === 'string' && val.toLowerCase() === 'infinity'); + } + + /** + * Determine if a Scratch argument is negative infinity. + * @param {*} val value to check. + * @return {boolean} True if the argument is a '-' folled by any capitalization of infinity. + */ + static isNegativeInfinity (val) { + return val === -Infinity || (typeof val === 'string' && val.toLowerCase() === '-infinity'); + } + /** * Compare two values, using Scratch cast, case-insensitive string compare, etc. * In Scratch 2.0, this is captured by `interp.compare.` @@ -125,8 +150,10 @@ class Cast { * @returns {number} Negative number if v1 < v2; 0 if equal; positive otherwise. */ static compare (v1, v2) { - let n1 = Number(v1); - let n2 = Number(v2); + // If Cast.toNumber returns '0', then the value might be NaN. If the value is NaN, + // this comparison algorithm needs to know. + let n1 = Cast.toNumber(v1) || Number(v1); + let n2 = Cast.toNumber(v2) || Number(v2); if (n1 === 0 && Cast.isWhiteSpace(v1)) { n1 = NaN; } else if (n2 === 0 && Cast.isWhiteSpace(v2)) { @@ -145,8 +172,16 @@ class Cast { return 0; } // Compare as numbers. - return n1 - n2; - + const r = n1 - n2; + if (isNaN(r)) { + if (n1 === Infinity && n2 === Infinity) { + return 0; + } + if (n1 === -Infinity && n2 === -Infinity) { + return 0; + } + } + return r; } /** From b4e5655b18955aacf0ff5b6f90f8fc550784bc44 Mon Sep 17 00:00:00 2001 From: Valerie R Young <valerie@bocoup.com> Date: Tue, 13 Nov 2018 13:59:22 -0500 Subject: [PATCH 3/8] Satisfy linter --- test/unit/blocks_operators_infinity.js | 1 - 1 file changed, 1 deletion(-) diff --git a/test/unit/blocks_operators_infinity.js b/test/unit/blocks_operators_infinity.js index aff138301..e0e0ae241 100644 --- a/test/unit/blocks_operators_infinity.js +++ b/test/unit/blocks_operators_infinity.js @@ -1,6 +1,5 @@ const test = require('tap').test; const Operators = require('../../src/blocks/scratch3_operators'); -const Data = require('../../src/blocks/scratch3_data'); const blocks = new Operators(null); From 3fe1d3bf6d4d90e51e1ccbd31b452e965548d026 Mon Sep 17 00:00:00 2001 From: Valerie R Young <valerie@bocoup.com> Date: Tue, 13 Nov 2018 14:58:28 -0500 Subject: [PATCH 4/8] Minor typo --- src/util/cast.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/util/cast.js b/src/util/cast.js index 6cd2162cb..c1320b7cb 100644 --- a/src/util/cast.js +++ b/src/util/cast.js @@ -30,7 +30,6 @@ class Cast { static toNumber (value) { // If value is already a number we don't need to coerce it with // Number(). - if (typeof value === 'number') { // Scratch treats NaN as 0, when needed as a number. // E.g., 0 + NaN -> 0. @@ -136,7 +135,7 @@ class Cast { /** * Determine if a Scratch argument is negative infinity. * @param {*} val value to check. - * @return {boolean} True if the argument is a '-' folled by any capitalization of infinity. + * @return {boolean} True if the argument is a '-' followed by any capitalization of infinity. */ static isNegativeInfinity (val) { return val === -Infinity || (typeof val === 'string' && val.toLowerCase() === '-infinity'); From 73e157ba7cc8edcf2938a335cdf4844e8c6a8d4c Mon Sep 17 00:00:00 2001 From: Valerie R Young <valerie@bocoup.com> Date: Fri, 16 Nov 2018 16:30:26 -0500 Subject: [PATCH 5/8] Use Number instead of Cast.toNumber --- src/engine/comment.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/engine/comment.js b/src/engine/comment.js index 34bbcabd5..c18014c70 100644 --- a/src/engine/comment.js +++ b/src/engine/comment.js @@ -23,8 +23,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; } From dd81a261df360f9188c83ab548a22bba91d76f9f Mon Sep 17 00:00:00 2001 From: Valerie R Young <valerie@bocoup.com> Date: Fri, 16 Nov 2018 16:49:27 -0500 Subject: [PATCH 6/8] Remove Cast import --- src/engine/comment.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/engine/comment.js b/src/engine/comment.js index c18014c70..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 { From 70f00e30c4b699f6e65fd8bef4b88c65aea6a7f6 Mon Sep 17 00:00:00 2001 From: Valerie R Young <valerie@bocoup.com> Date: Tue, 27 Nov 2018 17:51:00 -0500 Subject: [PATCH 7/8] Revert changes to toNumber, fix comparison of infinity in cast.compare --- src/util/cast.js | 51 ++++++-------------------- test/unit/blocks_operators_infinity.js | 20 +++++----- 2 files changed, 21 insertions(+), 50 deletions(-) diff --git a/src/util/cast.js b/src/util/cast.js index c1320b7cb..fd40a339e 100644 --- a/src/util/cast.js +++ b/src/util/cast.js @@ -38,14 +38,7 @@ class Cast { } return value; } - let n; - if (Cast.isInfinity(value)) { - n = Infinity; - } else if (Cast.isNegativeInfinity(value)) { - n = -Infinity; - } else { - n = Number(value); - } + const n = Number(value); if (_NumberIsNaN(n)) { // Scratch treats NaN as 0, when needed as a number. // E.g., 0 + NaN -> 0. @@ -123,24 +116,6 @@ class Cast { return val === null || (typeof val === 'string' && val.trim().length === 0); } - /** - * Determine if a Scratch argument is infinity. - * @param {*} val value to check. - * @return {boolean} True if the argument is any capitalization of infinity. - */ - static isInfinity (val) { - return val === Infinity || (typeof val === 'string' && val.toLowerCase() === 'infinity'); - } - - /** - * Determine if a Scratch argument is negative infinity. - * @param {*} val value to check. - * @return {boolean} True if the argument is a '-' followed by any capitalization of infinity. - */ - static isNegativeInfinity (val) { - return val === -Infinity || (typeof val === 'string' && val.toLowerCase() === '-infinity'); - } - /** * Compare two values, using Scratch cast, case-insensitive string compare, etc. * In Scratch 2.0, this is captured by `interp.compare.` @@ -149,10 +124,8 @@ class Cast { * @returns {number} Negative number if v1 < v2; 0 if equal; positive otherwise. */ static compare (v1, v2) { - // If Cast.toNumber returns '0', then the value might be NaN. If the value is NaN, - // this comparison algorithm needs to know. - let n1 = Cast.toNumber(v1) || Number(v1); - let n2 = Cast.toNumber(v2) || Number(v2); + let n1 = Number(v1); + let n2 = Number(v2); if (n1 === 0 && Cast.isWhiteSpace(v1)) { n1 = NaN; } else if (n2 === 0 && Cast.isWhiteSpace(v2)) { @@ -170,17 +143,15 @@ class Cast { } return 0; } - // Compare as numbers. - const r = n1 - n2; - if (isNaN(r)) { - if (n1 === Infinity && n2 === Infinity) { - return 0; - } - if (n1 === -Infinity && n2 === -Infinity) { - return 0; - } + // Handle the special case of Infinity + if ( + (n1 === Infinity && n2 === Infinity) + || (n1 === -Infinity && n2 === -Infinity) + ) { + return 0; } - return r; + // Compare as numbers. + return n1 - n2; } /** diff --git a/test/unit/blocks_operators_infinity.js b/test/unit/blocks_operators_infinity.js index e0e0ae241..96771ab98 100644 --- a/test/unit/blocks_operators_infinity.js +++ b/test/unit/blocks_operators_infinity.js @@ -16,7 +16,7 @@ test('divide: division with Infinity', t => { blocks.divide({NUM1: 'Infinity', NUM2: 111}), Infinity, '"Infinity" / 111 = Infinity' ); t.strictEqual( - blocks.divide({NUM1: 'INFINITY', NUM2: 222}), Infinity, '"INFINITY" / 222 = Infinity' + blocks.divide({NUM1: 'INFINITY', NUM2: 222}), 0, '"INFINITY" / 222 = 0' ); t.strictEqual( blocks.divide({NUM1: Infinity, NUM2: 333}), Infinity, 'Infinity / 333 = Infinity' @@ -26,7 +26,7 @@ test('divide: division with Infinity', t => { blocks.divide({NUM1: 111, NUM2: 'Infinity'}), 0, '111 / "Infinity" = 0' ); t.strictEqual( - blocks.divide({NUM1: 222, NUM2: 'INFINITY'}), 0, '222 / "INFINITY" = 0' + blocks.divide({NUM1: 222, NUM2: 'INFINITY'}), Infinity, '222 / "INFINITY" = Infinity' ); t.strictEqual( blocks.divide({NUM1: 333, NUM2: Infinity}), 0, '333 / Infinity = 0' @@ -36,7 +36,7 @@ test('divide: division with Infinity', t => { blocks.divide({NUM1: '-Infinity', NUM2: 111}), -Infinity, '"-Infinity" / 111 = -Infinity' ); t.strictEqual( - blocks.divide({NUM1: '-INFINITY', NUM2: 222}), -Infinity, '"-INFINITY" / 222 = -Infinity' + blocks.divide({NUM1: '-INFINITY', NUM2: 222}), 0, '"-INFINITY" / 222 = 0' ); t.strictEqual( blocks.divide({NUM1: -Infinity, NUM2: 333}), -Infinity, '-Infinity / 333 = -Infinity' @@ -46,7 +46,7 @@ test('divide: division with Infinity', t => { blocks.divide({NUM1: 111, NUM2: '-Infinity'}), 0, '111 / "-Infinity" = 0' ); t.strictEqual( - blocks.divide({NUM1: 222, NUM2: '-INFINITY'}), 0, '222 / "-INFINITY" = 0' + blocks.divide({NUM1: 222, NUM2: '-INFINITY'}), Infinity, '222 / "-INFINITY" = Infinity' ); t.strictEqual( blocks.divide({NUM1: 333, NUM2: -Infinity}), 0, '333 / -Infinity = 0' @@ -60,7 +60,7 @@ test('multiply: multiply Infinity with numbers', t => { blocks.multiply({NUM1: 'Infinity', NUM2: 111}), Infinity, '"Infinity" * 111 = Infinity' ); t.strictEqual( - blocks.multiply({NUM1: 'INFINITY', NUM2: 222}), Infinity, '"INFINITY" * 222 = Infinity' + blocks.multiply({NUM1: 'INFINITY', NUM2: 222}), 0, '"INFINITY" * 222 = 0' ); t.strictEqual( blocks.multiply({NUM1: Infinity, NUM2: 333}), Infinity, 'Infinity * 333 = Infinity' @@ -69,7 +69,7 @@ test('multiply: multiply Infinity with numbers', t => { blocks.multiply({NUM1: '-Infinity', NUM2: 111}), -Infinity, '"-Infinity" * 111 = -Infinity' ); t.strictEqual( - blocks.multiply({NUM1: '-INFINITY', NUM2: 222}), -Infinity, '"-INFINITY" * 222 = -Infinity' + blocks.multiply({NUM1: '-INFINITY', NUM2: 222}), 0, '"-INFINITY" * 222 = 0' ); t.strictEqual( blocks.multiply({NUM1: -Infinity, NUM2: 333}), -Infinity, '-Infinity * 333 = -Infinity' @@ -90,7 +90,7 @@ test('add: add Infinity to a number', t => { blocks.add({NUM1: 'Infinity', NUM2: 111}), Infinity, '"Infinity" + 111 = Infinity' ); t.strictEqual( - blocks.add({NUM1: 'INFINITY', NUM2: 222}), Infinity, '"INFINITY" + 222 = Infinity' + blocks.add({NUM1: 'INFINITY', NUM2: 222}), 222, '"INFINITY" + 222 = 222' ); t.strictEqual( blocks.add({NUM1: Infinity, NUM2: 333}), Infinity, 'Infinity + 333 = Infinity' @@ -99,7 +99,7 @@ test('add: add Infinity to a number', t => { blocks.add({NUM1: '-Infinity', NUM2: 111}), -Infinity, '"-Infinity" + 111 = -Infinity' ); t.strictEqual( - blocks.add({NUM1: '-INFINITY', NUM2: 222}), -Infinity, '"-INFINITY" + 222 = -Infinity' + blocks.add({NUM1: '-INFINITY', NUM2: 222}), 222, '"-INFINITY" + 222 = 222' ); t.strictEqual( blocks.add({NUM1: -Infinity, NUM2: 333}), -Infinity, '-Infinity + 333 = -Infinity' @@ -117,7 +117,7 @@ test('subtract: subtract Infinity with a number', t => { blocks.subtract({NUM1: 'Infinity', NUM2: 111}), Infinity, '"Infinity" - 111 = Infinity' ); t.strictEqual( - blocks.subtract({NUM1: 'INFINITY', NUM2: 222}), Infinity, '"INFINITY" - 222 = Infinity' + blocks.subtract({NUM1: 'INFINITY', NUM2: 222}), -222, '"INFINITY" - 222 = -222' ); t.strictEqual( blocks.subtract({NUM1: Infinity, NUM2: 333}), Infinity, 'Infinity - 333 = Infinity' @@ -126,7 +126,7 @@ test('subtract: subtract Infinity with a number', t => { blocks.subtract({NUM1: 111, NUM2: 'Infinity'}), -Infinity, '111 - "Infinity" = -Infinity' ); t.strictEqual( - blocks.subtract({NUM1: 222, NUM2: 'INFINITY'}), -Infinity, '222 - "INFINITY" = -Infinity' + blocks.subtract({NUM1: 222, NUM2: 'INFINITY'}), 222, '222 - "INFINITY" = 222' ); t.strictEqual( blocks.subtract({NUM1: 333, NUM2: Infinity}), -Infinity, '333 - Infinity = -Infinity' From 8c2da6b56a5a11b43d6c6f60848a8c5404605f3b Mon Sep 17 00:00:00 2001 From: Valerie R Young <valerie@bocoup.com> Date: Thu, 29 Nov 2018 14:07:27 -0500 Subject: [PATCH 8/8] Satisfy linter --- src/util/cast.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/util/cast.js b/src/util/cast.js index fd40a339e..14089cd9f 100644 --- a/src/util/cast.js +++ b/src/util/cast.js @@ -145,8 +145,8 @@ class Cast { } // Handle the special case of Infinity if ( - (n1 === Infinity && n2 === Infinity) - || (n1 === -Infinity && n2 === -Infinity) + (n1 === Infinity && n2 === Infinity) || + (n1 === -Infinity && n2 === -Infinity) ) { return 0; }