Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: randomization support [WIP REFERENCE - DO NOT MERGE] #2301

Closed
wants to merge 13 commits into from

Conversation

boneskull
Copy link
Contributor

@boneskull boneskull commented Jun 10, 2016

Edit (January 2024, @JoshuaKGoldberg): This PR is just here as a reference to support #902. If you want to see this feature land, send a PR yourself. See #902 (comment). Thanks! ✨


  • Adds randomization support via --random CLI flag or random option in browser
  • Random seed is created if not supplied for deterministic test runs

QUESTIONS:

Automatic Seed Creation

How should we handle automatic seed creation?

Issues:

  • Some reporters (XUnit in particular) are extremely fussy about output.
  • If we were to simply echo the automatically created seed to the console, problems would happen

Suggestions:

  • In the browser, this can simply be displayed.
  • Output a .mocha-seed.txt file or something. This file would not be read from; only written to.
  • Disable automatic seed creation; require a seed for operation. Trivially generate a seed via Mocha.seed() in the browser, or --generate-seed on the CLI.

Update, Nov 20 2016:

"Specification"

  • "Automatic" seed generation is not (yet?) available, due to concerns about output and reporter support / conflicts.
  • A seed is required for randomization.
  • On the command line:
    • Mocha can generate a seed via mocha --generate-seed. A random seed will be printed to the console.
    • --random <seed> will enable randomization.
  • In the browser:
    • Mocha can generate a seed via mocha.seed().
    • Enable randomization thru mocha.options({random: mocha.seed()}) before calling mocha.run().
  • For Mocha's purposes, a seed is a positive integer which is less than or equal to the value of Number.MAX_SAFE_INTEGER, which is typically 9007199254740991.
  • Acceptable formats for seeds are numbers (decimal, octal, hexadecimal) or strings that will convert via parseInt().
  • Mocha will use a hexadecimal representation for display purposes.
  • When randomizing tests, they are only randomized within the context of a suite. In other words, suites will still run in order, but the tests within them will not.

TODO

  • Add --generate-seed
  • Add --random <seed>
  • Seed validation
  • Unit tests
  • Display active seed in HTML reporter
  • Docs
  • Support MOCHA_SEED environment variable. This would ease usage in CI envs.
  • Node integration tests
  • Browser integration tests

@boneskull boneskull added type: feature enhancement proposal High priority labels Jun 10, 2016
@boneskull boneskull added this to the 2.6.0 milestone Jun 10, 2016
@boneskull
Copy link
Contributor Author

cc @mochajs/mocha

@boneskull
Copy link
Contributor Author

cc @TimothyGu

@@ -12,6 +12,8 @@ var escapeRe = require('escape-string-regexp');
var path = require('path');
var reporters = require('./reporters');
var utils = require('./utils');
var Random = require('random-js');
var MAX_SAFE_INTEGER = 0x1fffffffffffff;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is this number coming from?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather get the number from the JS API or, if not available, an npm module.

Copy link
Contributor

@TimothyGu TimothyGu Jun 14, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From https://www.npmjs.com/package/random-js#distributions

  • Random.integer(min, max)(engine): Produce an integer within the inclusive range [min, max]. min can be at its minimum -9007199254740992 (-2 ** 53). max can be at its maximum 9007199254740992 (2 ** 53).

Number.MAX_SAFE_INTEGER is the same value.

EDIT: Number.MAX_SAFE_INTEGER is only available in ES2015 and later.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, sorry. 0x1fffffffffffff is the value of Number.MAX_SAFE_INTEGER if Number.MAX_SAFE_INTEGER is implemented. But we're not guaranteed that, of course, and I didn't want to pull in some polyfill. I'll add a comment.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dasilvacontin Would prefer not to npm install --save max-safe-integer (don't know if that exists; probably does) if I can help it.

@dasilvacontin
Copy link
Contributor

Thanks for putting time into this one peeps!

@boneskull
Copy link
Contributor Author

@ScottFreeCode @TimothyGu Thanks for looking this over. I'll implement the suggested changes

@boneskull
Copy link
Contributor Author

I don't think my questions in the description were answered though.

If I'm deciding, I'll go with --generate-seed so I don't have to deal with the file system.

@TimothyGu
Copy link
Contributor

I'm not familiar with the current situation of browser testing. How do reporters interact with browsers? Can I use, say, the XUnit reporter with the browser? If yes, will the XUnit output be made to the browser console?

@boneskull
Copy link
Contributor Author

so this is how it works on the CLI:

# generate the seed first
$ mocha --generate-seed 
0x10FFFA5D98AE6F
# run tests with the seed
$ mocha --random 0x10FFFA5D98AE6F test/acceptance/random.js --reporter spec


  random option
    seed
      ✓ should be a valid seed
      ✓ should return a finite number
      ✓ should return a hex string
    when Mocha is instantiated with "random" option
      and that option is "false"
        ✓ should not populate the "randomConfig" option
      and that option is undefined
        ✓ should not populate the "randomConfig" option
      and that option is not an integer or convertable string
        ✓ should throw
      and that option is a valid seed
        randomConfig
          ✓ should define a function to shuffle tests in the "shuffleTests" prop
          ✓ should define a "seed" prop
          ✓ should be defined
          ✓ should define a hexadecimal representation in the "hex" prop


  10 passing (24ms)

For comparison, without randomization:

$ mocha test/acceptance/random.js  --reporter spec


  random option
    seed
      ✓ should return a hex string
      ✓ should be a valid seed
      ✓ should return a finite number
    when Mocha is instantiated with "random" option
      and that option is "false"
        ✓ should not populate the "randomConfig" option
      and that option is undefined
        ✓ should not populate the "randomConfig" option
      and that option is not an integer or convertable string
        ✓ should throw
      and that option is a valid seed
        randomConfig
          ✓ should be defined
          ✓ should define a "seed" prop
          ✓ should define a hexadecimal representation in the "hex" prop
          ✓ should define a function to shuffle tests in the "shuffleTests" prop


  10 passing (24ms)

@boneskull
Copy link
Contributor Author

boneskull commented Jun 27, 2016

@TimothyGu Generally, if you're in a browser, your only available reporter is the HTML reporter.

If you're in a headless browser, then the HTML reporter is going to just dump HTML into your terminal, and you'll want one of the console-based reporters, like spec. I would also assume something with the complexity of nyan would not work in this context. Furthermore, I doubt a reporter like json-stream would work in a PhantomJS context, but who knows.

I don't use Mocha in a browser context without Karma to hold my hand, so I'm not the best source of information on this topic. @mantoni is much more qualified to elaborate on which reporters are supported, if he cares to.

@boneskull
Copy link
Contributor Author

What's left to do:

  • Display active seed in HTML reporter
  • Write integration tests to assert deterministic, random order of tests within a suite context
  • Docs
  • Support MOCHA_SEED environment variable or something. This would ease usage in CI envs.

@boneskull
Copy link
Contributor Author

note to self:

Karma setup would look something like:

var seed = require('mocha').seed();
console.log('Mocha seed: ' + seed);
config.set({
  frameworks: ['mocha'],
  client: {
    random: seed
  }
});

};

/**
* Returns `true` if `value` is a "safe" 32-bit unsigned int.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here and in various other places, it's a bit confusing to see uint32_t or integers in the range of [0, 2³² – 1] being called "safe," while what the function actually takes is integers in the range of [1, 2⁵³ – 1]. IMO, what we should allow is [0, 2⁵³ – 1] which can be described as "safe" unsigned 53-bit integer and matches with ECMAScript semantics. (In addition, we can optionally allow negative values as well.)

@boneskull
Copy link
Contributor Author

SauceLabs is taking a big ol' dump trying to run our tests in IE8. Considering the test-windows/appveyor branch hasn't been merged, it's not specific to my changes or @TimothyGu's changes there. Curiously, the pr tests pass, but the push tests fail. Must have something to do with trying to run in a branch. At least that's something to go on for SauceLabs--they're waiting for my reply to a ticket about this.

@mantoni
Copy link
Contributor

mantoni commented Jun 27, 2016

@TimothyGu @boneskull Pretty much all the reporters work in PhantomJS, too. The trick is to redirect console output and to provide a shim for process.stdout. I do this with brout and then set Mocha.process.stdout = process.stdout (see mocaccino browser setup). With this setup, even the nyan reporter output is redirected properly to the terminal.

@boneskull boneskull modified the milestones: v3.1.0, 2.6.0 Jul 2, 2016
@boneskull boneskull modified the milestones: v3.2.0, v3.1.0 Sep 18, 2016
// (traditional)-> space/name parameters body (lambda)-> parameters body multi-statement/single keep body content
// (traditional)-> space/name parameters body (lambda)->
// parameters body multi-statement/single keep body
// content
.replace(/^function(?:\s*|\s+[^(]*)\([^)]*\)\s*\{((?:.|\n)*?)\s*\}$|^\([^)]*\)\s*=>\s*(?:\{((?:.|\n)*?)\s*\}|((?:.|\n)*))$/, '$1$2$3');

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment wrapping here basically breaks this documentation of the regex. Do we need to stop the linter from paying attention to that comment? Or should we break up the regex into parts (as strings) and compose them using new RegExp?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

didn't notice this. I'll ensure it's left as-is

@macalinao
Copy link

woohoo finally

@boneskull
Copy link
Contributor Author

So basically needs more tests. But I don't necessarily need to be the person to write them.

@boneskull boneskull modified the milestone: v3.2.0 Nov 24, 2016
@Robdel12
Copy link

Is there an updated description of what needs to happen to push this over the line? We would like to randomize our mocha builds to bring the flaky tests to the surface.

@boneskull
Copy link
Contributor Author

not so much. new features are frozen until we get traction on immediate coverage needs. though I haven't had any time to pay attention to where we're at there

@craigtaub
Copy link
Contributor

@boneskull this sounds good. Worth pursuing now coverage in better place? Or do you think given where codebase is now there's a better alternative approach here?

@JoshuaKGoldberg
Copy link
Member

JoshuaKGoldberg commented Dec 2, 2023

🤖 Closing out old PRs to keep the review queue small. Cheers!

Edit: per #902 (comment), re-opening and marking as a draft.

Copy link

CLA Not Signed

@JoshuaKGoldberg JoshuaKGoldberg marked this pull request as draft January 21, 2024 06:15
@JoshuaKGoldberg JoshuaKGoldberg added the status: waiting for author waiting on response from OP - more information needed label Jan 21, 2024
@JoshuaKGoldberg JoshuaKGoldberg changed the title randomization support [WIP DO NOT MERGE] feat: randomization support [WIP DO NOT MERGE] Jan 21, 2024
Comment on lines +795 to +802
var intVal;
if (/^0[xX]/.test(value)) {
intVal = parseInt(value, 16);
} else if (/^0[^.]/.test(value)) {
intVal = parseInt(value, 8);
} else {
intVal = parseInt(value, 10);
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels like this could be simplified a bit.

Suggested change
var intVal;
if (/^0[xX]/.test(value)) {
intVal = parseInt(value, 16);
} else if (/^0[^.]/.test(value)) {
intVal = parseInt(value, 8);
} else {
intVal = parseInt(value, 10);
}
var base = 10;
var intVal;
if (/^0[xX]/.test(value)) {
base = 16;
} else if (/^0[^.]/.test(value)) {
base = 8;
}
intVal = parseInt(value, base);

Comment on lines +803 to +806
return !isNaN(intVal) && parseFloat(intVal) === intVal
&& exports.isSafeUnsignedInteger(intVal)
? intVal
: NaN;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isSafeUnsignedInteger already checks for an integer so there's no need to double check here. Also if the value is NaN, you'll still hit the right checks without the additional NaN check.

Suggested change
return !isNaN(intVal) && parseFloat(intVal) === intVal
&& exports.isSafeUnsignedInteger(intVal)
? intVal
: NaN;
return exports.isSafeUnsignedInteger(intVal) ? intVal : NaN;

* @returns {string} Hexadecimal representation thereof
*/
exports.toHex = function toHex(int) {
return '0x' + int.toString(16).toUpperCase();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't have any validation compared to other functions.toHex('foo') will happily return 0xFOO. Is that expected behavior?

@JoshuaKGoldberg JoshuaKGoldberg changed the title feat: randomization support [WIP DO NOT MERGE] feat: randomization support [WIP REFERENCE - DO NOT MERGE] Jan 21, 2024
@JoshuaKGoldberg
Copy link
Member

JoshuaKGoldberg commented Jan 21, 2024

@fearphage thanks for the reviews, but this PR is just here as a reference. It's got a whole bunch of merge conflicts. I edited the title & OP to make that more clear.

If you're interested in the work (great! 🙌) the right next step would be to send a separate PR.

Edit: Closing to keep our review queue small.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: waiting for author waiting on response from OP - more information needed type: feature enhancement proposal
Projects
None yet
Development

Successfully merging this pull request may close these issues.