Skip to content

Commit

Permalink
Add ability to filter tests from the command line (#235)
Browse files Browse the repository at this point in the history
Summary:
Addresses part of #224

Fairly intuitive filtering implementation as well as integration with our internal CLI library.

![image](https://user-images.githubusercontent.com/5252755/74194208-b5680380-4c0d-11ea-8e74-b99e5ee5a842.png)
![image](https://user-images.githubusercontent.com/5252755/74194244-c1ec5c00-4c0d-11ea-9406-6c6aa09ee6ad.png)
Pull Request resolved: #235

Differential Revision: D19827404

Pulled By: bandersongit

fbshipit-source-id: bca7c71f33af48e7cf5dc6a6e1ea1910be5da15d
  • Loading branch information
bandersongit authored and facebook-github-bot committed Feb 11, 2020
1 parent d99df20 commit 70bc30c
Show file tree
Hide file tree
Showing 11 changed files with 456 additions and 69 deletions.
1 change: 1 addition & 0 deletions src/rely/RelyAPI.rei
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ module RunConfig: {
let withReporters: (list(reporter), t) => t;
let internal_do_not_use_get_time: (unit => Time.t, t) => t;
let ciMode: (bool, t) => t;
let withTestNamePattern: (option(string), t) => t;
};

module MatcherUtils: {
Expand Down
66 changes: 66 additions & 0 deletions src/rely/RelyCLI.re
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/;

let cli = (argv, testSuites) => {
CLI.(
program("Rely")
|> description("A jest inspired native Reason testing framework")
|> option(
"--updateSnapshots",
~aliases=["-u"],
"Updates snapshots to match what is currently generated by the program",
Optional(Bool),
)
|> option(
"--onlyPrintDetailsForFailedSuites",
"Prints output only for test suites that fail",
Optional(Bool),
)
|> option(
"--ci",
~aliases=["-ci"],
"Runs Rely in CI mode (errors on usage of testOnly and describeOnly)",
Optional(Bool),
)
|> argument(
"testNamePattern",
"Only run tests matching the passed in pattern",
Optional(String),
)
|> action(({options, args}) => {
let shouldUpdateSnapshots = options.bool("--updateSnapshots");
let ci = options.bool("--ci");
let onlyPrintDetailsForFailedSuites =
options.bool("--onlyPrintDetailsForFailedSuites");

let config =
RunConfig.(
initialize()
|> updateSnapshots(shouldUpdateSnapshots)
|> ciMode(ci)
|> withTestNamePattern(
args.optionalString("testNamePattern"),
)
|> withReporters([
Custom(
TerminalReporter.createTerminalReporter(
~onlyPrintDetailsForFailedSuites,
{
printEndline: print_endline,
printNewline: print_newline,
printString: print_string,
flush,
},
),
),
])
);
TestSuiteRunner.run(config, testSuites);
})
|> parseAndRun(argv)
);
};
7 changes: 7 additions & 0 deletions src/rely/RunConfig.re
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type reporter =
type t = {
updateSnapshots: bool,
onTestFrameworkFailure: unit => unit,
testNamePattern: option(string),
reporters: list(reporter),
getTime: unit => Time.t,
ci: bool,
Expand All @@ -30,6 +31,7 @@ let initialize = () => {
reporters: [Default],
getTime: Clock.getTime,
ci: false,
testNamePattern: None,
};

let updateSnapshots: (bool, t) => t =
Expand All @@ -47,3 +49,8 @@ let onTestFrameworkFailure = (onTestFrameworkFailure, config) => {
...config,
onTestFrameworkFailure,
};

let withTestNamePattern = (testNamePattern, config) => {
...config,
testNamePattern,
};
4 changes: 2 additions & 2 deletions src/rely/TestFramework.re
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ type combineResult = {
let combine = libraries => {
let testLibrary = List.flatten(libraries);
let run = config => TestSuiteRunner.run(config, testLibrary);
let cli = () => TestSuiteRunner.cli(testLibrary);
let cli = () => RelyCLI.cli(Sys.argv, testLibrary);

{testLibrary, run, cli};
};
Expand Down Expand Up @@ -248,7 +248,7 @@ module MakeInternal =
let run = (config: RunConfig.t) =>
TestSuiteRunner.run(config, testSuites^);

let cli = () => TestSuiteRunner.cli(testSuites^);
let cli = () => RelyCLI.cli(Sys.argv, testSuites^);

let toLibrary = () => testSuites^;
};
Expand Down
71 changes: 71 additions & 0 deletions src/rely/TestSuiteFilter.re
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/;

let filterTestsByName =
(tests: list(TestSuite.test('ext, 'env)), nameFilter) => {
tests
|> List.filter((test: TestSuite.test('ext, 'env)) =>
nameFilter(test.name)
);
};

let rec filterDescribeByName =
(describeRecord: TestSuite.describeRecord('a, 'b), nameFilter) => {
let {TestSuite.name, tests, describes, mode} = describeRecord;

if (nameFilter(name)) {
Some(describeRecord);
} else {
let tests = filterTestsByName(tests, nameFilter);
let describes = filterDescribesByName(describes, nameFilter);

switch (tests, describes) {
| ([], []) => None
| _ =>
Some({name, tests, describes, mode}: TestSuite.describeRecord('a, 'b))
};
};
}
and filterDescribesByName = (describes, nameFilter) => {
describes
|> List.map(describe => filterDescribeByName(describe, nameFilter))
|> List.fold_left(
(acc, optDescribe) =>
switch (optDescribe) {
| Some(describe) => [describe, ...acc]
| None => acc
},
[],
);
};

let filterSuiteByName = (testSuite, nameFilter) => {
let TestSuite.TestSuite(describeRecord, extensionFn, lifeCycle, context) = testSuite;
switch (filterDescribeByName(describeRecord, nameFilter)) {
| Some(record) =>
Some(TestSuite.TestSuite(record, extensionFn, lifeCycle, context))
| None => None
};
};

let filterTestSuitesByName = (testSuites: list(TestSuite.t), nameFilter) => {
testSuites
|> List.fold_left(
(acc, suite) => {
switch (filterSuiteByName(suite, nameFilter)) {
| Some(suite) => [suite, ...acc]
| None => acc
}
},
[],
);
};

let filterTestSuitesByRegex = (testSuites, regex) => {
let filterFn = name => Re.Pcre.pmatch(regex, name);
filterTestSuitesByName(testSuites, filterFn);
};
90 changes: 43 additions & 47 deletions src/rely/TestSuiteRunner.re
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ open Describe;
open Util;
open RunConfig;
open Reporter;
open CommonOption.Infix;

exception PendingTestException(string);
exception InvalidInCIMode(string);

Expand All @@ -26,6 +28,8 @@ module SnapshotModuleSet =
module type TestSuiteRunnerConfig = {
let getTime: unit => Time.t;
let updateSnapshots: bool;
let removeUnusedSnapshots: bool;
let testNamePattern: option(string);
let maxNumStackFrames: int;
let ci: bool;
let reporters: list(Reporter.t);
Expand Down Expand Up @@ -320,12 +324,15 @@ module Make = (Config: TestSuiteRunnerConfig) => {
snapshotModule;
});

let _ =
List.iter(
(module SnapshotModule: Snapshot.Sig) =>
SnapshotModule.removeUnusedSnapshots(),
uniqueModules,
);
if (Config.removeUnusedSnapshots) {
let _ =
List.iter(
(module SnapshotModule: Snapshot.Sig) =>
SnapshotModule.removeUnusedSnapshots(),
uniqueModules,
);
();
};

let aggregateSnapshotResult =
List.fold_left(
Expand Down Expand Up @@ -371,7 +378,12 @@ module Make = (Config: TestSuiteRunnerConfig) => {
| TestSuite({name}, _, _, _) => {name: name}
}
);
notifyReporters(r => r.onRunStart({testSuites: reporterTestSuites}));
notifyReporters(r =>
r.onRunStart({
testSuites: reporterTestSuites,
testNamePattern: Config.testNamePattern,
})
);
let result =
testSuites
|> List.fold_left(
Expand Down Expand Up @@ -416,20 +428,36 @@ module Make = (Config: TestSuiteRunnerConfig) => {
};
};

let run = (config: RunConfig.t, testSuites) =>
let run = (runConfig: RunConfig.t, testSuites) =>
Util.withBacktrace(() => {
let testNamePattern = ref(None);
let testSuites =
switch (runConfig.testNamePattern) {
| Some(pattern) =>
testNamePattern.contents =
Some(String.concat("", ["\\", pattern, "\\i"]));
TestSuiteFilter.filterTestSuitesByRegex(
testSuites,
Re.Pcre.regexp(~flags=[`CASELESS], pattern),
);

| None => testSuites
};
module RunnerConfig = {
let getTime = config.getTime;
let getTime = runConfig.getTime;
let maxNumStackFrames = 3;
let updateSnapshots = config.updateSnapshots;
let ci = config.ci;
let updateSnapshots = runConfig.updateSnapshots;
let ci = runConfig.ci;
let removeUnusedSnapshots =
CommonOption.isNone(runConfig.testNamePattern);
let testNamePattern = testNamePattern.contents;
let reporters =
config.reporters
runConfig.reporters
|> List.map(reporter =>
switch (reporter) {
| RunConfig.Default =>
TerminalReporter.createTerminalReporter(
~getTime=config.getTime,
~getTime=runConfig.getTime,
{
printEndline: print_endline,
printNewline: print_newline,
Expand All @@ -441,41 +469,9 @@ let run = (config: RunConfig.t, testSuites) =>
| Custom(reporter) => reporter
}
);
let onTestFrameworkFailure = config.onTestFrameworkFailure;
let onTestFrameworkFailure = runConfig.onTestFrameworkFailure;
};
module Runner = Make(RunnerConfig);

Runner.runTestSuites(testSuites);
});

let cli = testSuites => {
let hasFlag = flag =>
Array.length(Sys.argv) >= 2 && Array.exists(arg => arg == flag, Sys.argv);

let shouldUpdateSnapshots = hasFlag("-u");

let ci = hasFlag("--ci");

let onlyPrintDetailsForFailedSuites =
hasFlag("--onlyPrintDetailsForFailedSuites");

let config =
RunConfig.(
initialize()
|> updateSnapshots(shouldUpdateSnapshots)
|> ciMode(ci)
|> withReporters([
Custom(
TerminalReporter.createTerminalReporter(
~onlyPrintDetailsForFailedSuites,
{
printEndline: print_endline,
printNewline: print_newline,
printString: print_string,
flush,
},
),
),
])
);
run(config, testSuites);
};
2 changes: 1 addition & 1 deletion src/rely/dune
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
library
(name RelyInternal)
(public_name rely.internal)
(libraries pastel.lib file-context-printer.lib unix junit re)
(libraries pastel.lib file-context-printer.lib unix junit re cli.lib)
(modules (:standard \ Rely))
(flags (:standard -w -30-9))
)
Expand Down
3 changes: 2 additions & 1 deletion src/rely/reporters/Reporter.re
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ type testSuite = {
};

type relyRunInfo = {
testSuites: list(testSuite)
testSuites: list(testSuite),
testNamePattern: option(string),
};

type aggregatedResult = AggregatedResult.t;
Expand Down
Loading

0 comments on commit 70bc30c

Please sign in to comment.