-
Notifications
You must be signed in to change notification settings - Fork 2k
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
simplified test file skipping (#4996) #5003
simplified test file skipping (#4996) #5003
Conversation
I started looking through other PRs and noticed that #4880 would benefit from this feature as well. The tests which need to be skipped would have to be in their own file though. Supposing it were called testFilesToSkip = [
/async/ unless try new Function 'async () => {}'
/async_iterators/ unless try new Function 'async () * => {}'
/exponent_ops/ unless try new Function 'x = 3; x **= 2 ** 2; return x === 81'
/node9/ unless process.version.match /^v9/
].filter _.identity (I wonder why the GitHub coffee colorizer is confused by this code? Vim gets it right.) |
#4884 seems to want this too. |
While I'm confident this patch is a step forward, I've been working on a more elegant approach. My current thinking is similar to what I proposed on another thread, where each set of tests with the same requirements goes in its own directory. Instead of having a config file, Edit to add: #5007 is my more elegant solution and obsoletes this PR if it is accepted. |
I think this PR has better, easier solution for skipping specific tests. Organizing files in folders seem to much work. In most cases, there would be just one file (+ This got me thinking if there could be an even better solution to skip tests. Instead of relying on the file name pattern (or folders) we could use custom identifiers. What if sometimes test needs to pass more then one check, e.g. async generators and object rest/spread? We could add an array of conditions per test function: test "async as argument", ["async", "some_other_condition"], ->
ok ->
await winning() (The array is placed after the description because some functions are quite long and I think it is more readable this way.) The array of test conditions identifiers is similar as in this PR but contains conditions which passed the tests. testFilesConditons = [
/async/ if try new Function 'async () => {}'
/async_iterators/ if try new Function 'async () * => {}'
/exponent_ops/ if try new Function 'x = 3; x **= 2 ** 2; return x === 81'
].filter _.identity
featuresPresentFor = (cond) -> testFilesConditons.find (filePattern) -> cond.match filePattern Then, in # Our test helper function for delimiting different test cases.
global.test = (description, fnCond, fn = no) ->
canTest = (conditions) ->
passed = (yes for cond in conditions when featuresPresentFor(cond)?)
passed.length is conditions.length
try
fn = fnCond if _.isFunction fnCond
fn.test = {description, currentFile}
# If the test has specific conditions (i.e. `async generators`)
# and doesn't pass, it will be skipped.
if _.isArray(fnCond) and not canTest fnCond
result = yes
else
result = fn.call(fn)
... I tried this approach locally and it seems to be working. |
I agree with @zdenko that I’d rather a @zdenko I don’t think your suggestion will work. Use the tool |
Cakefile
Outdated
@@ -29,6 +29,15 @@ header = """ | |||
# Used in folder names like `docs/v1`. | |||
majorVersion = parseInt CoffeeScript.VERSION.split('.')[0], 10 | |||
|
|||
# Patterns of names of test files which depend on currently absent features | |||
testFilesToSkip = [ | |||
/async/ unless try new Function 'async () => {}' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn’t this be /^async$/
so that it doesn’t automatically include async_iterators
as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My reasoning was that any file with 'async' in its name probably requires the async feature, but I don't mind being more specific if you prefer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should map feature detections to specific files, rather than relying on substrings in filenames. At quick glance it’s not obvious that /async/
will match both async.coffee
and async_iterators.coffee
, and so this is a potential bug in the future when someone adds another one that overlaps with filenames they didn’t intend. I think all you need to do is change these regexes into strings? And then change fileName.match
to be an equality check.
Cakefile
Outdated
/exponent_ops/ unless try new Function 'x = 3; x **= 2 ** 2; return x === 81' | ||
].filter _.identity | ||
|
||
featuresPresentFor = (fileName) -> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you please explain the name of this variable? Why create this function as a separate variable at all, rather than an inline argument to filter
below (similar to the old code)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking of it in terms of being a predicate.
if featuresPresentFor someFile then process someFile
But you're right that it looks weird when passed to filter. Perhaps it should be "fileCanRun"? I'm open to other suggestions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe just put the function inline next to filter
? Then it doesn’t need a name.
@GeoffreyBooth you're right of course. It won't work. In my experiment, I used simple test function which passed on Node 6. |
whoops. I should have run the tests before pushing... jussec |
Cakefile
Outdated
'async.coffee' unless try new Function 'async () => {}' | ||
'async_iterators.coffee' unless try new Function 'async () * => {}' | ||
'exponent_ops.coffee' unless try new Function 'x = 3; x **= 2 ** 2; return x === 81' | ||
].filter _.identity |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the purpose of this is just to prevent undefined
elements in the array? But such elements shouldn’t matter in filename not in testFilesToSkip
. I know this is a bit nitpicky of an optimization, but I was scratching my head wondering what filter _.identity
was doing 😄
Also please put a period at the end of your comment. The comments become the documentation in the annotated source, so they should be complete sentences.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When the list was treated as a list of RegExps to be String::match'd against, undefined was unwelcome in the list. Now it's fine to leave them in. This is why we have code reviews. :)
I also didn't realize the Cakefile was subjected to the same comment -> documentation treatment as the files in src/. I'm adjusting my comments to account for this. Update forthcoming...
Cakefile
Outdated
# The `skipUnless` function adds file names to the `testFilesToSkip` array | ||
# when the feature(s) the file(s) depend on are not available. | ||
skipUnless = (code, names...) -> | ||
if not (try new Function code) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be unless (try new Function code)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
Cakefile
Outdated
if not (try new Function code) | ||
testFilesToSkip = testFilesToSkip.concat names | ||
|
||
skipUnless 'async () => {}', 'async.coffee' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Technically, if async functions aren’t supported, we should be skipping async.coffee
and async_iterators.coffee
both. It’s irrelevant in this case because you have the second check below, but if we’re thinking of this code as being self-documenting—i.e., ”the files to skip when this test fails are…”—then the second argument should be ['async.coffee', 'async_iterators.coffee']
.
Also I’m not sure how scalable this style is, when we add a test that isn’t < 60 characters or so, but we can worry about that when that day comes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's easy to re-factor for widely-applicable tests
asyncTestFiles = (files...) ->
skipUnless 'async () = {}', files...
asyncTestFiles 'async.coffee', 'async_iterator.coffee'
asyncTestFiles 'some_other_async_test.coffee'
Or we could get really wild...
asyncTest = 'async () -> {}'
appendDotCoffee = (s) -> s + ".coffee"
asyncPrefixes = (s) -> skipUnless asyncTest, s.split(/\s+/).map(appendCoffee)...
asyncPrefixes """
async
async_iterators
some_other_async_test
"""
Can you tell I <3 CoffeeScript? :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you tell I <3 CoffeeScript? :)
Indeed. 😄 We try to make the CoffeeScript codebase a “best practices” example of how to write CoffeeScript, so code golf isn’t exactly what we’re going for. 😄 I think something as simple as making the second argument an array is probably sufficient.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's my Perl roots showing. Sorry about that. :) As you said, we can cross that bridge when we come to it.
Cakefile
Outdated
|
||
skipUnless 'async () => {}', 'async.coffee' | ||
skipUnless 'async () * => {}', 'async_iterators.coffee' | ||
skipUnless 'x = 3; x **= 2 ** 2; return x === 81', 'exponent_ops.coffee' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rdeforest If you don’t mind just changing this section so that the second parameters are arrays, and the first one is both async.coffee
and async_iterators.coffee
, I think this can be merged in.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done and done. I also made the third test multi-line to serve as an example. I hope you agree it's an improvement.
Thanks @rdeforest! |
This is a re-creation of PR #4998, which automatically closed because I deleted the branch it came from, and was itself a re-creation of PR #4997 without the extraneous history. Original description follows.
Per discussion on #4893 and #4997, managing exclusion of tests when newer features are required is currently non-obvious. The goal of this PR is to make maintenance of such exceptions easy and surprise-free.
The existing method was a hard-wired exception for the file 'async.coffee'. When I added async_iterators for #4893 I was surprised because I didn't know about the
build:watch:harmony
build target nor the hardwired exception in runTests. Now both the feature test and filename match are handled on the same lines near the top of Cakefile.I included two currently unused pattern/feature pairs to demonstrate the intent and value of this approach. Both will likely be used for #4877 and #4893.
I didn't write a test for this change since it's a change to the test system.
After posting this I'm going to figure out how to remove the extra unrelated empty commits, but I wanted to get this out there before the weekend to follow through on my previous commitment.