From 4d60a6a3c8c2bc224c4975bd607cfc26e7be9147 Mon Sep 17 00:00:00 2001 From: Christoph Diehl <1614333+posidron@users.noreply.github.com> Date: Thu, 22 Mar 2018 18:36:59 +0100 Subject: [PATCH 1/9] Update README.md --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 2093525..3c07d26 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,11 @@ Octo.js bundles core functions and generic boilerplate code commonly used in mos Octo's future aims to be a stable, well-tested and well-documented standard library for fuzzing in a JavaScript environment. +# NOTE +The ES6 branch is under active development while we are incorporating it into our existing fuzzers. +This branch is current not maintained. + + ## Usage ```html From 9807668e3e7dea2002a98683c9572f23b865a7b3 Mon Sep 17 00:00:00 2001 From: pyoor Date: Wed, 28 Mar 2018 13:26:28 -0400 Subject: [PATCH 2/9] Add support for wrapping arrays elements in try/catch --- lib/utils/script.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/utils/script.js b/lib/utils/script.js index f245d5d..4070d3d 100644 --- a/lib/utils/script.js +++ b/lib/utils/script.js @@ -58,8 +58,12 @@ utils.script = { } return JSON.stringify(o) }, - safely: function (s) { - return 'try { ' + s + ' } catch(e) { }' + safely: function (obj) { + if (Array.isArray(obj)) { + return obj.map(s => utils.script.safely(s)).join(' ') + } else { + return `try { ${obj} } catch(e) { }` + } }, makeLoop: function (s, max) { return 'for (let i = 0; i < ' + (max || make.number.range()) + '; i++) {' + s + '}' From f8c8f7037ed81a864ad81e6ad15dc1073fd836cf Mon Sep 17 00:00:00 2001 From: Christoph Diehl <1614333+posidron@users.noreply.github.com> Date: Wed, 11 Apr 2018 20:36:29 +0200 Subject: [PATCH 3/9] Backport WebGL additions from Node --- lib/make/webgl.js | 187 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 187 insertions(+) diff --git a/lib/make/webgl.js b/lib/make/webgl.js index db6f384..aef2a66 100644 --- a/lib/make/webgl.js +++ b/lib/make/webgl.js @@ -551,5 +551,192 @@ make.webgl = { ]) } return random.item(usage) + }, + randomParameter: (isWebGL2) => { + let pname = [ + 'ACTIVE_TEXTURE', + 'ALIASED_LINE_WIDTH_RANGE', + 'ALIASED_POINT_SIZE_RANGE', + 'ALPHA_BITS', + 'ARRAY_BUFFER_BINDING', + 'BLEND', + 'BLEND_COLOR', + 'BLEND_DST_ALPHA', + 'BLEND_DST_RGB', + 'BLEND_EQUATION_ALPHA', + 'BLEND_EQUATION_RGB', + 'BLEND_SRC_ALPHA', + 'BLEND_SRC_RGB', + 'BLUE_BITS', + 'COLOR_CLEAR_VALUE', + 'COLOR_WRITEMASK', + 'COMPRESSED_TEXTURE_FORMATS', + 'CULL_FACE', + 'CULL_FACE_MODE', + 'CURRENT_PROGRAM', + 'DEPTH_BITS', + 'DEPTH_CLEAR_VALUE', + 'DEPTH_FUNC', + 'DEPTH_RANGE', + 'DEPTH_TEST', + 'DEPTH_WRITEMASK', + 'DITHER', + 'ELEMENT_ARRAY_BUFFER_BINDING', + 'FRAMEBUFFER_BINDING', + 'FRONT_FACE', + 'GENERATE_MIPMAP_HINT', + 'GREEN_BITS', + 'IMPLEMENTATION_COLOR_READ_FORMAT', + 'IMPLEMENTATION_COLOR_READ_TYPE', + 'LINE_WIDTH', + 'MAX_COMBINED_TEXTURE_IMAGE_UNITS', + 'MAX_CUBE_MAP_TEXTURE_SIZE', + 'MAX_FRAGMENT_UNIFORM_VECTORS', + 'MAX_RENDERBUFFER_SIZE', + 'MAX_TEXTURE_IMAGE_UNITS', + 'MAX_TEXTURE_SIZE', + 'MAX_VARYING_VECTORS', + 'MAX_VERTEX_ATTRIBS', + 'MAX_VERTEX_TEXTURE_IMAGE_UNITS', + 'MAX_VERTEX_UNIFORM_VECTORS', + 'MAX_VIEWPORT_DIMS', + 'PACK_ALIGNMENT', + 'POLYGON_OFFSET_FACTOR', + 'POLYGON_OFFSET_FILL', + 'POLYGON_OFFSET_UNITS', + 'RED_BITS', + 'RENDERBUFFER_BINDING', + 'RENDERER', + 'SAMPLE_ALPHA_TO_COVERAGE', + 'SAMPLE_BUFFERS', + 'SAMPLE_COVERAGE', + 'SAMPLE_COVERAGE_INVERT', + 'SAMPLE_COVERAGE_VALUE', + 'SAMPLES', + 'SCISSOR_BOX', + 'SCISSOR_TEST', + 'SHADING_LANGUAGE_VERSION', + 'STENCIL_BACK_FAIL', + 'STENCIL_BACK_FUNC', + 'STENCIL_BACK_PASS_DEPTH_FAIL', + 'STENCIL_BACK_PASS_DEPTH_PASS', + 'STENCIL_BACK_REF', + 'STENCIL_BACK_VALUE_MASK', + 'STENCIL_BACK_WRITEMASK', + 'STENCIL_BITS', + 'STENCIL_CLEAR_VALUE', + 'STENCIL_FAIL', + 'STENCIL_FUNC', + 'STENCIL_PASS_DEPTH_FAIL', + 'STENCIL_PASS_DEPTH_PASS', + 'STENCIL_REF', + 'STENCIL_TEST', + 'STENCIL_VALUE_MASK', + 'STENCIL_WRITEMASK', + 'SUBPIXEL_BITS', + 'TEXTURE_BINDING_2D', + 'TEXTURE_BINDING_CUBE_MAP', + 'UNPACK_ALIGNMENT', + 'UNPACK_COLORSPACE_CONVERSION_WEBGL', + 'UNPACK_FLIP_Y_WEBGL', + 'UNPACK_PREMULTIPLY_ALPHA_WEBGL', + 'VENDOR', + 'VERSION', + 'VIEWPORT', + 'VERSION', + 'SHADING_LANGUAGE_VERSION', + 'VENDOR', + 'RENDERER' + ] + + if (isWebGL2) { + pname.extends([ + 'COPY_READ_BUFFER_BINDING', + 'COPY_WRITE_BUFFER_BINDING', + 'DRAW_BUFFERi', + 'DRAW_FRAMEBUFFER_BINDING', + 'FRAGMENT_SHADER_DERIVATIVE_HINT', + 'MAX_3D_TEXTURE_SIZE', + 'MAX_ARRAY_TEXTURE_LAYERS', + 'MAX_CLIENT_WAIT_TIMEOUT_WEBGL', + 'MAX_COLOR_ATTACHMENTS', + 'MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS', + 'MAX_COMBINED_UNIFORM_BLOCKS', + 'MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS', + 'MAX_DRAW_BUFFERS', + 'MAX_ELEMENT_INDEX', + 'MAX_ELEMENTS_INDICE', + 'MAX_ELEMENTS_VERTICES', + 'MAX_FRAGMENT_INPUT_COMPONENTS', + 'MAX_FRAGMENT_UNIFORM_BLOCKS', + 'MAX_FRAGMENT_UNIFORM_COMPONENTS', + 'MAX_PROGRAM_TEXEL_OFFSET', + 'MAX_SAMPLES', + 'MAX_SERVER_WAIT_TIMEOUT', + 'MAX_TEXTURE_LOD_BIAS', + 'MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS', + 'MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS', + 'MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS', + 'MAX_UNIFORM_BLOCK_SIZE', + 'MAX_UNIFORM_BUFFER_BINDINGS', + 'MAX_VARYING_COMPONENTS', + 'MAX_VERTEX_OUTPUT_COMPONENTS', + 'MAX_VERTEX_UNIFORM_BLOCKS', + 'MAX_VERTEX_UNIFORM_COMPONENTS', + 'MIN_PROGRAM_TEXEL_OFFSET', + 'PACK_ROW_LENGTH', + 'PACK_SKIP_PIXELS', + 'PACK_SKIP_ROWS', + 'PIXEL_PACK_BUFFER_BINDING', + 'PIXEL_UNPACK_BUFFER_BINDING', + 'RASTERIZER_DISCARD', + 'READ_BUFFER', + 'READ_FRAMEBUFFER_BINDING', + 'SAMPLER_BINDING', + 'TEXTURE_BINDING_2D_ARRAY', + 'TEXTURE_BINDING_3D', + 'TRANSFORM_FEEDBACK_ACTIVE', + 'TRANSFORM_FEEDBACK_BINDING', + 'TRANSFORM_FEEDBACK_BUFFER_BINDING', + 'TRANSFORM_FEEDBACK_PAUSED', + 'UNIFORM_BUFFER_BINDING', + 'UNIFORM_BUFFER_OFFSET_ALIGNMENT', + 'UNPACK_IMAGE_HEIGHT', + 'UNPACK_ROW_LENGTH', + 'UNPACK_SKIP_IMAGES', + 'UNPACK_SKIP_PIXELS', + 'UNPACK_SKIP_ROWS', + 'VERTEX_ARRAY_BINDING' + ]) + } + return random.item(pname) + }, + randomProgramParameter: () => { + let pname = [ + 'DELETE_STATUS', + 'LINK_STATUS', + 'VALIDATE_STATUS', + 'ATTACHED_SHADERS', + 'ACTIVE_ATTRIBUTES', + 'ACTIVE_UNIFORMS', + 'TRANSFORM_FEEDBACK_BUFFER_MODE', + 'TRANSFORM_FEEDBACK_VARYINGS', + 'ACTIVE_UNIFORM_BLOCKS' + ] + return random.item(pname) + }, + randomRenderBufferParameter: () => { + let pname = [ + 'RENDERBUFFER_WIDTH', + 'RENDERBUFFER_HEIGHT', + 'RENDERBUFFER_INTERNAL_FORMAT', + 'RENDERBUFFER_RED_SIZE', + 'RENDERBUFFER_GREEN_SIZE', + 'RENDERBUFFER_BLUE_SIZE', + 'RENDERBUFFER_ALPHA_SIZE', + 'RENDERBUFFER_DEPTH_SIZE', + 'RENDERBUFFER_STENCIL_SIZE' + ] + return random.item(pname) } } From cd0d0c316a94d19390935dd8fd7d511b45981f10 Mon Sep 17 00:00:00 2001 From: posidron <1614333+posidron@users.noreply.github.com> Date: Thu, 23 Aug 2018 17:30:52 +0200 Subject: [PATCH 4/9] Fix stuff failed during merge --- README.md | 4 - lib/make/webgl.js | 187 ---------------------------------------------- 2 files changed, 191 deletions(-) diff --git a/README.md b/README.md index 4aaf101..92defe2 100644 --- a/README.md +++ b/README.md @@ -18,10 +18,6 @@ Octo.js bundles core functions and generic boilerplate code commonly used in mos Octo's future aims to be a stable, well-tested and well-documented standard library for fuzzing in a JavaScript environment. -## Note -The ES6 branch is under active development while we are incorporating it with our existing fuzzers. - - ## Playbook https://runkit.com/posidron/octo-js-playbook diff --git a/lib/make/webgl.js b/lib/make/webgl.js index d14c72b..6a4cce0 100644 --- a/lib/make/webgl.js +++ b/lib/make/webgl.js @@ -621,193 +621,6 @@ class webgl extends make { ]) } return random.item(usage) - }, - randomParameter: (isWebGL2) => { - let pname = [ - 'ACTIVE_TEXTURE', - 'ALIASED_LINE_WIDTH_RANGE', - 'ALIASED_POINT_SIZE_RANGE', - 'ALPHA_BITS', - 'ARRAY_BUFFER_BINDING', - 'BLEND', - 'BLEND_COLOR', - 'BLEND_DST_ALPHA', - 'BLEND_DST_RGB', - 'BLEND_EQUATION_ALPHA', - 'BLEND_EQUATION_RGB', - 'BLEND_SRC_ALPHA', - 'BLEND_SRC_RGB', - 'BLUE_BITS', - 'COLOR_CLEAR_VALUE', - 'COLOR_WRITEMASK', - 'COMPRESSED_TEXTURE_FORMATS', - 'CULL_FACE', - 'CULL_FACE_MODE', - 'CURRENT_PROGRAM', - 'DEPTH_BITS', - 'DEPTH_CLEAR_VALUE', - 'DEPTH_FUNC', - 'DEPTH_RANGE', - 'DEPTH_TEST', - 'DEPTH_WRITEMASK', - 'DITHER', - 'ELEMENT_ARRAY_BUFFER_BINDING', - 'FRAMEBUFFER_BINDING', - 'FRONT_FACE', - 'GENERATE_MIPMAP_HINT', - 'GREEN_BITS', - 'IMPLEMENTATION_COLOR_READ_FORMAT', - 'IMPLEMENTATION_COLOR_READ_TYPE', - 'LINE_WIDTH', - 'MAX_COMBINED_TEXTURE_IMAGE_UNITS', - 'MAX_CUBE_MAP_TEXTURE_SIZE', - 'MAX_FRAGMENT_UNIFORM_VECTORS', - 'MAX_RENDERBUFFER_SIZE', - 'MAX_TEXTURE_IMAGE_UNITS', - 'MAX_TEXTURE_SIZE', - 'MAX_VARYING_VECTORS', - 'MAX_VERTEX_ATTRIBS', - 'MAX_VERTEX_TEXTURE_IMAGE_UNITS', - 'MAX_VERTEX_UNIFORM_VECTORS', - 'MAX_VIEWPORT_DIMS', - 'PACK_ALIGNMENT', - 'POLYGON_OFFSET_FACTOR', - 'POLYGON_OFFSET_FILL', - 'POLYGON_OFFSET_UNITS', - 'RED_BITS', - 'RENDERBUFFER_BINDING', - 'RENDERER', - 'SAMPLE_ALPHA_TO_COVERAGE', - 'SAMPLE_BUFFERS', - 'SAMPLE_COVERAGE', - 'SAMPLE_COVERAGE_INVERT', - 'SAMPLE_COVERAGE_VALUE', - 'SAMPLES', - 'SCISSOR_BOX', - 'SCISSOR_TEST', - 'SHADING_LANGUAGE_VERSION', - 'STENCIL_BACK_FAIL', - 'STENCIL_BACK_FUNC', - 'STENCIL_BACK_PASS_DEPTH_FAIL', - 'STENCIL_BACK_PASS_DEPTH_PASS', - 'STENCIL_BACK_REF', - 'STENCIL_BACK_VALUE_MASK', - 'STENCIL_BACK_WRITEMASK', - 'STENCIL_BITS', - 'STENCIL_CLEAR_VALUE', - 'STENCIL_FAIL', - 'STENCIL_FUNC', - 'STENCIL_PASS_DEPTH_FAIL', - 'STENCIL_PASS_DEPTH_PASS', - 'STENCIL_REF', - 'STENCIL_TEST', - 'STENCIL_VALUE_MASK', - 'STENCIL_WRITEMASK', - 'SUBPIXEL_BITS', - 'TEXTURE_BINDING_2D', - 'TEXTURE_BINDING_CUBE_MAP', - 'UNPACK_ALIGNMENT', - 'UNPACK_COLORSPACE_CONVERSION_WEBGL', - 'UNPACK_FLIP_Y_WEBGL', - 'UNPACK_PREMULTIPLY_ALPHA_WEBGL', - 'VENDOR', - 'VERSION', - 'VIEWPORT', - 'VERSION', - 'SHADING_LANGUAGE_VERSION', - 'VENDOR', - 'RENDERER' - ] - - if (isWebGL2) { - pname.extends([ - 'COPY_READ_BUFFER_BINDING', - 'COPY_WRITE_BUFFER_BINDING', - 'DRAW_BUFFERi', - 'DRAW_FRAMEBUFFER_BINDING', - 'FRAGMENT_SHADER_DERIVATIVE_HINT', - 'MAX_3D_TEXTURE_SIZE', - 'MAX_ARRAY_TEXTURE_LAYERS', - 'MAX_CLIENT_WAIT_TIMEOUT_WEBGL', - 'MAX_COLOR_ATTACHMENTS', - 'MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS', - 'MAX_COMBINED_UNIFORM_BLOCKS', - 'MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS', - 'MAX_DRAW_BUFFERS', - 'MAX_ELEMENT_INDEX', - 'MAX_ELEMENTS_INDICE', - 'MAX_ELEMENTS_VERTICES', - 'MAX_FRAGMENT_INPUT_COMPONENTS', - 'MAX_FRAGMENT_UNIFORM_BLOCKS', - 'MAX_FRAGMENT_UNIFORM_COMPONENTS', - 'MAX_PROGRAM_TEXEL_OFFSET', - 'MAX_SAMPLES', - 'MAX_SERVER_WAIT_TIMEOUT', - 'MAX_TEXTURE_LOD_BIAS', - 'MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS', - 'MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS', - 'MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS', - 'MAX_UNIFORM_BLOCK_SIZE', - 'MAX_UNIFORM_BUFFER_BINDINGS', - 'MAX_VARYING_COMPONENTS', - 'MAX_VERTEX_OUTPUT_COMPONENTS', - 'MAX_VERTEX_UNIFORM_BLOCKS', - 'MAX_VERTEX_UNIFORM_COMPONENTS', - 'MIN_PROGRAM_TEXEL_OFFSET', - 'PACK_ROW_LENGTH', - 'PACK_SKIP_PIXELS', - 'PACK_SKIP_ROWS', - 'PIXEL_PACK_BUFFER_BINDING', - 'PIXEL_UNPACK_BUFFER_BINDING', - 'RASTERIZER_DISCARD', - 'READ_BUFFER', - 'READ_FRAMEBUFFER_BINDING', - 'SAMPLER_BINDING', - 'TEXTURE_BINDING_2D_ARRAY', - 'TEXTURE_BINDING_3D', - 'TRANSFORM_FEEDBACK_ACTIVE', - 'TRANSFORM_FEEDBACK_BINDING', - 'TRANSFORM_FEEDBACK_BUFFER_BINDING', - 'TRANSFORM_FEEDBACK_PAUSED', - 'UNIFORM_BUFFER_BINDING', - 'UNIFORM_BUFFER_OFFSET_ALIGNMENT', - 'UNPACK_IMAGE_HEIGHT', - 'UNPACK_ROW_LENGTH', - 'UNPACK_SKIP_IMAGES', - 'UNPACK_SKIP_PIXELS', - 'UNPACK_SKIP_ROWS', - 'VERTEX_ARRAY_BINDING' - ]) - } - return random.item(pname) - }, - randomProgramParameter: () => { - let pname = [ - 'DELETE_STATUS', - 'LINK_STATUS', - 'VALIDATE_STATUS', - 'ATTACHED_SHADERS', - 'ACTIVE_ATTRIBUTES', - 'ACTIVE_UNIFORMS', - 'TRANSFORM_FEEDBACK_BUFFER_MODE', - 'TRANSFORM_FEEDBACK_VARYINGS', - 'ACTIVE_UNIFORM_BLOCKS' - ] - return random.item(pname) - }, - randomRenderBufferParameter: () => { - let pname = [ - 'RENDERBUFFER_WIDTH', - 'RENDERBUFFER_HEIGHT', - 'RENDERBUFFER_INTERNAL_FORMAT', - 'RENDERBUFFER_RED_SIZE', - 'RENDERBUFFER_GREEN_SIZE', - 'RENDERBUFFER_BLUE_SIZE', - 'RENDERBUFFER_ALPHA_SIZE', - 'RENDERBUFFER_DEPTH_SIZE', - 'RENDERBUFFER_STENCIL_SIZE' - ] - return random.item(pname) } static randomParameter (isWebGL2) { From fb4e0b1fcafbd87cd8b15613118e109228f732a6 Mon Sep 17 00:00:00 2001 From: posidron <1614333+posidron@users.noreply.github.com> Date: Thu, 23 Aug 2018 22:29:55 +0200 Subject: [PATCH 5/9] Add ESDoc documentation generator --- .travis.yml | 44 +++++++++++++++++++++++--------------------- esdoc.json | 14 ++++++++++++++ package.json | 6 +++++- 3 files changed, 42 insertions(+), 22 deletions(-) create mode 100644 esdoc.json diff --git a/.travis.yml b/.travis.yml index 0433354..df0d8ab 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,31 +1,33 @@ language: node_js dist: trusty env: - - DISPLAY=:99.0 CHROME_BIN=chromium-browser +- DISPLAY=:99.0 CHROME_BIN=chromium-browser node_js: - - "7" +- "7" addons: - firefox: latest +- firefox: latest before_script: - - sh -e /etc/init.d/xvfb start -before_deploy: - - mkdir -p deploy - - ./build.py -l lib -d deploy +- sh -e /etc/init.d/xvfb start +script: +- yarn run build +after_success: +- yarn run docs notifications: slack: secure: Kwe1KBh4SMzgXaj6GQg3ZmopRYcDR3Vnd4C/gyiEOJsXzKRlU5dA7WwM/mbyOe9+ZvWDyp+g2CoQYYyvLR0SHEy1m0gn6M8fBzgSZQlWxOxAJxkwVsyxUNwAy2FylMpS+ugpw/fBMSbnSFqfZSa/tGB3KhBc0yA7V9wVS0hnwBfICqKKlFdLPcp+Us4zpXRUmcL1e0BVwm2klJi0CoKlOmTa4lI3tUa/YiuuMpgk89/PioMEr6/PP+5OfWV624djw2gznoualvcwdfZniC/oxkdTUbcu4nbTf+bFk5uwwW+XBpUhm9rMUoftngYCrWdtYHS6qenyzmflO5bgCDg5W8onv3qgMcDTEHDjlO8JMoEQLC2o27Hsyukrh2Iu0Hn2k4S10ZIDWLUZXf8ERKdU7v8o5xaiCrP7NO8fJlyGbWwYxwpFi9dpacb8qWuWw53k8+ld9orl2Zu5t1Y1QJCvT0DP0iGYSxbbICfex7im0fgh3A+MHTfhxxiGQtFJRUxqO5wGCIsT4dJJkYwRJ1HoCPyLT1x1WT8iy4lr8ivRhanEwQ5g2OmuRKz522yCrm2DPaRR/LDhqK9xghJqqh4RNx9Be5xNaxSxKs4Hcya4ZXrGaU9tzGTehRkNQksKUHq9PpdY85Mbk84wEq7zKLhtLPTn17DQePFm2DbdX4392o4= deploy: - - provider: releases - api_key: - secure: HszUovTqcRxuXTmH3/rStEe1ZeZbrZweQah8Bq0POs89P3/GjyEat11WbH9S7tfIT1yVcF736TmsDNRtl34Rmr71P7rczwFlX1yJOIamH4jT9vG8ocpZH4Orc6XA2s7UclD1HwJwStM1k2J4CV34eTTlX36ngjYrp09HertXAQWSK6TZk9RK6ql2HlNzchImz2EY1aQnfqkrFByOtM4o/SJJryJJBJUMD69gWrYu6cv51w/jMEoUtzvV6Nr2o9YOuo97B0scJmbxgfx67FaCQwEOxKtr8xT5nttJ2q6EeaAiErAieG8/o8yRsJRFrHyVm42v9PEfoe8O8Y4g/1VrtbjY3vH8LPpCotMxhshjUCBaLDc8TCC/osik6dxhv/fqjbdPwrGRW2qzsmXIZug7+YCEW+qUFRPwwX+Cr0ENnaPFMSDFxANeLF7KfF7oaVnr8JTlZnu9G+Uliqe6RNCoWfQtpTRM759cDLQp4b5eBQF2G61x2XOdRqcNZepRaRVnsaCo0lNPW9o5HCQnJnMbEB9Gdowt18PwgVucIdIPJeAPodMs40s7sUOP5Xik58b4YBG9lsgLBcfCgTy+n8JPS2LJsclG82pBACtSV+ucc6rVeD+5fbSUxn4ItaK4e0MonCv4Jnp7zGn6hErcpexBmXSdYQSrdsemKDTGHibOF6U= - file: deploy/octo.js - skip_cleanup: true - on: - tags: true - - provider: pages - github_token: - secure: HszUovTqcRxuXTmH3/rStEe1ZeZbrZweQah8Bq0POs89P3/GjyEat11WbH9S7tfIT1yVcF736TmsDNRtl34Rmr71P7rczwFlX1yJOIamH4jT9vG8ocpZH4Orc6XA2s7UclD1HwJwStM1k2J4CV34eTTlX36ngjYrp09HertXAQWSK6TZk9RK6ql2HlNzchImz2EY1aQnfqkrFByOtM4o/SJJryJJBJUMD69gWrYu6cv51w/jMEoUtzvV6Nr2o9YOuo97B0scJmbxgfx67FaCQwEOxKtr8xT5nttJ2q6EeaAiErAieG8/o8yRsJRFrHyVm42v9PEfoe8O8Y4g/1VrtbjY3vH8LPpCotMxhshjUCBaLDc8TCC/osik6dxhv/fqjbdPwrGRW2qzsmXIZug7+YCEW+qUFRPwwX+Cr0ENnaPFMSDFxANeLF7KfF7oaVnr8JTlZnu9G+Uliqe6RNCoWfQtpTRM759cDLQp4b5eBQF2G61x2XOdRqcNZepRaRVnsaCo0lNPW9o5HCQnJnMbEB9Gdowt18PwgVucIdIPJeAPodMs40s7sUOP5Xik58b4YBG9lsgLBcfCgTy+n8JPS2LJsclG82pBACtSV+ucc6rVeD+5fbSUxn4ItaK4e0MonCv4Jnp7zGn6hErcpexBmXSdYQSrdsemKDTGHibOF6U= - local_dir: deploy - skip_cleanup: true - on: - branch: master +- provider: releases + api_key: + secure: HszUovTqcRxuXTmH3/rStEe1ZeZbrZweQah8Bq0POs89P3/GjyEat11WbH9S7tfIT1yVcF736TmsDNRtl34Rmr71P7rczwFlX1yJOIamH4jT9vG8ocpZH4Orc6XA2s7UclD1HwJwStM1k2J4CV34eTTlX36ngjYrp09HertXAQWSK6TZk9RK6ql2HlNzchImz2EY1aQnfqkrFByOtM4o/SJJryJJBJUMD69gWrYu6cv51w/jMEoUtzvV6Nr2o9YOuo97B0scJmbxgfx67FaCQwEOxKtr8xT5nttJ2q6EeaAiErAieG8/o8yRsJRFrHyVm42v9PEfoe8O8Y4g/1VrtbjY3vH8LPpCotMxhshjUCBaLDc8TCC/osik6dxhv/fqjbdPwrGRW2qzsmXIZug7+YCEW+qUFRPwwX+Cr0ENnaPFMSDFxANeLF7KfF7oaVnr8JTlZnu9G+Uliqe6RNCoWfQtpTRM759cDLQp4b5eBQF2G61x2XOdRqcNZepRaRVnsaCo0lNPW9o5HCQnJnMbEB9Gdowt18PwgVucIdIPJeAPodMs40s7sUOP5Xik58b4YBG9lsgLBcfCgTy+n8JPS2LJsclG82pBACtSV+ucc6rVeD+5fbSUxn4ItaK4e0MonCv4Jnp7zGn6hErcpexBmXSdYQSrdsemKDTGHibOF6U= + file: deploy/octo.js + skip_cleanup: true + on: + tags: true +- provider: pages + keep-history: true + skip_cleanup: true + github_token: + secure: PvCrB9ckDfWedl1rdo/3mUvCy/Vg4ksZR2mj4KT3AdEyukbpMiVVDkocMFohuIq+mnRdeyhpvM2KluLHhQrEhWjvqVobiZ9/c0gWWxZHV8doDdqNUPm693MpGojMOxHT4qRih6x9KyjanmWAwy/Bc972dpD5vtaTx1gKOzRUjUI= + on: + branch: master + local_dir: docs/ diff --git a/esdoc.json b/esdoc.json new file mode 100644 index 0000000..937dff9 --- /dev/null +++ b/esdoc.json @@ -0,0 +1,14 @@ +{ + "source": "./octo", + "destination": "./docs", + "plugins": [ + { + "name": "esdoc-standard-plugin", + "option": { + "lint": { "enable": true }, + "coverage": { "enable": true } + } + }, + { "name": "esdoc-node" } + ] +} diff --git a/package.json b/package.json index f4f8a99..a79946f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@mozillasecurity/octo", - "version": "1.0.14", + "version": "1.0.15", "description": "", "keywords": [ "fuzzing", @@ -24,6 +24,7 @@ "test": "grunt test --verbose", "test:lint": "cross-env NODE_ENV=test standard --verbose", "test:lint:fix": "cross-env NODE_ENV=test standard --fix --verbose", + "docs": "esdoc -c esdoc.json", "build": "webpack -p", "watch": "webpack --watch", "precommit": "npm run test:lint", @@ -43,6 +44,9 @@ }, "devDependencies": { "cross-env": "^5.1.4", + "esdoc": "^1.1.0", + "esdoc-node": "^1.0.3", + "esdoc-standard-plugin": "^1.0.0", "grunt": "*", "grunt-karma": "*", "grunt-karma-coveralls": "*", From 60c8f2174d697b698e89bb4368dc9bd6e5058fdb Mon Sep 17 00:00:00 2001 From: posidron <1614333+posidron@users.noreply.github.com> Date: Sat, 25 Aug 2018 07:28:30 +0200 Subject: [PATCH 6/9] Fix test suite which is broken since the switch to Webpack #4 --- .gitignore | 2 + Gruntfile.js | 31 - README.md | 53 +- esdoc.json | 14 - karma.conf.js | 60 - lib/random/__tests__/mersennetwister.test.js | 92 ++ lib/random/__tests__/random.test.js | 1374 ++++++++++++++++++ package.json | 60 +- tests/index.html | 44 - tests/random/mersennetwister.js | 58 - tests/random/random.js | 892 ------------ 11 files changed, 1536 insertions(+), 1144 deletions(-) delete mode 100644 Gruntfile.js delete mode 100644 esdoc.json delete mode 100644 karma.conf.js create mode 100644 lib/random/__tests__/mersennetwister.test.js create mode 100644 lib/random/__tests__/random.test.js delete mode 100644 tests/index.html delete mode 100644 tests/random/mersennetwister.js delete mode 100644 tests/random/random.js diff --git a/.gitignore b/.gitignore index ec70e51..9895e88 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ .DS_Store +.vscode node_modules package-lock.json +yarn.lock tests/coverage diff --git a/Gruntfile.js b/Gruntfile.js deleted file mode 100644 index 5cc3bff..0000000 --- a/Gruntfile.js +++ /dev/null @@ -1,31 +0,0 @@ -module.exports = function (grunt) { - let pkg = grunt.file.readJSON('package.json') - - grunt.initConfig({ - pkg: pkg, - - karma: { - unit: { - configFile: 'karma.conf.js' - } - }, - coveralls: { - options: { - coverageDir: 'tests/coverage/', - force: true - } - }, - standard: { - options: pkg.standard, - lib: { - src: ['lib/**/*.js'] - } - } - }) - - grunt.loadNpmTasks('grunt-karma') - grunt.loadNpmTasks('grunt-karma-coveralls') - grunt.loadNpmTasks('grunt-standard') - - grunt.registerTask('test', ['standard', 'karma', 'coveralls']) -} diff --git a/README.md b/README.md index 92defe2..9f661d9 100644 --- a/README.md +++ b/README.md @@ -18,15 +18,24 @@ Octo.js bundles core functions and generic boilerplate code commonly used in mos Octo's future aims to be a stable, well-tested and well-documented standard library for fuzzing in a JavaScript environment. -## Playbook +## Table of Contents +- [Table of Contents](#table-of-contents) + - [Playbook](#playbook) + - [Usage in Node](#usage-in-node) + - [Usage in Browser](#usage-in-browser) + - [Develop](#develop) + - [Testing](#testing) + - [API Documentation](#api-documentation) + +### Playbook https://runkit.com/posidron/octo-js-playbook -## Node +### Usage in Node ``` -npm i @mozillasecurity/octo +yarn add @mozillasecurity/octo ``` ``` @@ -34,33 +43,35 @@ const {random} = require('@mozillasecurity/octo') random.init() ``` - -## Browser - -We have not yet merged ES6 to master, hence the browser version which was released on master is not up-to-date. -Use the `dist/octo.js` version of this branch by running the following command. +### Usage in Browser ``` -npm run build +yarn build ``` - -## Development +### Develop ```bash -npm install -npm run build -npm run watch -npm run test:lint +yarn install +yarn lint +yarn test +yarn build ``` -## Testing +### Testing -Tests live in the `tests/` subdirectory, and are written for [QUnit](https://qunitjs.com/). -To run tests locally, open `tests/index.html` in a browser. -The automated tests are run in Firefox or Chrome using [Karma](https://karma-runner.github.io/). -To run the automated tests: +Octo.js uses Jest for testing. Each directory should contain a `__tests__` folder containing the tests. ```bash -npm test +yarn test +``` + +### API Documentation + +* https:// + +or + +``` +yarn docs ``` diff --git a/esdoc.json b/esdoc.json deleted file mode 100644 index 937dff9..0000000 --- a/esdoc.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "source": "./octo", - "destination": "./docs", - "plugins": [ - { - "name": "esdoc-standard-plugin", - "option": { - "lint": { "enable": true }, - "coverage": { "enable": true } - } - }, - { "name": "esdoc-node" } - ] -} diff --git a/karma.conf.js b/karma.conf.js deleted file mode 100644 index 324735b..0000000 --- a/karma.conf.js +++ /dev/null @@ -1,60 +0,0 @@ -module.exports = function (config) { - let configuration = { - basePath: './tests', - - frameworks: ['qunit'], - - files: [ - '../lib/utils/init.js', - '../lib/utils/*.js', - '../lib/logging/*.js', - '../lib/make/init.js', - '../lib/make/*.js', - '../lib/random/*.js', - '**/*.js' - ], - - exclude: [ - ], - - preprocessors: { - '../lib/**/*.js': ['coverage'] - }, - - reporters: ['progress', 'coverage'], - - port: 9876, - - colors: true, - - logLevel: config.LOG_INFO, - - autoWatch: true, - - browsers: ['Chrome', 'Firefox'], - - singleRun: true, - - browserNoActivityTimeout: 30000, - - customLaunchers: { - Chrome_travis_ci: { - base: 'Chrome', - flags: ['--no-sandbox'] - } - }, - - coverageReporter: { - reporters: [ - { type: 'lcov', dir: 'coverage/' }, - { type: 'text-summary' } - ] - } - } - - if (process.env.TRAVIS) { - configuration.browsers = ['Chrome_travis_ci', 'Firefox'] - } - - config.set(configuration) -} diff --git a/lib/random/__tests__/mersennetwister.test.js b/lib/random/__tests__/mersennetwister.test.js new file mode 100644 index 0000000..49f2764 --- /dev/null +++ b/lib/random/__tests__/mersennetwister.test.js @@ -0,0 +1,92 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* eslint-env jest */ +const MersenneTwister = require('../mersennetwister') + +describe('MersenneTwister', () => { + test('uniform distribution', () => { + const N = Math.pow(2, 18) + const TRIES = 10 + const XSQ = 293.25 // quantile of chi-square dist. k=255, p=.05 + + let mt = new MersenneTwister() + mt.seed(Math.random() * 0x100000000) + + const _test = () => { + let tries = [] + for (let attempt = 0; attempt < TRIES; ++attempt) { + let data = new Uint32Array(N) + let sh + for (let i = 0; i < data.length; ++i) { + data[i] = mt.int32() + } + for (sh = 0; sh <= 24; ++sh) { + let bins = new Uint32Array(256) + for (let b of data) { + ++bins[(b >>> sh) & 0xff] + } + let xsq = bins.reduce((a, v) => { + let e = N / bins.length + return a + Math.pow(v - e, 2) / e + }, 0) + /* + * XSQ = scipy.stats.chi2.isf(.05, 255) + * if xsq > XSQ, the result is biased at 95% significance + */ + if (xsq < XSQ) { + console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`) + return true + } + tries.push(xsq) + } + if (sh === 25) { + return + } + } + return false + } + + expect(_test()).toBe(true) + }) + + test('float distribution', () => { + const N = Math.pow(2, 18) + const TRIES = 3 + const XSQ = 564.7 // quantile of chi-square dist. k=511, p=.05 + + let mt = new MersenneTwister() + mt.seed(Math.random() * 0x100000000) + + const _test = () => { + let tries = [] + for (let attempt = 0; attempt < TRIES; ++attempt) { + let bins = new Uint32Array(512) + for (let i = 0; i < N; ++i) { + let tmp = (mt.real2() * bins.length) >>> 0 + if (tmp >= bins.length) { + throw new Error('random.float() >= 1.0') + } + ++bins[tmp] + } + let xsq = bins.reduce((a, v) => { + let e = N / bins.length + return a + Math.pow(v - e, 2) / e + }, 0) + /* + * XSQ = scipy.stats.chi2.isf(.05, 511) + * if xsq > XSQ, the result is biased at 95% significance + */ + if (xsq < XSQ) { + console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`) + return true + } + tries.push(xsq) + } + // assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": " + tries) + return false + } + expect(_test()).toBe(true) + }) +}) diff --git a/lib/random/__tests__/random.test.js b/lib/random/__tests__/random.test.js new file mode 100644 index 0000000..d3434e9 --- /dev/null +++ b/lib/random/__tests__/random.test.js @@ -0,0 +1,1374 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* eslint-disable camelcase */ +/* eslint-env jest */ +const random = require('../random') + +describe('Random', () => { + test('init', () => { + expect(() => { + random.number() + }).toThrow() + random.init(1) + random.number() + }) + + test('number() corner cases', () => { + random.init(Math.random() * 0x100000000) + let sum = 0 + + for (let i = 0; i < 100; ++i) { + sum += random.number(0) + } + expect(sum).toEqual(0) + + for (let i = 0; i < 100; ++i) { + sum += random.number(1) + } + expect(sum).toEqual(0) + + let bins = new Uint32Array(2) + for (let i = 0; i < 100; ++i) { + ++bins[random.number(2)] + } + expect(bins[0] + bins[1]).toEqual(100) + expect(bins[0]).toBeGreaterThan(20) + + sum = 0 + for (let i = 0; i < 15; ++i) { + sum |= random.number() + } + expect(sum >>> 0).toEqual(0xffffffff) + }) + + test('number() uniform distriution', () => { + const N = Math.pow(2, 17) + const TRIES = 3 + const XSQ = 564.7 // quantile of chi-square dist. k=511, p=.05 + + random.init(Math.random() * 0x100000000) + + const _test = () => { + let tries = [] + for (let attempt = 0; attempt < TRIES; ++attempt) { + let bins = new Uint32Array(512) + for (let i = 0; i < N; ++i) { + let tmp = random.number(bins.length) + expect(tmp).toBeLessThan(bins.length) + ++bins[tmp] + } + let xsq = bins.reduce((a, v) => { + let e = N / bins.length + return a + Math.pow(v - e, 2) / e + }, 0) + /* + * XSQ = scipy.stats.chi2.isf(.05, 511) + * if xsq > XSQ, the result is biased at 95% significance + */ + if (xsq < XSQ) { + console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`) + return true + } + tries.push(xsq) + } + // assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": " + tries) + return false + } + + expect(_test()).toBe(true) + }) + + test('float() uniform distribution', () => { + const N = Math.pow(2, 17) + const TRIES = 3 + const XSQ = 564.7 // quantile of chi-square dist. k=511, p=.05 + + random.init(Math.random() * 0x100000000) + + const _test = () => { + let tries = [] + for (let attempt = 0; attempt < TRIES; ++attempt) { + let bins = new Uint32Array(512) + for (let i = 0; i < N; ++i) { + let tmp = (random.float() * bins.length) >>> 0 + expect(tmp).toBeLessThan(bins.length) + ++bins[tmp] + } + let xsq = bins.reduce((a, v) => { + let e = N / bins.length + return a + Math.pow(v - e, 2) / e + }, 0) + /* + * XSQ = scipy.stats.chi2.isf(.05, 511) + * if xsq > XSQ, the result is biased at 95% significance + */ + if (xsq < XSQ) { + console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`) + return true + } + tries.push(xsq) + } + // assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": " + tries) + return false + } + + expect(_test()).toBe(true) + }) + + test('range() uniform distribution', () => { + const N = 1e4 + const TRIES = 3 + const XSQ = 66.34 // quantile of chi-square dist. k=49, p=.05 + + random.init(Math.random() * 0x100000000) + + const _test = () => { + let tries = [] + for (let attempt = 0; attempt < TRIES; ++attempt) { + let bins = new Uint32Array(50) + for (let i = 0; i < N; ++i) { + let tmp = random.range(0, bins.length - 1) + expect(tmp).toBeLessThan(bins.length) + ++bins[tmp] + } + let xsq = bins.reduce((a, v) => { + let e = N / bins.length + return a + Math.pow(v - e, 2) / e + }, 0) + /* + * XSQ = scipy.stats.chi2.isf(.05, 49) + * if xsq > XSQ, the result is biased at 95% significance + */ + if (xsq < XSQ) { + console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`) + return true + } + tries.push(xsq) + } + // assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": " + tries) + return false + } + + expect(_test()).toBe(true) + }) + + test('range() uniform distribution with offset', () => { + const N = 1e4 + const TRIES = 3 + const XSQ = 66.34 // quantile of chi-square dist. k=49, p=.05 + + random.init(Math.random() * 0x100000000) + + const _test = () => { + let tries = [] + for (let attempt = 0; attempt < TRIES; ++attempt) { + let bins = new Uint32Array(50) + for (let i = 0; i < N; ++i) { + let tmp = random.range(10, 10 + bins.length - 1) - 10 + expect(tmp).toBeGreaterThanOrEqual(0) + expect(tmp).toBeLessThan(bins.length) + ++bins[tmp] + } + let xsq = bins.reduce((a, v) => { + let e = N / bins.length + return a + Math.pow(v - e, 2) / e + }, 0) + /* + * XSQ = scipy.stats.chi2.isf(.05, 49) + * if xsq > XSQ, the result is biased at 95% significance + */ + if (xsq < XSQ) { + console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`) + return true + } + tries.push(xsq) + } + // assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": " + tries) + return false + } + + expect(_test()).toBe(true) + }) + + test('range() PRNG reproducibility', () => { + let seed, result1, result2 + seed = Math.random() * 0x100000000 + for (let t = 0; t < 50; ++t) { + random.init(seed) + result1 = random.range(1, 20) + for (let i = 0; i < 5; ++i) { + random.init(seed) + result2 = random.range(1, 20) + expect(result1).toBe(result2) + } + seed = random.number() + } + }) + + test('item() exception cases', () => { + expect(() => { + random.item() + }).toThrowError('random.item() received invalid object: (undefined)') + expect(() => { + random.item(1) + }).toThrowError('random.item() received invalid object: (1)') + expect(() => { + random.item('1') + }).toThrowError('random.item() received invalid object: (1)') + expect(() => { + random.item({}) + }).toThrowError('random.item() received invalid object: ([object Object])') + }) + + test('item() distribution with list', () => { + const N = 1e4 + const TRIES = 3 + const XSQ = 5.99 // quantile of chi-square dist. k=2, p=.05 + + random.init(Math.random() * 0x100000000) + + const _test = () => { + let tries = [] + for (let attempt = 0; attempt < TRIES; ++attempt) { + let bins = new Uint32Array(3) + for (let i = 0; i < N; ++i) { + let tmp = random.item([99, 100, 101]) - 99 + expect(tmp).toBeGreaterThanOrEqual(0) + expect(tmp).toBeLessThan(bins.length) + ++bins[tmp] + } + let xsq = bins.reduce((a, v) => { + let e = N / bins.length + return a + Math.pow(v - e, 2) / e + }, 0) + /* + * XSQ = scipy.stats.chi2.isf(.05, 2) + * if xsq > XSQ, the result is biased at 95% significance + */ + if (xsq < XSQ) { + console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`) + return true + } + tries.push(xsq) + } + + // assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": " + tries) + return false + } + + expect(_test()).toBe(true) + }) + + test('key() distribution', () => { + const N = 1e4 + const TRIES = 3 + const XSQ = 5.99 // quantile of chi-square dist. k=2, p=.05 + + random.init(Math.random() * 0x100000000) + + const _test = () => { + let tries = [] + for (let attempt = 0; attempt < TRIES; ++attempt) { + let bins = new Uint32Array(3) + for (let i = 0; i < N; ++i) { + let tmp = random.key({ 99: 0, 100: 0, 101: 0 }) - 99 + expect(tmp).toBeGreaterThanOrEqual(0) + expect(tmp).toBeLessThan(bins.length) + ++bins[tmp] + } + let xsq = bins.reduce((a, v) => { + let e = N / bins.length + return a + Math.pow(v - e, 2) / e + }, 0) + /* + * XSQ = scipy.stats.chi2.isf(.05, 2) + * if xsq > XSQ, the result is biased at 95% significance + */ + if (xsq < XSQ) { + console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`) + return true + } + tries.push(xsq) + } + // assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": " + tries) + return false + } + expect(_test()).toBe(true) + }) + + test('bool() distribution', () => { + const N = 1e4 + const TRIES = 3 + const XSQ = 3.84 // quantile of chi-square dist. k=1, p=.05 + + random.init(Math.random() * 0x100000000) + + const _test = () => { + let tries = [] + for (let attempt = 0; attempt < TRIES; ++attempt) { + let bins = new Uint32Array(2) + for (let i = 0; i < N; ++i) { + let tmp = random.bool() + if (tmp === true) { + tmp = 1 + } else if (tmp === false) { + tmp = 0 + } else { + throw new Error(`Unexpected random.bool() result: ${tmp}`) + } + ++bins[tmp] + } + let xsq = bins.reduce((a, v) => { + let e = N / bins.length + return a + Math.pow(v - e, 2) / e + }, 0) + /* + * XSQ = scipy.stats.chi2.isf(.05, 1) + * if xsq > XSQ, the result is biased at 95% significance + */ + if (xsq < XSQ) { + console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`) + return true + } + tries.push(xsq) + } + // assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": " + tries) + return false + } + expect(_test()).toBe(true) + }) + + test('pick() cases', () => { + random.init(Math.random() * 0x100000000) + + for (let i = 0; i < 100; ++i) { + let tmp = Math.random() + expect(random.pick(tmp)).toEqual(tmp) + } + + for (let i = 0; i < 100; ++i) { + let tmp = (Math.random() * 100) >>> 0 + expect(random.pick(tmp)).toEqual(tmp) + } + + for (let i = 0; i < 100; ++i) { + let tmp = Math.random() + '' + expect(random.pick(tmp)).toEqual(tmp) + } + + for (let i = 0; i < 100; ++i) { + let tmp = Math.random() + expect(random.pick([tmp])).toEqual(tmp) + } + + for (let i = 0; i < 100; ++i) { + let tmp = Math.random() + expect(random.pick(() => tmp)).toEqual(tmp) + } + + for (let i = 0; i < 100; ++i) { + let tmp = Math.random() + expect(random.pick(() => [tmp])).toEqual([tmp]) + } + }) + + test('pick() with equal distribution', () => { + const N = 1e4 + const TRIES = 3 + const XSQ = 5.99 // quantile of chi-square dist. k=2, p=.05 + + random.init(Math.random() * 0x100000000) + + const _test = () => { + let tries = [] + for (let attempt = 0; attempt < TRIES; ++attempt) { + let bins = new Uint32Array(3) + for (let i = 0; i < N; ++i) { + let tmp = random.pick([ + 0, + [1, 1], + () => 2 + ]) + expect(tmp).toBeGreaterThanOrEqual(0) + expect(tmp).toBeLessThan(bins.length) + ++bins[tmp] + } + let xsq = bins.reduce((a, v) => { + let e = N / bins.length + return a + Math.pow(v - e, 2) / e + }, 0) + /* + * XSQ = scipy.stats.chi2.isf(.05, 2) + * if xsq > XSQ, the result is biased at 95% significance + */ + if (xsq < XSQ) { + console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`) + return true + } + tries.push(xsq) + } + // assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": " + tries) + return false + } + + expect(_test()).toBe(true) + }) + + test('pick() with unequal distribution', () => { + const N = 1e4 + const TRIES = 3 + const XSQ = 5.99 // quantile of chi-square dist. k=2, p=.05 + + random.init(Math.random() * 0x100000000) + + const _test = () => { + let tries = [] + for (let attempt = 0; attempt < TRIES; ++attempt) { + let bins = new Uint32Array(3) + for (let i = 0; i < N; ++i) { + let tmp = random.pick([ + [0, 1], + [1], + () => 2 + ]) + expect(tmp).toBeGreaterThanOrEqual(0) + expect(tmp).toBeLessThan(bins.length) + ++bins[tmp] + } + let xsq = + Math.pow(bins[0] - N / 6, 2) / (N / 6) + + Math.pow(bins[1] - N / 2, 2) / (N / 2) + + Math.pow(bins[2] - N / 3, 2) / (N / 3) + /* + * XSQ = scipy.stats.chi2.isf(.05, 2) + * if xsq > XSQ, the result is biased at 95% significance + */ + if (xsq < XSQ) { + console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`) + return true + } + tries.push(xsq) + } + // assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": " + tries) + return false + } + + expect(_test()).toBe(true) + }) + + test('chance(undefined) distribution', () => { + const N = 1e4 + const TRIES = 3 + const XSQ = 3.84 // quantile of chi-square dist. k=1, p=.05 + + random.init(Math.random() * 0x100000000) + + const _test = () => { + let tries = [] + for (let attempt = 0; attempt < TRIES; ++attempt) { + let bins = new Uint32Array(2) + for (let i = 0; i < N; ++i) { + let tmp = random.chance() + if (tmp === true) { + tmp = 1 + } else if (tmp === false) { + tmp = 0 + } else { + throw new Error(`Unexpected random.chance() result: ${tmp}`) + } + ++bins[tmp] + } + let xsq = bins.reduce((a, v) => { + let e = N / bins.length + return a + Math.pow(v - e, 2) / e + }, 0) + /* + * XSQ = scipy.stats.chi2.isf(.05, 1) + * if xsq > XSQ, the result is biased at 95% significance + */ + if (xsq < XSQ) { + console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`) + return true + } + tries.push(xsq) + } + // assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": " + tries) + return false + } + + expect(_test()).toBe(true) + }) + + test('chance(2) distribution', () => { + const N = 1e4 + const TRIES = 3 + const XSQ = 3.84 // quantile of chi-square dist. k=1, p=.05 + + random.init(Math.random() * 0x100000000) + + const _test = () => { + let tries = [] + for (let attempt = 0; attempt < TRIES; ++attempt) { + let bins = new Uint32Array(2) + for (let i = 0; i < N; ++i) { + let tmp = random.chance(2) + if (tmp === true) { + tmp = 1 + } else if (tmp === false) { + tmp = 0 + } else { + throw new Error(`Unexpected random.chance() result: ${tmp}`) + } + ++bins[tmp] + } + let xsq = bins.reduce((a, v) => { + let e = N / bins.length + return a + Math.pow(v - e, 2) / e + }, 0) + /* + * XSQ = scipy.stats.chi2.isf(.05, 1) + * if xsq > XSQ, the result is biased at 95% significance + */ + if (xsq < XSQ) { + console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`) + return true + } + tries.push(xsq) + } + // assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": " + tries) + return false + } + + expect(_test()).toBe(true) + }) + + test('chance(3) distribution', () => { + const N = 1e4 + const TRIES = 3 + const XSQ = 3.84 // quantile of chi-square dist. k=1, p=.05 + + random.init(Math.random() * 0x100000000) + + const _test = () => { + let tries = [] + for (let attempt = 0; attempt < TRIES; ++attempt) { + let bins = new Uint32Array(2) + for (let i = 0; i < N; ++i) { + let tmp = random.chance(3) + if (tmp === true) { + tmp = 0 + } else if (tmp === false) { + tmp = 1 + } else { + throw new Error(`Unexpected random.chance() result: ${tmp}`) + } + ++bins[tmp] + } + let xsq = + Math.pow(bins[0] - N / 3, 2) / (N / 3) + + Math.pow(bins[1] - (2 * N) / 3, 2) / ((2 * N) / 3) + /* + * XSQ = scipy.stats.chi2.isf(.05, 1) + * if xsq > XSQ, the result is biased at 95% significance + */ + if (xsq < XSQ) { + console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`) + return true + } + tries.push(xsq) + } + // assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": " + tries) + return false + } + + expect(_test()).toBe(true) + }) + + test('chance(1000) distribution', () => { + const N = 1e6 + const TRIES = 3 + const XSQ = 3.84 // quantile of chi-square dist. k=1, p=.05 + + random.init(Math.random() * 0x100000000) + + const _test = () => { + let tries = [] + for (let attempt = 0; attempt < TRIES; ++attempt) { + let bins = new Uint32Array(2) + for (let i = 0; i < N; ++i) { + let tmp = random.chance(1000) + if (tmp === true) { + tmp = 0 + } else if (tmp === false) { + tmp = 1 + } else { + throw new Error(`Unexpected random.chance() result: ${tmp}`) + } + ++bins[tmp] + } + let xsq = + Math.pow(bins[0] - N / 1000, 2) / (N / 1000) + + Math.pow(bins[1] - (999 * N) / 1000, 2) / ((999 * N) / 1000) + /* + * XSQ = scipy.stats.chi2.isf(.05, 1) + * if xsq > XSQ, the result is biased at 95% significance + */ + if (xsq < XSQ) { + console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`) + return true + } + tries.push(xsq) + } + // assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": " + tries) + return false + } + + expect(_test()).toBe(true) + }) + + test('choose() with equal distribution', () => { + const N = 1e4 + const TRIES = 3 + const XSQ = 5.99 // quantile of chi-square dist. k=2, p=.05 + + random.init(Math.random() * 0x100000000) + + const _test = () => { + let tries = [] + for (let attempt = 0; attempt < TRIES; ++attempt) { + let bins = new Uint32Array(3) + for (let i = 0; i < N; ++i) { + let tmp = random.choose([[1, 0], [1, 1], [1, 2]]) + expect(tmp).toBeLessThan(bins.length) + ++bins[tmp] + } + let xsq = bins.reduce((a, v) => { + let e = N / bins.length + return a + Math.pow(v - e, 2) / e + }, 0) + /* + * XSQ = scipy.stats.chi2.isf(.05, 2) + * if xsq > XSQ, the result is biased at 95% significance + */ + if (xsq < XSQ) { + console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`) + return true + } + tries.push(xsq) + } + // assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": " + tries) + return false + } + + expect(_test()).toBe(true) + }) + + test('choose() with unequal distribution', () => { + const N = 1e4 + const TRIES = 3 + const XSQ = 5.99 // quantile of chi-square dist. k=2, p=.05 + + random.init(Math.random() * 0x100000000) + + const _test = () => { + let tries = [] + for (let attempt = 0; attempt < TRIES; ++attempt) { + let bins = new Uint32Array(3) + for (let i = 0; i < N; ++i) { + let tmp = random.choose([[1, 0], [2, 1], [1, 2]]) + expect(tmp).toBeLessThan(bins.length) + ++bins[tmp] + } + let xsq = + Math.pow(bins[0] - N / 4, 2) / (N / 4) + + Math.pow(bins[1] - N / 2, 2) / (N / 2) + + Math.pow(bins[2] - N / 4, 2) / (N / 4) + /* + * XSQ = scipy.stats.chi2.isf(.05, 2) + * if xsq > XSQ, the result is biased at 95% significance + */ + if (xsq < XSQ) { + console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`) + return true + } + tries.push(xsq) + } + // assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": " + tries) + return false + } + + expect(_test()).toBe(true) + }) + + test('choose(flat) equal distribution with types not picked', () => { + const N = 1e4 + const TRIES = 3 + const XSQ = 5.99 // quantile of chi-square dist. k=2, p=.05 + const v1 = 1 + const v2 = [12] + const v3 = () => {} + + random.init(Math.random() * 0x100000000) + + const _test = () => { + let tries = [] + for (let attempt = 0; attempt < TRIES; ++attempt) { + let bins = new Uint32Array(3) + for (let i = 0; i < N; ++i) { + let tmp = random.choose([[1, v1], [1, v2], [1, v3]], true) + if (tmp === v1) { + tmp = 0 + } else if (tmp === v2) { + tmp = 1 + } else if (tmp === v3) { + tmp = 2 + } else { + throw new Error(`Unexpected random.choose() result: ${tmp}`) + } + ++bins[tmp] + } + let xsq = bins.reduce((a, v) => { + let e = N / bins.length + return a + Math.pow(v - e, 2) / e + }, 0) + /* + * XSQ = scipy.stats.chi2.isf(.05, 2) + * if xsq > XSQ, the result is biased at 95% significance + */ + if (xsq < XSQ) { + console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`) + return true + } + tries.push(xsq) + } + // assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": " + tries) + return false + } + + expect(_test()).toBe(true) + }) + + test('weighted() with equal distribution', () => { + const N = 1e4 + const TRIES = 3 + const XSQ = 5.99 // quantile of chi-square dist. k=2, p=.05 + + random.init(Math.random() * 0x100000000) + + const _test = () => { + let tries = [] + for (let attempt = 0; attempt < TRIES; ++attempt) { + let bins = new Uint32Array(3) + for (let i = 0; i < N; ++i) { + let tmp = random.item(random.weighted([{ w: 1, v: 0 }, { w: 1, v: 1 }, { w: 1, v: 2 }])) + expect(tmp).toBeLessThan(bins.length) + ++bins[tmp] + } + let xsq = bins.reduce((a, v) => { + let e = N / bins.length + return a + Math.pow(v - e, 2) / e + }, 0) + /* + * XSQ = scipy.stats.chi2.isf(.05, 2) + * if xsq > XSQ, the result is biased at 95% significance + */ + if (xsq < XSQ) { + console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`) + return true + } + tries.push(xsq) + } + // assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": " + tries) + return false + } + + expect(_test()).toBe(true) + }) + + test('choose(flat) with unequal distribution', () => { + const N = 1e4 + const TRIES = 3 + const XSQ = 5.99 // quantile of chi-square dist. k=2, p=.05 + + random.init(Math.random() * 0x100000000) + + const _test = () => { + let tries = [] + for (let attempt = 0; attempt < TRIES; ++attempt) { + let bins = new Uint32Array(3) + for (let i = 0; i < N; ++i) { + let tmp = random.choose([[1, 0], [2, 1], [1, 2]], true) + expect(tmp).toBeLessThan(bins.length) + ++bins[tmp] + } + let xsq = + Math.pow(bins[0] - N / 4, 2) / (N / 4) + + Math.pow(bins[1] - N / 2, 2) / (N / 2) + + Math.pow(bins[2] - N / 4, 2) / (N / 4) + /* + * XSQ = scipy.stats.chi2.isf(.05, 2) + * if xsq > XSQ, the result is biased at 95% significance + */ + if (xsq < XSQ) { + console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`) + return true + } + tries.push(xsq) + } + // assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": " + tries) + return false + } + + expect(_test()).toBe(true) + }) + + test('weighted() with unequal distribution', () => { + const N = 1e4 + const TRIES = 3 + const XSQ = 5.99 // quantile of chi-square dist. k=2, p=.05 + + random.init(Math.random() * 0x100000000) + + const _test = () => { + let tries = [] + for (let attempt = 0; attempt < TRIES; ++attempt) { + let bins = new Uint32Array(3) + for (let i = 0; i < N; ++i) { + let tmp = random.item(random.weighted([{ w: 1, v: 0 }, { w: 2, v: 1 }, { w: 1, v: 2 }])) + expect(tmp).toBeLessThan(bins.length) + ++bins[tmp] + } + let xsq = + Math.pow(bins[0] - N / 4, 2) / (N / 4) + + Math.pow(bins[1] - N / 2, 2) / (N / 2) + + Math.pow(bins[2] - N / 4, 2) / (N / 4) + /* + * XSQ = scipy.stats.chi2.isf(.05, 2) + * if xsq > XSQ, the result is biased at 95% significance + */ + if (xsq < XSQ) { + console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`) + return true + } + tries.push(xsq) + } + // assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": " + tries) + return false + } + + expect(_test()).toBe(true) + }) + + test('weighted() equal distribution with types not picked', () => { + const N = 1e4 + const TRIES = 3 + const XSQ = 5.99 // quantile of chi-square dist. k=2, p=.05 + const v1 = 1 + const v2 = [12] + const v3 = () => {} + + random.init(Math.random() * 0x100000000) + const _test = () => { + let tries = [] + for (let attempt = 0; attempt < TRIES; ++attempt) { + let bins = new Uint32Array(3) + for (let i = 0; i < N; ++i) { + let tmp = random.item( + random.weighted([{ w: 1, v: v1 }, { w: 1, v: v2 }, { w: 1, v: v3 }]) + ) + if (tmp === v1) { + tmp = 0 + } else if (tmp === v2) { + tmp = 1 + } else if (tmp === v3) { + tmp = 2 + } else { + throw new Error(`Unexpected random.weighted() result: ${tmp}`) + } + ++bins[tmp] + } + let xsq = bins.reduce((a, v) => { + let e = N / bins.length + return a + Math.pow(v - e, 2) / e + }, 0) + /* + * XSQ = scipy.stats.chi2.isf(.05, 2) + * if xsq > XSQ, the result is biased at 95% significance + */ + if (xsq < XSQ) { + console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`) + return true + } + tries.push(xsq) + } + // assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": "+ tries); + return false + } + expect(_test()).toBe(true) + }) + + test('choose() with unequal distribution and pick', () => { + const N = 1e4 + const TRIES = 3 + const XSQ = 5.99 // quantile of chi-square dist. k=2, p=.05 + + random.init(Math.random() * 0x100000000) + + const _test = () => { + let tries = [] + for (let attempt = 0; attempt < TRIES; ++attempt) { + let bins = new Uint32Array(3) + for (let i = 0; i < N; ++i) { + let tmp = random.choose([ + [1, 0], + [2, [1, 2]], + [ + 1, + () => 2 + ] + ]) + expect(tmp).toBeGreaterThanOrEqual(0) + expect(tmp).toBeLessThan(bins.length) + ++bins[tmp] + } + let xsq = + Math.pow(bins[0] - N / 4, 2) / (N / 4) + + Math.pow(bins[1] - N / 4, 2) / (N / 4) + + Math.pow(bins[2] - N / 2, 2) / (N / 2) + /* + * XSQ = scipy.stats.chi2.isf(.05, 2) + * if xsq > XSQ, the result is biased at 95% significance + */ + if (xsq < XSQ) { + console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`) + return true + } + tries.push(xsq) + } + // assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": "+ tries); + return false + } + expect(_test()).toBe(true) + }) + + test('weighted() equal distribution with types not picked', () => { + const N = 1e4 + const TRIES = 3 + const XSQ = 5.99 // quantile of chi-square dist. k=2, p=.05 + const v1 = 1 + const v2 = [12] + const v3 = () => {} + + random.init(Math.random() * 0x100000000) + + const _test = () => { + let tries = [] + for (let attempt = 0; attempt < TRIES; ++attempt) { + let bins = new Uint32Array(3) + for (let i = 0; i < N; ++i) { + let tmp = random.item( + random.weighted([{ w: 1, v: v1 }, { w: 1, v: v2 }, { w: 1, v: v3 }]) + ) + if (tmp === v1) { + tmp = 0 + } else if (tmp === v2) { + tmp = 1 + } else if (tmp === v3) { + tmp = 2 + } else { + throw new Error(`Unexpected random.weighted() result: ${tmp}`) + } + ++bins[tmp] + } + let xsq = bins.reduce((a, v) => { + let e = N / bins.length + return a + Math.pow(v - e, 2) / e + }, 0) + /* + * XSQ = scipy.stats.chi2.isf(.05, 2) + * if xsq > XSQ, the result is biased at 95% significance + */ + if (xsq < XSQ) { + console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`) + return true + } + tries.push(xsq) + } + // assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": "+ tries); + return false + } + expect(_test()).toBe(true) + }) + + test('use() distribution', () => { + const N = 1e4 + const TRIES = 3 + const XSQ = 3.84 // quantile of chi-square dist. k=1, p=.05 + + random.init(Math.random() * 0x100000000) + + const _test = () => { + let tries = [] + for (let attempt = 0; attempt < TRIES; ++attempt) { + let bins = new Uint32Array(2) + for (let i = 0; i < N; ++i) { + let rnd = Math.random() + let use = random.use(rnd) + if (use === rnd) { + use = 1 + } else if (use === '') { + use = 0 + } else { + throw new Error(`Unexpected random.use() result: ${use}`) + } + ++bins[use] + } + let xsq = bins.reduce((a, v) => { + let e = N / bins.length + return a + Math.pow(v - e, 2) / e + }, 0) + /* + * XSQ = scipy.stats.chi2.isf(.05, 1) + * if xsq > XSQ, the result is biased at 95% significance + */ + if (xsq < XSQ) { + console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`) + return true + } + tries.push(xsq) + } + // assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": "+ tries); + return false + } + expect(_test()).toBe(true) + }) + + test('shuffle() distribution', () => { + const N = 1e4 + const M = 10 + const TRIES = 3 + const XSQ = 123.23 // quantile of chi-square dist. k=M*M-1, p=.05 + // XXX: shouldn't k be M! ? + + random.init(Math.random() * 0x100000000) + + const _test = () => { + let tries = [] + for (let attempt = 0; attempt < TRIES; ++attempt) { + let bins = new Uint32Array(M * M) + for (let i = 0; i < N; ++i) { + let array = [] + for (let j = 0; j < M; ++j) array.push(j) + random.shuffle(array) + for (let j = 0; j < M; ++j) ++bins[j * M + array[j]] + } + let xsq = bins.reduce((a, v) => { + let e = N / M + return a + Math.pow(v - e, 2) / e + }, 0) + /* + * XSQ = scipy.stats.chi2.isf(.05, 99) + * if xsq > XSQ, the result is biased at 95% significance + */ + if (xsq < XSQ) { + console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`) + return true + } + tries.push(xsq) + } + // assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": "+ tries); + return false + } + + expect(_test()).toBe(true) + }) + + test('shuffled() distribution', () => { + const N = 1e4 + const M = 10 + const TRIES = 3 + const XSQ = 123.23 // quantile of chi-square dist. k=M*M-1, p=.05 + // XXX: shouldn't k be M! ? + + random.init(Math.random() * 0x100000000) + + const _test = () => { + let tries = [] + for (let attempt = 0; attempt < TRIES; ++attempt) { + let bins = new Uint32Array(M * M) + let array_ref = [] + for (let j = 0; j < M; ++j) { + array_ref.push(j) + } + for (let i = 0; i < N; ++i) { + let array = random.shuffled(array_ref) + for (let j = 0; j < M; ++j) { + ++bins[j * M + array[j]] + expect(array_ref[j]).toEqual(j) + } + let xsq = bins.reduce((a, v) => { + let e = N / M + return a + Math.pow(v - e, 2) / e + }, 0) + /* + * XSQ = scipy.stats.chi2.isf(.05, 99) + * if xsq > XSQ, the result is biased at 95% significance + */ + if (xsq < XSQ) { + console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`) + return true + } + tries.push(xsq) + } + } + // assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": "+ tries); + return false + } + + expect(_test()).toBe(true) + }) + + test.skip('subset() with equal distribution', () => { + /* + * This doesn't specify limit, so length distribution should be even, + * and selections should be even within each length. + */ + const N = 1e4 + const M = 3 + const TRIES = 3 + const B0_XSQ = 5.99 + const B1_XSQ = 15.51 + const B2_XSQ = 38.89 + const LEN_XSQ = 7.81 // quantile of chi-square dist. k=[2,8,26,3], p=.05 + + random.init(Math.random() * 0x100000000) + + const _test = () => { + let bin0_xsq, bin1_xsq, bin2_xsq, length_xsq + for (let attempt = 0; attempt < TRIES; ++attempt) { + let bins = [new Uint32Array(3), new Uint32Array(9), new Uint32Array(27)] + let lengths = new Uint32Array(M + 1) + for (let i = 0; i < N; ++i) { + let tmp = random.subset([ + 0, + [1, 1], + () => 2 + ]) + expect(tmp.length).toBeLessThanOrEqual(M) + ++lengths[tmp.length] + if (tmp.length) { + ++bins[tmp.length - 1][tmp.reduce((a, v) => a * 3 + v, 0)] + } + } + bin0_xsq = bins[0].reduce((a, v) => { + let e = N / (M + 1) / Math.pow(M, 1) + return a + Math.pow(v - e, 2) / e + }, 0) + bin1_xsq = bins[1].reduce((a, v) => { + let e = N / (M + 1) / Math.pow(M, 2) + return a + Math.pow(v - e, 2) / e + }, 0) + bin2_xsq = bins[2].reduce((a, v) => { + let e = N / (M + 1) / Math.pow(M, 3) + return a + Math.pow(v - e, 2) / e + }, 0) + length_xsq = lengths.reduce((a, v) => { + let e = N / (M + 1) + return a + Math.pow(v - e, 2) / e + }, 0) + /* + * XSQ = scipy.stats.chi2.isf(.05, 2) + * if xsq > XSQ, the result is biased at 95% significance + */ + if (bin0_xsq < B0_XSQ && bin1_xsq < B1_XSQ && bin2_xsq < B2_XSQ && length_xsq < LEN_XSQ) { + console.log(`Expected lengths x^2 to be < ${LEN_XSQ} got ${length_xsq} on attempt #${attempt + 1}`) + console.log(`Expected length=1 x^2 to be < ${B0_XSQ} got ${bin0_xsq} on attempt # ${attempt + 1}`) + console.log(`Expected length=2 x^2 to be < ${B1_XSQ} got ${bin1_xsq} on attempt #${attempt + 1}`) + console.log(`Expected length=3 x^2 to be < ${B2_XSQ} got ${bin2_xsq} on attempt #${attempt + 1}`) + return true + } + } + console.log(`Expected lengths x^2 to be < ${LEN_XSQ} got ${length_xsq}`) + console.log(`Expected length=1 x^2 to be < ${B0_XSQ} got ${bin0_xsq}`) + console.log(`Expected length=2 x^2 to be < ${B1_XSQ} got ${bin1_xsq}`) + console.log(`Expected length=3 x^2 to be < ${B2_XSQ} got ${bin2_xsq}`) + // assert.ok(false, "Failed in " + TRIES + " attempts to get xsq low enough"); + return false + } + + expect(_test()).toBe(true) + }) + + test.skip('subset(limit) with equal distribution', () => { + /* + * limit is specified, so length should always == limit, and selections should be even + */ + const N = 1e4 + const M = 3 + const TRIES = 3 + const B0_XSQ = 5.99 + const B1_XSQ = 15.51 + const B2_XSQ = 38.89 + const B3_XSQ = 101.88 // quantile of chi-square dist. k=[2,8,26,80], p=.05 + + let bin0_xsq, bin1_xsq, bin2_xsq, bin3_xsq + + random.init(Math.random() * 0x100000000) + + const _test = () => { + for (let attempt = 0; attempt < 100; ++attempt) { + expect(random.subset([1, 2, 3], 0).length).toBe(0) + } + for (let attempt = 0; attempt < TRIES; ++attempt) { + let bins = [ + new Uint32Array(3), + new Uint32Array(9), + new Uint32Array(27), + new Uint32Array(81) + ] + for (let i = 0; i < N; ++i) { + let tmp = random.subset([0, 1, 2], 1) + expect(tmp.length).toBe(1) + + ++bins[0][tmp.reduce((a, v) => a * 3 + v, 0)] + tmp = random.subset([0, 1, 2], 2) + expect(tmp.length).toBe(2) + + ++bins[1][tmp.reduce((a, v) => a * 3 + v, 0)] + tmp = random.subset([0, 1, 2], 3) + expect(tmp.length).toBe(3) + + ++bins[2][tmp.reduce((a, v) => a * 3 + v, 0)] + tmp = random.subset([0, 1, 2], 4) + expect(tmp.length).toBe(4) + + ++bins[3][tmp.reduce((a, v) => a * 3 + v, 0)] + } + bin0_xsq = bins[0].reduce((a, v) => { + let e = N / Math.pow(M, 1) + return a + Math.pow(v - e, 2) / e + }, 0) + bin1_xsq = bins[1].reduce((a, v) => { + let e = N / Math.pow(M, 2) + return a + Math.pow(v - e, 2) / e + }, 0) + bin2_xsq = bins[2].reduce((a, v) => { + let e = N / Math.pow(M, 3) + return a + Math.pow(v - e, 2) / e + }, 0) + bin3_xsq = bins[3].reduce((a, v) => { + let e = N / Math.pow(M, 4) + return a + Math.pow(v - e, 2) / e + }, 0) + /* + * XSQ = scipy.stats.chi2.isf(.05, 2) + * if xsq > XSQ, the result is biased at 95% significance + */ + if (bin0_xsq < B0_XSQ && bin1_xsq < B1_XSQ && bin2_xsq < B2_XSQ && bin3_xsq < B3_XSQ) { + // assert.ok(true, "Expected length=1 x^2 to be < " + B0_XSQ + ", got " + bin0_xsq + " on attempt #" + (attempt + 1)); + // assert.ok(true, "Expected length=2 x^2 to be < " + B1_XSQ + ", got " + bin1_xsq); + // assert.ok(true, "Expected length=3 x^2 to be < " + B2_XSQ + ", got " + bin2_xsq); + // assert.ok(true, "Expected length=4 x^2 to be < " + B3_XSQ + ", got " + bin3_xsq); + return true + } + } + console.log(`Expected length=1 x^2 to be < ${B0_XSQ} got ${bin0_xsq}`) + console.log(`Expected length=2 x^2 to be < ${B1_XSQ} got ${bin1_xsq}`) + console.log(`Expected length=3 x^2 to be < ${B2_XSQ} got ${bin2_xsq}`) + console.log(`Expected length=4 x^2 to be < ${B3_XSQ} got ${bin3_xsq}`) + // assert.ok(false, "Failed in " + TRIES + " attempts to get xsq low enough"); + return false + } + expect(_test()).toBe(true) + }) + + test('pop() distribution', () => { + const N = 1e4 + const TRIES = 3 + const XSQ = 5.99 // quantile of chi-square dist. k=2, p=.05 + + random.init(Math.random() * 0x100000000) + + const _test = () => { + let tries = [] + for (let attempt = 0; attempt < TRIES; ++attempt) { + let bins = new Uint32Array(3) + const orig = [99, 100, 101] + for (let i = 0; i < N; ++i) { + let arr = orig.slice() + let tmp = random.pop(arr) - 99 + expect(tmp).toBeGreaterThanOrEqual(0) + expect(tmp).toBeLessThan(bins.length) + expect(arr.length).toBe(2) + expect(arr.reduce((a, v) => { return a + v }, tmp)).toBe(201) + ++bins[tmp] + } + let xsq = bins.reduce((a, v) => { + let e = N / bins.length + return a + Math.pow(v - e, 2) / e + }, 0) + /* + * XSQ = scipy.stats.chi2.isf(.05, 2) + * if xsq > XSQ, the result is biased at 95% significance + */ + if (xsq < XSQ) { + console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`) + return true + } + tries.push(xsq) + } + // assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": "+ tries); + return false + } + expect(_test()).toBe(true) + }) + + test('ludOneTo() distribution', () => { + const N = 1e5 + const TRIES = 3 + const XSQ = 123.22 // quantile of chi-square dist. k=99, p=.05 + let dist = new Uint32Array(100) + + random.init(Math.random() * 0x100000000) + + /* Build the ideal distribution for comparison + * I thought this would be the PDF of the log-normal distribution, but I couldn't get mu & sigma figured out? */ + for (let i = 0; i < 100 * dist.length; ++i) { + dist[Math.floor(Math.exp((i / (100 * dist.length)) * Math.log(dist.length)))] += + N / (dist.length * 100) + } + expect(dist[0]).toEqual(0) + + const _test = () => { + let tries = [] + for (let attempt = 0; attempt < TRIES; ++attempt) { + let bins = new Uint32Array(dist.length) + let xsq = 0 + for (let i = 0; i < N; ++i) { + let tmp = random.ludOneTo(bins.length) >>> 0 + expect(tmp).toBeLessThan(bins.length) + ++bins[tmp] + } + expect(bins[0]).toEqual(0) + + for (let i = 1; i < bins.length; ++i) { + xsq += Math.pow(bins[i] - dist[i], 2) / dist[i] + } + /* + * XSQ = scipy.stats.chi2.isf(.05, 99) + * if xsq > XSQ, the result is biased at 95% significance + */ + if (xsq < XSQ) { + console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`) + return true + } + tries.push(xsq) + } + // assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": "+ tries); + return false + } + expect(_test()).toBe(true) + }) +}) diff --git a/package.json b/package.json index a79946f..0ef0dc2 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@mozillasecurity/octo", "version": "1.0.15", - "description": "", + "description": "A unified shared library which aids in building fuzzers for browsers or as complement for an existing fuzzing framework.", "keywords": [ "fuzzing", "browser", @@ -9,7 +9,7 @@ "node", "library" ], - "homepage": "https://github.com/mozillasecurity/octo/tree/es6", + "homepage": "https://github.com/mozillasecurity/octo", "repository": { "type": "git", "url": "https://github.com/mozillasecurity/octo.git" @@ -21,19 +21,18 @@ "author": "Christoph Diehl ", "license": "MPL-2.0", "scripts": { - "test": "grunt test --verbose", - "test:lint": "cross-env NODE_ENV=test standard --verbose", - "test:lint:fix": "cross-env NODE_ENV=test standard --fix --verbose", - "docs": "esdoc -c esdoc.json", + "test": "jest --silent", + "lint": "cross-env NODE_ENV=test standard --verbose", + "lint:fix": "cross-env NODE_ENV=test standard --fix --verbose", + "docs": "esdoc", "build": "webpack -p", "watch": "webpack --watch", - "precommit": "npm run test:lint", - "postinstall": "npm run build", + "precommit": "yarn lint", + "postinstall": "yarn build", "release": "np" }, "standard": { "ignore": [ - "tests/**", "dist/" ], "envs": { @@ -42,29 +41,42 @@ "es6": true } }, + "jest": { + "verbose": true + }, + "esdoc": { + "source": "./lib", + "destination": "./docs", + "plugins": [ + { + "name": "esdoc-standard-plugin", + "option": { + "lint": { + "enable": true + }, + "coverage": { + "enable": true + } + } + }, + { + "name": "esdoc-node" + } + ] + }, "devDependencies": { "cross-env": "^5.1.4", "esdoc": "^1.1.0", "esdoc-node": "^1.0.3", "esdoc-standard-plugin": "^1.0.0", - "grunt": "*", - "grunt-karma": "*", - "grunt-karma-coveralls": "*", - "grunt-standard": "*", "husky": "^0.14.3", - "karma": "*", - "karma-chrome-launcher": "*", - "karma-coverage": "*", - "karma-firefox-launcher": "^1.1.0", - "karma-qunit": "^2.0.1", + "jest": "^23.5.0", "np": "^3.0.4", - "qunit": "^2.5.1", - "qunitjs": "^2.4.1", - "standard": "^11.0.1" + "standard": "^11.0.1", + "webpack": "^4.1.1", + "webpack-cli": "^3.1.0" }, "dependencies": { - "jsesc": "^2.5.1", - "webpack": "^4.1.1", - "webpack-cli": "^2.0.12" + "jsesc": "^2.5.1" } } diff --git a/tests/index.html b/tests/index.html deleted file mode 100644 index 3cb6f54..0000000 --- a/tests/index.html +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - Octo Unit Tests - - - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tests/random/mersennetwister.js b/tests/random/mersennetwister.js deleted file mode 100644 index 9f55a6b..0000000 --- a/tests/random/mersennetwister.js +++ /dev/null @@ -1,58 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -QUnit.test("MersenneTwister test uniform distribution", function(assert) { - const N = Math.pow(2, 18), TRIES = 10, XSQ = 293.25; // quantile of chi-square dist. k=255, p=.05 - let mt = new MersenneTwister(); - mt.seed(Math.random() * 0x100000000); - for (let attempt = 0; attempt < TRIES; ++attempt) { - let data = new Uint32Array(N), sh; - for (let i = 0; i < data.length; ++i) { - data[i] = mt.int32(); - } - for (sh = 0; sh <= 24; ++sh) { - let bins = new Uint32Array(256); - for (let b of data) { - ++bins[(b >>> sh) & 0xFF]; - } - let xsq = bins.reduce(function(a, v){ let e = N / bins.length; return a + Math.pow(v - e, 2) / e; }, 0); - /* - * XSQ = scipy.stats.chi2.isf(.05, 255) - * if xsq > XSQ, the result is biased at 95% significance - */ - if (xsq >= XSQ) - break; - assert.ok(true, "Expected x^2 to be < " + XSQ + ", got " + xsq + " on attempt #" + (attempt + 1)); - } - if (sh == 25) { - return; - } - } - assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ); -}); - -QUnit.test("MersenneTwister test float distribution", function(assert) { - const N = Math.pow(2, 18), TRIES = 3, XSQ = 564.7; // quantile of chi-square dist. k=511, p=.05 - let tries = [], mt = new MersenneTwister(); - mt.seed(Math.random() * 0x100000000); - for (let attempt = 0; attempt < TRIES; ++attempt) { - let bins = new Uint32Array(512); - for (let i = 0; i < N; ++i) { - let tmp = (mt.real2() * bins.length) >>> 0; - if (tmp >= bins.length) throw "random.float() >= 1.0"; - ++bins[tmp]; - } - let xsq = bins.reduce(function(a, v){ let e = N / bins.length; return a + Math.pow(v - e, 2) / e; }, 0); - /* - * XSQ = scipy.stats.chi2.isf(.05, 511) - * if xsq > XSQ, the result is biased at 95% significance - */ - if (xsq < XSQ) { - assert.ok(true, "Expected x^2 to be < " + XSQ + ", got " + xsq + " on attempt #" + (attempt + 1)); - return; - } - tries.push(xsq); - } - assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": "+ tries); -}); diff --git a/tests/random/random.js b/tests/random/random.js deleted file mode 100644 index bc0a4eb..0000000 --- a/tests/random/random.js +++ /dev/null @@ -1,892 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -QUnit.test("random.init() is required", function(assert) { - assert.throws(random.number, /undefined/, "twister should be uninitialized before random.init()"); - random.init(1); - random.number(); -}); - -QUnit.test("random.number() corner cases", function(assert) { - random.init(Math.random() * 0x100000000); - let sum = 0; - for (let i = 0; i < 100; ++i) - sum += random.number(0); - assert.equal(sum, 0); - for (let i = 0; i < 100; ++i) - sum += random.number(1); - assert.equal(sum, 0); - let bins = new Uint32Array(2); - for (let i = 0; i < 100; ++i) - ++bins[random.number(2)]; - assert.equal(bins[0] + bins[1], 100); - assert.ok(bins[0] > 20); - sum = 0; - for (let i = 0; i < 15; ++i) - sum |= random.number(); - assert.equal(sum>>>0, 0xFFFFFFFF); -}); - -QUnit.test("random.number() uniform distribution", function(assert) { - const N = Math.pow(2, 17), TRIES = 3, XSQ = 564.7; // quantile of chi-square dist. k=511, p=.05 - let tries = []; - random.init(Math.random() * 0x100000000); - for (let attempt = 0; attempt < TRIES; ++attempt) { - let bins = new Uint32Array(512); - for (let i = 0; i < N; ++i) { - let tmp = random.number(bins.length); - if (tmp >= bins.length) throw "random.number() >= limit"; - ++bins[tmp]; - } - let xsq = bins.reduce(function(a, v){ let e = N / bins.length; return a + Math.pow(v - e, 2) / e; }, 0); - /* - * XSQ = scipy.stats.chi2.isf(.05, 511) - * if xsq > XSQ, the result is biased at 95% significance - */ - if (xsq < XSQ) { - assert.ok(true, "Expected x^2 to be < " + XSQ + ", got " + xsq + " on attempt #" + (attempt + 1)); - return; - } - tries.push(xsq); - } - assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": "+ tries); -}); - -QUnit.test("random.float() uniform distribution", function(assert) { - const N = Math.pow(2, 17), TRIES = 3, XSQ = 564.7; // quantile of chi-square dist. k=511, p=.05 - let tries = []; - random.init(Math.random() * 0x100000000); - for (let attempt = 0; attempt < TRIES; ++attempt) { - let bins = new Uint32Array(512); - for (let i = 0; i < N; ++i) { - let tmp = (random.float() * bins.length) >>> 0; - if (tmp >= bins.length) throw "random.float() >= 1.0"; - ++bins[tmp]; - } - let xsq = bins.reduce(function(a, v){ let e = N / bins.length; return a + Math.pow(v - e, 2) / e; }, 0); - /* - * XSQ = scipy.stats.chi2.isf(.05, 511) - * if xsq > XSQ, the result is biased at 95% significance - */ - if (xsq < XSQ) { - assert.ok(true, "Expected x^2 to be < " + XSQ + ", got " + xsq + " on attempt #" + (attempt + 1)); - return; - } - tries.push(xsq); - } - assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": "+ tries); -}); - -QUnit.test("random.range() uniform distribution", function(assert) { - const N = 1e4, TRIES = 3, XSQ = 66.34; // quantile of chi-square dist. k=49, p=.05 - let tries = []; - random.init(Math.random() * 0x100000000); - for (let attempt = 0; attempt < TRIES; ++attempt) { - let bins = new Uint32Array(50); - for (let i = 0; i < N; ++i) { - let tmp = random.range(0, bins.length - 1); - if (tmp >= bins.length) throw "random.range() > upper bound"; - ++bins[tmp]; - } - let xsq = bins.reduce(function(a, v){ let e = N / bins.length; return a + Math.pow(v - e, 2) / e; }, 0); - /* - * XSQ = scipy.stats.chi2.isf(.05, 49) - * if xsq > XSQ, the result is biased at 95% significance - */ - if (xsq < XSQ) { - assert.ok(true, "Expected x^2 to be < " + XSQ + ", got " + xsq + " on attempt #" + (attempt + 1)); - return; - } - tries.push(xsq); - } - assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": "+ tries); -}); - -QUnit.test("random.range() uniform distribution with offset", function(assert) { - const N = 1e4, TRIES = 3, XSQ = 66.34; // quantile of chi-square dist. k=49, p=.05 - let tries = []; - random.init(Math.random() * 0x100000000); - for (let attempt = 0; attempt < TRIES; ++attempt) { - let bins = new Uint32Array(50); - for (let i = 0; i < N; ++i) { - let tmp = random.range(10, 10 + bins.length - 1) - 10; - if (tmp < 0) throw "random.range() < lower bound"; - if (tmp >= bins.length) throw "random.range() > upper bound"; - ++bins[tmp]; - } - let xsq = bins.reduce(function(a, v){ let e = N / bins.length; return a + Math.pow(v - e, 2) / e; }, 0); - /* - * XSQ = scipy.stats.chi2.isf(.05, 49) - * if xsq > XSQ, the result is biased at 95% significance - */ - if (xsq < XSQ) { - assert.ok(true, "Expected x^2 to be < " + XSQ + ", got " + xsq + " on attempt #" + (attempt + 1)); - return; - } - tries.push(xsq); - } - assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": "+ tries); -}); - -QUnit.test("random.range() PRNG reproducibility", function(assert) { - let seed, result1, result2; - seed = Math.random() * 0x100000000; - for (let t = 0; t < 50; ++t) { - random.init(seed); - result1 = random.range(1, 20); - for (let i = 0; i < 5; ++i) { - random.init(seed); - result2 = random.range(1, 20); - assert.equal(result1, result2, "both results are the same") - } - seed = random.number(); - } -}); - -QUnit.test("random.ludOneTo() distribution", function(assert) { - const N = 1e5, TRIES = 3, XSQ = 123.22; // quantile of chi-square dist. k=99, p=.05 - let dist = new Uint32Array(100), tries = []; - random.init(Math.random() * 0x100000000); - /* build the ideal distribution for comparison - * I thought this would be the PDF of the log-normal distribution, but I couldn't get mu & sigma figured out? */ - for (let i = 0; i < (100 * dist.length); ++i) { - dist[Math.floor(Math.exp(i / (100*dist.length) * Math.log(dist.length)))] += N / (dist.length * 100); - } - assert.equal(dist[0], 0); - for (let attempt = 0; attempt < TRIES; ++attempt) { - let bins = new Uint32Array(dist.length), xsq = 0; - for (let i = 0; i < N; ++i) { - let tmp = random.ludOneTo(bins.length)>>>0; - if (tmp >= bins.length) throw "random.ludOneTo() > upper bound"; // this could happen.. - ++bins[tmp]; - } - assert.equal(bins[0], 0); - for (let i = 1; i < bins.length; ++i) { - xsq += Math.pow(bins[i] - dist[i], 2) / dist[i]; - } - /* - * XSQ = scipy.stats.chi2.isf(.05, 99) - * if xsq > XSQ, the result is biased at 95% significance - */ - if (xsq < XSQ) { - assert.ok(true, "Expected x^2 to be < " + XSQ + ", got " + xsq + " on attempt #" + (attempt + 1)); - return; - } - tries.push(xsq); - } - assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": "+ tries); -}); - -QUnit.test("random.item() exception cases", function(assert) { - assert.throws(random.item, /received an invalid object/); - assert.throws(function(){ return random.item(1); }, /received an invalid object/); - assert.throws(function(){ return random.item("1"); }, /received an invalid object/); - assert.throws(function(){ return random.item({}); }, /received an invalid object/); -}); - -QUnit.test("random.item() distribution with list", function(assert) { - const N = 1e4, TRIES = 3, XSQ = 5.99; // quantile of chi-square dist. k=2, p=.05 - let tries = []; - random.init(Math.random() * 0x100000000); - for (let attempt = 0; attempt < TRIES; ++attempt) { - let bins = new Uint32Array(3); - for (let i = 0; i < N; ++i) { - let tmp = random.item([99, 100, 101]) - 99; - if (tmp < 0) throw "random.item() < lower bound"; - if (tmp >= bins.length) throw "random.item() > upper bound"; - ++bins[tmp]; - } - let xsq = bins.reduce(function(a, v){ let e = N / bins.length; return a + Math.pow(v - e, 2) / e; }, 0); - /* - * XSQ = scipy.stats.chi2.isf(.05, 2) - * if xsq > XSQ, the result is biased at 95% significance - */ - if (xsq < XSQ) { - assert.ok(true, "Expected x^2 to be < " + XSQ + ", got " + xsq + " on attempt #" + (attempt + 1)); - return; - } - tries.push(xsq); - } - assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": "+ tries); -}); - -QUnit.test("random.key() distribution", function(assert) { - const N = 1e4, TRIES = 3, XSQ = 5.99; // quantile of chi-square dist. k=2, p=.05 - let tries = []; - random.init(Math.random() * 0x100000000); - for (let attempt = 0; attempt < TRIES; ++attempt) { - let bins = new Uint32Array(3); - for (let i = 0; i < N; ++i) { - let tmp = random.key({99: 0, 100: 0, 101: 0}) - 99; - if (tmp < 0) throw "random.key() < lower bound"; - if (tmp >= bins.length) throw "random.key() > upper bound"; - ++bins[tmp]; - } - let xsq = bins.reduce(function(a, v){ let e = N / bins.length; return a + Math.pow(v - e, 2) / e; }, 0); - /* - * XSQ = scipy.stats.chi2.isf(.05, 2) - * if xsq > XSQ, the result is biased at 95% significance - */ - if (xsq < XSQ) { - assert.ok(true, "Expected x^2 to be < " + XSQ + ", got " + xsq + " on attempt #" + (attempt + 1)); - return; - } - tries.push(xsq); - } - assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": "+ tries); -}); - -QUnit.test("random.bool() distribution", function(assert) { - const N = 1e4, TRIES = 3, XSQ = 3.84; // quantile of chi-square dist. k=1, p=.05 - let tries = []; - random.init(Math.random() * 0x100000000); - for (let attempt = 0; attempt < TRIES; ++attempt) { - let bins = new Uint32Array(2); - for (let i = 0; i < N; ++i) { - let tmp = random.bool(); - if (tmp === true) - tmp = 1; - else if (tmp === false) - tmp = 0; - else - assert.ok(false, "unexpected random.bool() result: " + tmp); - ++bins[tmp]; - } - let xsq = bins.reduce(function(a, v){ let e = N / bins.length; return a + Math.pow(v - e, 2) / e; }, 0); - /* - * XSQ = scipy.stats.chi2.isf(.05, 1) - * if xsq > XSQ, the result is biased at 95% significance - */ - if (xsq < XSQ) { - assert.ok(true, "Expected x^2 to be < " + XSQ + ", got " + xsq + " on attempt #" + (attempt + 1)); - return; - } - tries.push(xsq); - } - assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": "+ tries); -}); - -QUnit.test("random.pick() cases", function(assert) { - random.init(Math.random() * 0x100000000); - for (let i = 0; i < 100; ++i) { - let tmp = Math.random(); - assert.equal(tmp, random.pick(tmp)); - } - for (let i = 0; i < 100; ++i) { - let tmp = (Math.random() * 100) >>> 0; - assert.equal(tmp, random.pick(tmp)); - } - for (let i = 0; i < 100; ++i) { - let tmp = Math.random() + ""; - assert.equal(tmp, random.pick(tmp)); - } - for (let i = 0; i < 100; ++i) { - let tmp = Math.random(); - assert.equal(tmp, random.pick([tmp])); - } - for (let i = 0; i < 100; ++i) { - let tmp = Math.random(); - assert.equal(tmp, random.pick(function(){ return tmp; })); - } - for (let i = 0; i < 100; ++i) { - let tmp = Math.random(); - assert.equal(tmp, random.pick(function(){ return [tmp]; })); - } -}); - -QUnit.test("random.pick() with equal distribution", function(assert) { - const N = 1e4, TRIES = 3, XSQ = 5.99; // quantile of chi-square dist. k=2, p=.05 - let tries = []; - random.init(Math.random() * 0x100000000); - for (let attempt = 0; attempt < TRIES; ++attempt) { - let bins = new Uint32Array(3); - for (let i = 0; i < N; ++i) { - let tmp = random.pick([0, [1, 1], function(){ return 2; }]); - if (tmp < 0) throw "random.pick() < lower bound"; - if (tmp >= bins.length) throw "random.pick() > upper bound"; - ++bins[tmp]; - } - let xsq = bins.reduce(function(a, v){ let e = N / bins.length; return a + Math.pow(v - e, 2) / e; }, 0); - /* - * XSQ = scipy.stats.chi2.isf(.05, 2) - * if xsq > XSQ, the result is biased at 95% significance - */ - if (xsq < XSQ) { - assert.ok(true, "Expected x^2 to be < " + XSQ + ", got " + xsq + " on attempt #" + (attempt + 1)); - return; - } - tries.push(xsq); - } - assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": "+ tries); -}); - -QUnit.test("random.pick() with unequal distribution", function(assert) { - const N = 1e4, TRIES = 3, XSQ = 5.99; // quantile of chi-square dist. k=2, p=.05 - let tries = []; - random.init(Math.random() * 0x100000000); - for (let attempt = 0; attempt < TRIES; ++attempt) { - let bins = new Uint32Array(3); - for (let i = 0; i < N; ++i) { - let tmp = random.pick([[0, 1], [1], function(){ return [2]; }]); - if (tmp < 0) throw "random.pick() < lower bound"; - if (tmp >= bins.length) throw "random.pick() > upper bound"; - ++bins[tmp]; - } - let xsq = Math.pow(bins[0] - N / 6, 2) / (N / 6) + Math.pow(bins[1] - N / 2, 2) / (N / 2) + Math.pow(bins[2] - N / 3, 2) / (N / 3); - /* - * XSQ = scipy.stats.chi2.isf(.05, 2) - * if xsq > XSQ, the result is biased at 95% significance - */ - if (xsq < XSQ) { - assert.ok(true, "Expected x^2 to be < " + XSQ + ", got " + xsq + " on attempt #" + (attempt + 1)); - return; - } - tries.push(xsq); - } - assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": "+ tries); -}); - -QUnit.test("random.chance(2) distribution", function(assert) { - const N = 1e4, TRIES = 3, XSQ = 3.84; // quantile of chi-square dist. k=1, p=.05 - let tries = []; - random.init(Math.random() * 0x100000000); - for (let attempt = 0; attempt < TRIES; ++attempt) { - let bins = new Uint32Array(2); - for (let i = 0; i < N; ++i) { - let tmp = random.chance(2); - if (tmp === true) - tmp = 1; - else if (tmp === false) - tmp = 0; - else - assert.ok(false, "unexpected random.chance() result: " + tmp); - ++bins[tmp]; - } - let xsq = bins.reduce(function(a, v){ let e = N / bins.length; return a + Math.pow(v - e, 2) / e; }, 0); - /* - * XSQ = scipy.stats.chi2.isf(.05, 1) - * if xsq > XSQ, the result is biased at 95% significance - */ - if (xsq < XSQ) { - assert.ok(true, "Expected x^2 to be < " + XSQ + ", got " + xsq + " on attempt #" + (attempt + 1)); - return; - } - tries.push(xsq); - } - assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": "+ tries); -}); - -QUnit.test("random.chance(undefined) distribution", function(assert) { - const N = 1e4, TRIES = 3, XSQ = 3.84; // quantile of chi-square dist. k=1, p=.05 - let tries = []; - random.init(Math.random() * 0x100000000); - for (let attempt = 0; attempt < TRIES; ++attempt) { - let bins = new Uint32Array(2); - for (let i = 0; i < N; ++i) { - let tmp = random.chance(); - if (tmp === true) - tmp = 1; - else if (tmp === false) - tmp = 0; - else - assert.ok(false, "unexpected random.chance() result: " + tmp); - ++bins[tmp]; - } - let xsq = bins.reduce(function(a, v){ let e = N / bins.length; return a + Math.pow(v - e, 2) / e; }, 0); - /* - * XSQ = scipy.stats.chi2.isf(.05, 1) - * if xsq > XSQ, the result is biased at 95% significance - */ - if (xsq < XSQ) { - assert.ok(true, "Expected x^2 to be < " + XSQ + ", got " + xsq + " on attempt #" + (attempt + 1)); - return; - } - tries.push(xsq); - } - assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": "+ tries); -}); - -QUnit.test("random.chance(3) distribution", function(assert) { - const N = 1e4, TRIES = 3, XSQ = 3.84; // quantile of chi-square dist. k=1, p=.05 - let tries = []; - random.init(Math.random() * 0x100000000); - for (let attempt = 0; attempt < TRIES; ++attempt) { - let bins = new Uint32Array(2); - for (let i = 0; i < N; ++i) { - let tmp = random.chance(3); - if (tmp === true) - tmp = 0; - else if (tmp === false) - tmp = 1; - else - assert.ok(false, "unexpected random.chance() result: " + tmp); - ++bins[tmp]; - } - let xsq = Math.pow(bins[0] - (N / 3), 2) / (N / 3) + Math.pow(bins[1] - (2 * N / 3), 2) / (2 * N / 3); - /* - * XSQ = scipy.stats.chi2.isf(.05, 1) - * if xsq > XSQ, the result is biased at 95% significance - */ - if (xsq < XSQ) { - assert.ok(true, "Expected x^2 to be < " + XSQ + ", got " + xsq + " on attempt #" + (attempt + 1)); - return; - } - tries.push(xsq); - } - assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": "+ tries); -}); - -QUnit.test("random.chance(1000) distribution", function(assert) { - const N = 1e6, TRIES = 3, XSQ = 3.84; // quantile of chi-square dist. k=1, p=.05 - let tries = []; - random.init(Math.random() * 0x100000000); - for (let attempt = 0; attempt < TRIES; ++attempt) { - let bins = new Uint32Array(2); - for (let i = 0; i < N; ++i) { - let tmp = random.chance(1000); - if (tmp === true) - tmp = 0; - else if (tmp === false) - tmp = 1; - else - assert.ok(false, "unexpected random.chance() result: " + tmp); - ++bins[tmp]; - } - let xsq = Math.pow(bins[0] - (N / 1000), 2) / (N / 1000) + Math.pow(bins[1] - (999 * N / 1000), 2) / (999 * N / 1000); - /* - * XSQ = scipy.stats.chi2.isf(.05, 1) - * if xsq > XSQ, the result is biased at 95% significance - */ - if (xsq < XSQ) { - assert.ok(true, "Expected x^2 to be < " + XSQ + ", got " + xsq + " on attempt #" + (attempt + 1)); - return; - } - tries.push(xsq); - } - assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": "+ tries); -}); - -QUnit.test("random.choose() with equal distribution", function(assert) { - const N = 1e4, TRIES = 3, XSQ = 5.99; // quantile of chi-square dist. k=2, p=.05 - let tries = []; - random.init(Math.random() * 0x100000000); - for (let attempt = 0; attempt < TRIES; ++attempt) { - let bins = new Uint32Array(3); - for (let i = 0; i < N; ++i) { - let tmp = random.choose([[1, 0], [1, 1], [1, 2]]); - if (tmp >= bins.length) throw "random.choose() > upper bound"; - ++bins[tmp]; - } - let xsq = bins.reduce(function(a, v){ let e = N / bins.length; return a + Math.pow(v - e, 2) / e; }, 0); - /* - * XSQ = scipy.stats.chi2.isf(.05, 2) - * if xsq > XSQ, the result is biased at 95% significance - */ - if (xsq < XSQ) { - assert.ok(true, "Expected x^2 to be < " + XSQ + ", got " + xsq + " on attempt #" + (attempt + 1)); - return; - } - tries.push(xsq); - } - assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": "+ tries); -}); - -QUnit.test("random.choose() with unequal distribution", function(assert) { - const N = 1e4, TRIES = 3, XSQ = 5.99; // quantile of chi-square dist. k=2, p=.05 - let tries = []; - random.init(Math.random() * 0x100000000); - for (let attempt = 0; attempt < TRIES; ++attempt) { - let bins = new Uint32Array(3); - for (let i = 0; i < N; ++i) { - let tmp = random.choose([[1, 0], [2, 1], [1, 2]]); - if (tmp >= bins.length) throw "random.choose() > upper bound"; - ++bins[tmp]; - } - let xsq = Math.pow(bins[0] - N / 4, 2) / (N / 4) + Math.pow(bins[1] - N / 2, 2) / (N / 2) + Math.pow(bins[2] - N / 4, 2) / (N / 4); - /* - * XSQ = scipy.stats.chi2.isf(.05, 2) - * if xsq > XSQ, the result is biased at 95% significance - */ - if (xsq < XSQ) { - assert.ok(true, "Expected x^2 to be < " + XSQ + ", got " + xsq + " on attempt #" + (attempt + 1)); - return; - } - tries.push(xsq); - } - assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": "+ tries); -}); - -QUnit.test("random.choose() with unequal distribution and pick", function(assert) { - const N = 1e4, TRIES = 3, XSQ = 5.99; // quantile of chi-square dist. k=2, p=.05 - let tries = []; - random.init(Math.random() * 0x100000000); - for (let attempt = 0; attempt < TRIES; ++attempt) { - let bins = new Uint32Array(3); - for (let i = 0; i < N; ++i) { - let tmp = random.choose([[1, 0], [2, [1, 2]], [1, function(){ return 2; }]]); - if (tmp >= bins.length) throw "random.choose() > upper bound"; - ++bins[tmp]; - } - let xsq = Math.pow(bins[0] - N / 4, 2) / (N / 4) + Math.pow(bins[1] - N / 4, 2) / (N / 4) + Math.pow(bins[2] - N / 2, 2) / (N / 2); - /* - * XSQ = scipy.stats.chi2.isf(.05, 2) - * if xsq > XSQ, the result is biased at 95% significance - */ - if (xsq < XSQ) { - assert.ok(true, "Expected x^2 to be < " + XSQ + ", got " + xsq + " on attempt #" + (attempt + 1)); - return; - } - tries.push(xsq); - } - assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": "+ tries); -}); - -QUnit.test("random.choose(flat) with unequal distribution", function(assert) { - const N = 1e4, TRIES = 3, XSQ = 5.99; // quantile of chi-square dist. k=2, p=.05 - let tries = []; - random.init(Math.random() * 0x100000000); - for (let attempt = 0; attempt < TRIES; ++attempt) { - let bins = new Uint32Array(3); - for (let i = 0; i < N; ++i) { - let tmp = random.choose([[1, 0], [2, 1], [1, 2]], true); - if (tmp >= bins.length) throw "random.choose() > upper bound"; - ++bins[tmp]; - } - let xsq = Math.pow(bins[0] - N / 4, 2) / (N / 4) + Math.pow(bins[1] - N / 2, 2) / (N / 2) + Math.pow(bins[2] - N / 4, 2) / (N / 4); - /* - * XSQ = scipy.stats.chi2.isf(.05, 2) - * if xsq > XSQ, the result is biased at 95% significance - */ - if (xsq < XSQ) { - assert.ok(true, "Expected x^2 to be < " + XSQ + ", got " + xsq + " on attempt #" + (attempt + 1)); - return; - } - tries.push(xsq); - } - assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": "+ tries); -}); - -QUnit.test("random.choose(flat) equal distribution with types not picked", function(assert) { - const N = 1e4, TRIES = 3, XSQ = 5.99; // quantile of chi-square dist. k=2, p=.05 - const v1 = 1, v2 = [12], v3 = function(){}; - let tries = []; - random.init(Math.random() * 0x100000000); - for (let attempt = 0; attempt < TRIES; ++attempt) { - let bins = new Uint32Array(3); - for (let i = 0; i < N; ++i) { - let tmp = random.choose([[1, v1], [1, v2], [1, v3]], true); - if (tmp === v1) - tmp = 0; - else if (tmp === v2) - tmp = 1; - else if (tmp === v3) - tmp = 2; - else - assert.ok(false, "unexpected random.choose() result: " + tmp); - ++bins[tmp]; - } - let xsq = bins.reduce(function(a, v){ let e = N / bins.length; return a + Math.pow(v - e, 2) / e; }, 0); - /* - * XSQ = scipy.stats.chi2.isf(.05, 2) - * if xsq > XSQ, the result is biased at 95% significance - */ - if (xsq < XSQ) { - assert.ok(true, "Expected x^2 to be < " + XSQ + ", got " + xsq + " on attempt #" + (attempt + 1)); - return; - } - tries.push(xsq); - } - assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": "+ tries); -}); - -QUnit.test("random.weighted() with equal distribution", function(assert) { - const N = 1e4, TRIES = 3, XSQ = 5.99; // quantile of chi-square dist. k=2, p=.05 - let tries = []; - random.init(Math.random() * 0x100000000); - for (let attempt = 0; attempt < TRIES; ++attempt) { - let bins = new Uint32Array(3); - for (let i = 0; i < N; ++i) { - let tmp = random.item(random.weighted([{w: 1, v: 0}, {w: 1, v: 1}, {w: 1, v: 2}])); - if (tmp >= bins.length) throw "random.weighted() > upper bound"; - ++bins[tmp]; - } - let xsq = bins.reduce(function(a, v){ let e = N / bins.length; return a + Math.pow(v - e, 2) / e; }, 0); - /* - * XSQ = scipy.stats.chi2.isf(.05, 2) - * if xsq > XSQ, the result is biased at 95% significance - */ - if (xsq < XSQ) { - assert.ok(true, "Expected x^2 to be < " + XSQ + ", got " + xsq + " on attempt #" + (attempt + 1)); - return; - } - tries.push(xsq); - } - assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": "+ tries); -}); - -QUnit.test("random.weighted() with unequal distribution", function(assert) { - const N = 1e4, TRIES = 3, XSQ = 5.99; // quantile of chi-square dist. k=2, p=.05 - let tries = []; - random.init(Math.random() * 0x100000000); - for (let attempt = 0; attempt < TRIES; ++attempt) { - let bins = new Uint32Array(3); - for (let i = 0; i < N; ++i) { - let tmp = random.item(random.weighted([{w: 1, v: 0}, {w: 2, v: 1}, {w: 1, v: 2}])); - if (tmp >= bins.length) throw "random.weighted() > upper bound"; - ++bins[tmp]; - } - let xsq = Math.pow(bins[0] - N / 4, 2) / (N / 4) + Math.pow(bins[1] - N / 2, 2) / (N / 2) + Math.pow(bins[2] - N / 4, 2) / (N / 4); - /* - * XSQ = scipy.stats.chi2.isf(.05, 2) - * if xsq > XSQ, the result is biased at 95% significance - */ - if (xsq < XSQ) { - assert.ok(true, "Expected x^2 to be < " + XSQ + ", got " + xsq + " on attempt #" + (attempt + 1)); - return; - } - tries.push(xsq); - } - assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": "+ tries); -}); - -QUnit.test("random.weighted() equal distribution with types not picked", function(assert) { - const N = 1e4, TRIES = 3, XSQ = 5.99; // quantile of chi-square dist. k=2, p=.05 - const v1 = 1, v2 = [12], v3 = function(){}; - let tries = []; - random.init(Math.random() * 0x100000000); - for (let attempt = 0; attempt < TRIES; ++attempt) { - let bins = new Uint32Array(3); - for (let i = 0; i < N; ++i) { - let tmp = random.item(random.weighted([{w: 1, v: v1}, {w: 1, v: v2}, {w: 1, v: v3}])); - if (tmp === v1) - tmp = 0; - else if (tmp === v2) - tmp = 1; - else if (tmp === v3) - tmp = 2; - else - assert.ok(false, "unexpected random.weighted() result: " + tmp); - ++bins[tmp]; - } - let xsq = bins.reduce(function(a, v){ let e = N / bins.length; return a + Math.pow(v - e, 2) / e; }, 0); - /* - * XSQ = scipy.stats.chi2.isf(.05, 2) - * if xsq > XSQ, the result is biased at 95% significance - */ - if (xsq < XSQ) { - assert.ok(true, "Expected x^2 to be < " + XSQ + ", got " + xsq + " on attempt #" + (attempt + 1)); - return; - } - tries.push(xsq); - } - assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": "+ tries); -}); - -QUnit.test("random.use() distribution", function(assert) { - const N = 1e4, TRIES = 3, XSQ = 3.84; // quantile of chi-square dist. k=1, p=.05 - let tries = []; - random.init(Math.random() * 0x100000000); - for (let attempt = 0; attempt < TRIES; ++attempt) { - let bins = new Uint32Array(2); - for (let i = 0; i < N; ++i) { - let rnd = Math.random(), use = random.use(rnd); - if (use === rnd) - use = 1; - else if (use === "") - use = 0; - else - assert.ok(false, "unexpected random.use() result: " + use); - ++bins[use]; - } - let xsq = bins.reduce(function(a, v){ let e = N / bins.length; return a + Math.pow(v - e, 2) / e; }, 0); - /* - * XSQ = scipy.stats.chi2.isf(.05, 1) - * if xsq > XSQ, the result is biased at 95% significance - */ - if (xsq < XSQ) { - assert.ok(true, "Expected x^2 to be < " + XSQ + ", got " + xsq + " on attempt #" + (attempt + 1)); - return; - } - tries.push(xsq); - } - assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": "+ tries); -}); - -QUnit.test("random.shuffle() distribution", function(assert) { - const N = 1e4, M = 10, TRIES = 3, XSQ = 123.23; // quantile of chi-square dist. k=M*M-1, p=.05 - // XXX: shouldn't k be M! ? - let tries = []; - random.init(Math.random() * 0x100000000); - for (let attempt = 0; attempt < TRIES; ++attempt) { - let bins = new Uint32Array(M * M); - for (let i = 0; i < N; ++i) { - let array = []; - for (let j = 0; j < M; ++j) - array.push(j); - random.shuffle(array); - for (let j = 0; j < M; ++j) - ++bins[j * M + array[j]]; - } - let xsq = bins.reduce(function(a, v){ let e = N / M; return a + Math.pow(v - e, 2) / e; }, 0); - /* - * XSQ = scipy.stats.chi2.isf(.05, 99) - * if xsq > XSQ, the result is biased at 95% significance - */ - if (xsq < XSQ) { - assert.ok(true, "Expected x^2 to be < " + XSQ + ", got " + xsq + " on attempt #" + (attempt + 1)); - return; - } - tries.push(xsq); - } - assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": "+ tries); -}); - -QUnit.test("random.shuffled() distribution", function(assert) { - const N = 1e4, M = 10, TRIES = 3, XSQ = 123.23; // quantile of chi-square dist. k=M*M-1, p=.05 - // XXX: shouldn't k be M! ? - let tries = []; - random.init(Math.random() * 0x100000000); - for (let attempt = 0; attempt < TRIES; ++attempt) { - let bins = new Uint32Array(M * M); - let array_ref = []; - for (let j = 0; j < M; ++j) - array_ref.push(j); - for (let i = 0; i < N; ++i) { - let array = random.shuffled(array_ref); - for (let j = 0; j < M; ++j) { - ++bins[j * M + array[j]]; - if (array_ref[j] !== j) - throw "array modified"; - } - } - let xsq = bins.reduce(function(a, v){ let e = N / M; return a + Math.pow(v - e, 2) / e; }, 0); - /* - * XSQ = scipy.stats.chi2.isf(.05, 99) - * if xsq > XSQ, the result is biased at 95% significance - */ - if (xsq < XSQ) { - assert.ok(true, "Expected x^2 to be < " + XSQ + ", got " + xsq + " on attempt #" + (attempt + 1)); - return; - } - tries.push(xsq); - } - assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": "+ tries); -}); - -QUnit.test("random.subset() with equal distribution", function(assert) { - /* - * this doesn't specify limit, so length distribution should be even, and selections should be even within each length - */ - const N = 1e4, M = 3, TRIES = 3, B0_XSQ = 5.99, B1_XSQ = 15.51, B2_XSQ = 38.89, LEN_XSQ = 7.81; // quantile of chi-square dist. k=[2,8,26,3], p=.05 - let bin0_xsq, bin1_xsq, bin2_xsq, length_xsq; - random.init(Math.random() * 0x100000000); - for (let attempt = 0; attempt < TRIES; ++attempt) { - let bins = [new Uint32Array(3), new Uint32Array(9), new Uint32Array(27)], lengths = new Uint32Array(M+1); - for (let i = 0; i < N; ++i) { - let tmp = random.subset([0, [1, 1], function(){ return 2; }]); - if (tmp.length > M) throw "random.subset() result length > input"; - ++lengths[tmp.length]; - if (tmp.length) - ++bins[tmp.length-1][tmp.reduce(function(a, v){ return a * 3 + v; }, 0)]; - } - bin0_xsq = bins[0].reduce(function(a, v){ let e = N / (M + 1) / Math.pow(M, 1); return a + Math.pow(v - e, 2) / e; }, 0); - bin1_xsq = bins[1].reduce(function(a, v){ let e = N / (M + 1) / Math.pow(M, 2); return a + Math.pow(v - e, 2) / e; }, 0); - bin2_xsq = bins[2].reduce(function(a, v){ let e = N / (M + 1) / Math.pow(M, 3); return a + Math.pow(v - e, 2) / e; }, 0); - length_xsq = lengths.reduce(function(a, v){ let e = N / (M + 1); return a + Math.pow(v - e, 2) / e; }, 0); - /* - * XSQ = scipy.stats.chi2.isf(.05, 2) - * if xsq > XSQ, the result is biased at 95% significance - */ - if (bin0_xsq < B0_XSQ && bin1_xsq < B1_XSQ && bin2_xsq < B2_XSQ && length_xsq < LEN_XSQ) { - assert.ok(true, "Expected lengths x^2 to be < " + LEN_XSQ + ", got " + length_xsq + " on attempt #" + (attempt + 1)); - assert.ok(true, "Expected length=1 x^2 to be < " + B0_XSQ + ", got " + bin0_xsq + " on attempt #" + (attempt + 1)); - assert.ok(true, "Expected length=2 x^2 to be < " + B1_XSQ + ", got " + bin1_xsq + " on attempt #" + (attempt + 1)); - assert.ok(true, "Expected length=3 x^2 to be < " + B2_XSQ + ", got " + bin2_xsq + " on attempt #" + (attempt + 1)); - return; - } - } - console.log("Expected lengths x^2 to be < " + LEN_XSQ + ", got " + length_xsq); - console.log("Expected length=1 x^2 to be < " + B0_XSQ + ", got " + bin0_xsq); - console.log("Expected length=2 x^2 to be < " + B1_XSQ + ", got " + bin1_xsq); - console.log("Expected length=3 x^2 to be < " + B2_XSQ + ", got " + bin2_xsq); - assert.ok(false, "Failed in " + TRIES + " attempts to get xsq low enough"); -}); - -QUnit.test("random.subset(limit) with equal distribution", function(assert) { - /* - * limit is specified, so length should always == limit, and selections should be even - */ - const N = 1e4, M = 3, TRIES = 3, B0_XSQ = 5.99, B1_XSQ = 15.51, B2_XSQ = 38.89, B3_XSQ = 101.88; // quantile of chi-square dist. k=[2,8,26,80], p=.05 - let bin0_xsq, bin1_xsq, bin2_xsq, bin3_xsq; - random.init(Math.random() * 0x100000000); - for (let attempt = 0; attempt < 100; ++attempt) { - if (random.subset([1,2,3], 0).length !== 0) throw "random.subset(..., 0) returned non-empty array"; - } - for (let attempt = 0; attempt < TRIES; ++attempt) { - let bins = [new Uint32Array(3), new Uint32Array(9), new Uint32Array(27), new Uint32Array(81)]; - for (let i = 0; i < N; ++i) { - let tmp = random.subset([0, 1, 2], 1); - if (tmp.length !== 1) throw "random.subset() result length != limit"; - ++bins[0][tmp.reduce(function(a, v){ return a * 3 + v; }, 0)]; - tmp = random.subset([0, 1, 2], 2); - if (tmp.length !== 2) throw "random.subset() result length != limit"; - ++bins[1][tmp.reduce(function(a, v){ return a * 3 + v; }, 0)]; - tmp = random.subset([0, 1, 2], 3); - if (tmp.length !== 3) throw "random.subset() result length != limit"; - ++bins[2][tmp.reduce(function(a, v){ return a * 3 + v; }, 0)]; - tmp = random.subset([0, 1, 2], 4); - if (tmp.length !== 4) throw "random.subset() result length != limit"; - ++bins[3][tmp.reduce(function(a, v){ return a * 3 + v; }, 0)]; - } - bin0_xsq = bins[0].reduce(function(a, v){ let e = N / Math.pow(M, 1); return a + Math.pow(v - e, 2) / e; }, 0); - bin1_xsq = bins[1].reduce(function(a, v){ let e = N / Math.pow(M, 2); return a + Math.pow(v - e, 2) / e; }, 0); - bin2_xsq = bins[2].reduce(function(a, v){ let e = N / Math.pow(M, 3); return a + Math.pow(v - e, 2) / e; }, 0); - bin3_xsq = bins[3].reduce(function(a, v){ let e = N / Math.pow(M, 4); return a + Math.pow(v - e, 2) / e; }, 0); - /* - * XSQ = scipy.stats.chi2.isf(.05, 2) - * if xsq > XSQ, the result is biased at 95% significance - */ - if (bin0_xsq < B0_XSQ && bin1_xsq < B1_XSQ && bin2_xsq < B2_XSQ && bin3_xsq < B3_XSQ) { - assert.ok(true, "Expected length=1 x^2 to be < " + B0_XSQ + ", got " + bin0_xsq + " on attempt #" + (attempt + 1)); - assert.ok(true, "Expected length=2 x^2 to be < " + B1_XSQ + ", got " + bin1_xsq); - assert.ok(true, "Expected length=3 x^2 to be < " + B2_XSQ + ", got " + bin2_xsq); - assert.ok(true, "Expected length=4 x^2 to be < " + B3_XSQ + ", got " + bin3_xsq); - return; - } - } - console.log("Expected length=1 x^2 to be < " + B0_XSQ + ", got " + bin0_xsq); - console.log("Expected length=2 x^2 to be < " + B1_XSQ + ", got " + bin1_xsq); - console.log("Expected length=3 x^2 to be < " + B2_XSQ + ", got " + bin2_xsq); - console.log("Expected length=4 x^2 to be < " + B3_XSQ + ", got " + bin3_xsq); - assert.ok(false, "Failed in " + TRIES + " attempts to get xsq low enough"); -}); - -QUnit.test("random.pop() distribution", function(assert) { - const N = 1e4, TRIES = 3, XSQ = 5.99; // quantile of chi-square dist. k=2, p=.05 - let tries = []; - random.init(Math.random() * 0x100000000); - for (let attempt = 0; attempt < TRIES; ++attempt) { - let bins = new Uint32Array(3); - const orig = [99, 100, 101]; - for (let i = 0; i < N; ++i) { - let arr = orig.slice(), tmp = random.pop(arr) - 99; - if (tmp < 0) throw "random.pop() < lower bound"; - if (tmp >= bins.length) throw "random.pop() > upper bound"; - if (arr.length !== 2) throw "random.pop() did not pop"; - if (arr.reduce(function(a, v){ return a + v; }, tmp) !== 201) throw "random.pop() sum error"; - ++bins[tmp]; - } - let xsq = bins.reduce(function(a, v){ let e = N / bins.length; return a + Math.pow(v - e, 2) / e; }, 0); - /* - * XSQ = scipy.stats.chi2.isf(.05, 2) - * if xsq > XSQ, the result is biased at 95% significance - */ - if (xsq < XSQ) { - assert.ok(true, "Expected x^2 to be < " + XSQ + ", got " + xsq + " on attempt #" + (attempt + 1)); - return; - } - tries.push(xsq); - } - assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": "+ tries); -}); From d33e0fd6e4e557a376436842f26b8d1a920b2e9a Mon Sep 17 00:00:00 2001 From: posidron <1614333+posidron@users.noreply.github.com> Date: Sat, 25 Aug 2018 07:37:02 +0200 Subject: [PATCH 7/9] Fix logger imports in objects.js and random.js --- lib/random/random.js | 2 +- lib/utils/objects.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/random/random.js b/lib/random/random.js index ec87107..8983369 100644 --- a/lib/random/random.js +++ b/lib/random/random.js @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ const MersenneTwister = require('./mersennetwister') -const {logger} = require('../logging') +const logger = require('../logging') class random { /** diff --git a/lib/utils/objects.js b/lib/utils/objects.js index ee7ef0e..cbe1388 100644 --- a/lib/utils/objects.js +++ b/lib/utils/objects.js @@ -4,7 +4,7 @@ const random = require('../random') const utils = require('../utils') -const {logger} = require('../logging') +const logger = require('../logging') var o = null // eslint-disable-line no-unused-vars From 443399bd7a407c01f7f16aa187b09d120b8bccff Mon Sep 17 00:00:00 2001 From: posidron <1614333+posidron@users.noreply.github.com> Date: Sat, 25 Aug 2018 20:21:40 +0200 Subject: [PATCH 8/9] Add code coverage and coveralls support --- package.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/package.json b/package.json index 0ef0dc2..a4e3f0b 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,8 @@ "license": "MPL-2.0", "scripts": { "test": "jest --silent", + "coverage": "cross-env NODE_ENV=test jest --silent --coverage --collectCoverageFrom=lib/**/*.js", + "coveralls": "yarn coverage && cat ./coverage/lcov.info | coveralls", "lint": "cross-env NODE_ENV=test standard --verbose", "lint:fix": "cross-env NODE_ENV=test standard --fix --verbose", "docs": "esdoc", @@ -65,6 +67,7 @@ ] }, "devDependencies": { + "coveralls": "^3.0.2", "cross-env": "^5.1.4", "esdoc": "^1.1.0", "esdoc-node": "^1.0.3", From 92d58a58ef74665199c51e8bd907ddebfc23bc32 Mon Sep 17 00:00:00 2001 From: posidron <1614333+posidron@users.noreply.github.com> Date: Tue, 28 Aug 2018 00:54:24 +0200 Subject: [PATCH 9/9] Fix random seed value --- lib/random/random.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/random/random.js b/lib/random/random.js index 8983369..8986d47 100644 --- a/lib/random/random.js +++ b/lib/random/random.js @@ -13,6 +13,8 @@ class random { static init (seed) { if (seed === null || seed === undefined) { random.seed = new Date().getTime() + } else { + random.seed = seed } random.twister = new MersenneTwister()