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

Allow example groups to order tests either randomly OR as they are defined #766

Closed
wants to merge 9 commits into from

Conversation

jwfriese
Copy link
Contributor

@jwfriese jwfriese commented Jan 25, 2018

This PR attempts to add the ordering functionality requested in #759 .

As written, this PR adds a new argument to describe-type and context-type DSL methods -- an order enum value, which can be one of .defined or .random.

.defined will ensure that the examples are run in the order they are defined within the describe or context blocks. This includes respecting nested describe and context blocks in turn. The new test file (ExplicitOrderingTests.swift) shows you exactly how you can expect it to handle nested describes.

.random will randomize the examples within the describe, although it takes care to respect the order values defined by nested describe and context blocks.

Now, there exist some caveats to note here. The most significant of these is the fact that XCode 9 appears to revert to using alphabetical-by-name ordering of tests only when you use the side tray candies to run the tests. In other words, XCTest does not appear to respect the ordering we set as the return value of testInvocations from QuickSpec. I suspect this is related to #744 and #745, but I'm not sure.

I argue that this PR is still worth adding, because it's still giving many users (anyone using Cmd+U or the command line to run their tests) new goodness. Please let me know if you disagree!

Also, I chose to default the Order to .defined because of the principle of least surprise -- that's closest to how things work now.

Technically, we should probably default to .random since having an explicit ordering to tests is not the best practice.

Here's some stuff I still want to do with this PR once I get some feedback of the high-level design:

  • complete documentation of how to use the feature
  • it's probably unnecessary to maintain childGroups and childExamples as separate collections now within the ExampleGroup class True, but not an optimization that I feel is worth it for now.
  • add some context blocks to the test
  • super bonus: allow user to define a sorting function (I actually think this should be done separately though)

@QuickBot
Copy link

QuickBot commented Jan 25, 2018

2 Warnings
⚠️ Need to add an unit test if you’re modifying swift source
⚠️ Big PR

Generated by 🚫 Danger

@jwfriese
Copy link
Contributor Author

jwfriese commented Feb 1, 2018

By the way, this pull request also solves #152 🎉

This may not be the most lightweight API to allow it, but it achieves it nonetheless.

@jwfriese
Copy link
Contributor Author

jwfriese commented Feb 1, 2018

@modocache or @jeffh - This has been out here for about a week now, and has a couple of positive reactions. I'd love to hear feedback if you have any. Otherwise, I think this is ready to go! 😃

}

/**
Defines an example group. Equivalent to `describe`.
*/
public func context(_ description: String, flags: FilterFlags = [:], closure: () -> Void) {
World.sharedWorld.context(description, flags: flags, closure: closure)
public func context(_ description: String, order: Order = Order.defined, flags: FilterFlags = [:], closure: () -> Void) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: I would avoid duplicating the type in all these: order: Order = .defined.

@@ -143,7 +143,7 @@ extension World {
}
let callsite = Callsite(file: file, line: line)
let closure = behavior.spec
let group = ExampleGroup(description: behavior.name, flags: flags)
let group = ExampleGroup(description: behavior.name, order: Order.defined, flags: flags)
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: I’d use type inference here: .defined.

@jwfriese
Copy link
Contributor Author

jwfriese commented Feb 1, 2018

Thanks for the review and suggestions @NachoSoto 👍

@Thiryn
Copy link

Thiryn commented Jul 30, 2018

Just a small up to know if this feature is still considered or not.

@jwfriese
Copy link
Contributor Author

I'd like to know too. I have a few pull requests waiting to be merged.

@VojtaStavik
Copy link
Contributor

+1 for merging this 👍

@ArtemLyksa
Copy link

+1 to merge this. Should be really helpful.

#else
let d: Int = numericCast(arc4random_uniform(numericCast(unshuffledCount)))
#endif
let i = randomized.index(firstUnshuffled, offsetBy: d)

Choose a reason for hiding this comment

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

Identifier Name Violation: Variable name should be between 3 and 40 characters long: 'i' (identifier_name)

srandom(UInt32(time(nil)))
let d: Int = Int(random() % (unshuffledCount + 1))
#else
let d: Int = numericCast(arc4random_uniform(numericCast(unshuffledCount)))

Choose a reason for hiding this comment

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

Identifier Name Violation: Variable name should be between 3 and 40 characters long: 'd' (identifier_name)

for (firstUnshuffled, unshuffledCount) in zip(randomized.indices, stride(from: randomized.count, to: 1, by: -1)) {
#if os(Linux)
srandom(UInt32(time(nil)))
let d: Int = Int(random() % (unshuffledCount + 1))

Choose a reason for hiding this comment

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

Identifier Name Violation: Variable name should be between 3 and 40 characters long: 'd' (identifier_name)


private var randomizedExamples: [Example] {
var randomized = Array(groupUnits)
for (firstUnshuffled, unshuffledCount) in zip(randomized.indices, stride(from: randomized.count, to: 1, by: -1)) {

Choose a reason for hiding this comment

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

Line Length Violation: Line should be 120 characters or less: currently 122 characters (line_length)

@jwfriese
Copy link
Contributor Author

Since it looks like this feature is still in demand, I am in the process of updating this branch since it's a year old and had a couple merge conflicts. I currently have the tests all passing locally on my Mac. It's running through CI now. I'll fix any breakages that might find.

@cmillani
Copy link

+1 to merge this, would love to be able to use this 😃

@jradivojsa
Copy link

I also believe this PR would benefit all of us greatly. To not include such a magnificent feature is nothing short of a sin.
pls murg 👍

@cbauer10
Copy link

Any movement on this?

@ashikahmad
Copy link

Badly missing ordering. Looking forward to see this merged 🤩

@ValeFCF
Copy link

ValeFCF commented Sep 19, 2019

What is the status for this?

@jwfriese
Copy link
Contributor Author

jwfriese commented Oct 8, 2019

I have updated this PR now, resolving merge conflicts, and ensuring we still have a clean local rake run. I'll address any check failures as necessary. @ikesyo please let me know if there's anything else you need me to address in order to get this code merged.

@ZevEisenberg
Copy link

+1 from the future. If you're still in the past, maybe stay there for a bit 😅

srandom(UInt32(time(nil)))
let offset: Int = Int(random() % (unshuffledCount + 1))
#else
let offset: Int = numericCast(arc4random_uniform(numericCast(unshuffledCount)))

Choose a reason for hiding this comment

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

Since Swift 4.2, perhaps this could be replaced with the newer randomness API?

Base automatically changed from master to main March 2, 2021 13:54
@jessesquires
Copy link
Member

Hey there! 👋🏼

I'm a new maintainer for this project and I'm trying to get the next release out ASAP and also clean up old issues and old PRs. I'm closing all PRs older than 1-2 years, but that doesn't mean these changes are necessarily being rejected. If you are still interested in contributing these changes, please submit a new PR branching from the top of main.

We appreciate your work here and acknowledge the time and effort you put in to contribute! You're awesome. 💯 However, we are all volunteers here with limited capacity working for free. Unfortunately, that means we must close out stale issues and PRs to move the project forward in a reasonable way.

We apologize for the inconvenience and appreciate your understanding! 😎 ✌🏼

@younata
Copy link
Member

younata commented Jun 30, 2023

@jwfriese Hey, I'm coming back to this. I'm curious what you think about changing Quick's default behavior so that it always attempts to execute tests in the order they're defined in (as you've noted, XCTest will ignore that attempt whenever it desires). Additionally, we'll give the guidance that developers should enable "Randomize Execution Order" in the xctestplan when they want to randomize execution order.

This is less configurable than what this PR specifies, but I think overall what I just outlined will be better/easier for people to understand.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Allow tests to have a specific order Provide option to randomize test order