Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main'
Browse files Browse the repository at this point in the history
  • Loading branch information
lukfor committed May 24, 2024
2 parents e18f751 + a7032d0 commit 826b010
Show file tree
Hide file tree
Showing 12 changed files with 238 additions and 16 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ test_mock.nf
site
/tests
/.nf-test.log
/temp
5 changes: 5 additions & 0 deletions docs/docs/cli/test.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ Writes test results in csv file.
By default,nf-test automatically stores a new snapshot. When CI mode is activated, nf-test will fail the test instead of storing the snapshot automatically.


### `--filter <types>`

Filter test cases by specified types (e.g., module, pipeline, workflow or function). Multiple types can be separated by commas.


### Optimizing Test Execution

#### `--related-tests <files>`
Expand Down
18 changes: 14 additions & 4 deletions docs/tutorials/github-actions.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ jobs:
sudo mv nf-test /usr/local/bin/
- name: Run Tests
run: nf-test test
run: nf-test test --ci
```
### Explanation:
Expand All @@ -57,7 +57,7 @@ jobs:
2. **Set up JDK 11**: Uses the `actions/setup-java@v2` action to set up Java Development Kit version 11.
3. **Setup Nextflow**: Uses the `nf-core/setup-nextflow@v1` action to install the latest-edge version of Nextflow.
4. **Install nf-test**: Downloads and installs nf-test.
5. **Run Tests**: Runs nf-test without sharding.
5. **Run Tests**: Runs nf-test with the `--ci` flag. This activates the CI mode. Instead of automatically storing a new snapshot as per usual, it will now fail the test if no reference snapshot is available. This enables tests to fail when a snapshot file was forgotten to be committed.

## Step 2: Extending to Use Sharding

Expand Down Expand Up @@ -95,7 +95,7 @@ jobs:
sudo mv nf-test /usr/local/bin/
- name: Run Tests (Shard ${{ matrix.shard }}/${{ strategy.job-total }})
run: nf-test test --shard ${{ matrix.shard }}/${{ strategy.job-total }}
run: nf-test test --ci --shard ${{ matrix.shard }}/${{ strategy.job-total }}
```

### Explanation of Sharding:
Expand Down Expand Up @@ -141,7 +141,7 @@ jobs:
sudo mv nf-test /usr/local/bin/
- name: Run Tests (Shard ${{ matrix.shard }}/${{ strategy.job-total }})
run: nf-test test --shard ${{ matrix.shard }}/${{ strategy.job-total }} --changed-since HEAD^
run: nf-test test --ci --shard ${{ matrix.shard }}/${{ strategy.job-total }} --changed-since HEAD^
```

### Explanation of Changes:
Expand Down Expand Up @@ -172,6 +172,16 @@ config {

This configuration ensures that critical changes always result in a comprehensive validation of the pipeline, providing additional confidence in your CI process.

## Step 5: Additional useful Options

The `--filter` flag allows you to selectively run test cases based on their specified types. For example, you can filter tests by module, pipeline, workflow, or function. This is particularly useful when you have a large suite of tests and need to focus on specific areas of functionality. By separating multiple types with commas, you can run a customized subset of tests that match the exact criteria you're interested in, thereby saving time and resources.

The `--related-tests` flag enables you to identify and execute all tests related to the provided `.nf` or `nf.test` files. This is ideal for scenarios where you have made changes to specific files and want to ensure that only the relevant tests are run. You can provide multiple files by separating them with spaces, which makes it easy to manage and test multiple changes at once, ensuring thorough validation of your updates.

When the `--follow-dependencies` flag is set, the nf-test tool will automatically traverse and execute all tests for dependencies related to the files specified with the `--related-tests` flag. This ensures that any interdependent components are also tested, providing comprehensive coverage. This option is particularly useful for complex projects with multiple dependencies, as it bypasses the firewall calculation process and guarantees that all necessary tests are executed.

The `--changed-until` flag allows you to run tests based on changes made up until a specified commit hash or branch name. By default, this parameter uses `HEAD`, but you can specify any commit or branch to target the changes made up to that point. This is particularly useful for validating changes over a specific range of commits, ensuring that all modifications within that period are tested comprehensively.

## Summary

1. **Without Sharding**: A straightforward setup where all tests run in a single job.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import com.askimed.nf.test.lang.dependencies.Coverage;
import com.askimed.nf.test.lang.dependencies.DependencyExporter;
import com.askimed.nf.test.lang.dependencies.DependencyResolver;
import com.askimed.nf.test.lang.dependencies.IMetaFile;
import com.askimed.nf.test.util.AnsiText;
import com.askimed.nf.test.util.GitCommand;
import org.slf4j.Logger;
Expand Down Expand Up @@ -80,6 +81,9 @@ public class RunTestsCommand extends AbstractCommand {
@Option(names = { "--follow-dependencies", "--followDependencies"}, description = "Follows all dependencies when related-tests is set.", required = false, showDefaultValue = Visibility.ALWAYS)
private boolean followDependencies = false;

@Option(names = { "--filter" }, description = "Filter test cases by specified types (e.g., module, pipeline, workflow or function). Multiple types can be separated by commas.", required = false, showDefaultValue = Visibility.ALWAYS)
private String dependencies = "all";

@Option(names = { "--only-changed", "--onlyChanged"}, description = "Runs tests only for those files which are modified in the current git repository", required = false, showDefaultValue = Visibility.ALWAYS)
private boolean onlyChanged = false;

Expand Down Expand Up @@ -188,6 +192,7 @@ public Integer execute() throws Exception {
File baseDir = new File(new File("").getAbsolutePath());
DependencyResolver resolver = new DependencyResolver(baseDir);
resolver.setFollowingDependencies(followDependencies);
resolver.setTargets(IMetaFile.TargetType.parse(dependencies));

if (onlyChanged || changedSince != null) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ public void addFile(IMetaFile metaFile) {
public void connectDependencies(){
for (Node node: nodes.values()) {
for (String dependency: node.getMetaFile().getDependencies()) {
//addDependency(dependency, node.getFilename());
addDependency(dependency, node.getFilename());
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ public class DependencyResolver {

private boolean followingDependencies = false;

private Set<IMetaFile.TargetType> targets = new HashSet<IMetaFile.TargetType>();

private static Logger log = LoggerFactory.getLogger(DependencyResolver.class);

public DependencyResolver(File baseDir) {
Expand All @@ -38,11 +40,15 @@ public void setFollowingDependencies(boolean followingDependencies) {
this.followingDependencies = followingDependencies;
}

public void setTargets(Set<IMetaFile.TargetType> targets) {
this.targets = targets;
}

public List<File> findAllTests() throws Exception {

List<File> results = new Vector<File>();
for (IMetaFile metaFile: graph.getFiles()) {
if (metaFile.getType() == IMetaFile.MetaFileType.TEST_FILE) {
if (metaFile.getType() == IMetaFile.MetaFileType.TEST_FILE && acceptMetaFile(metaFile)) {
results.add(new File(metaFile.getFilename()));
}
}
Expand All @@ -61,7 +67,7 @@ public List<File> findTestsByFiles(List<File> files) throws Exception {

List<File> results = new Vector<File>();
for (IMetaFile metaFile: graph.getFiles()) {
if (metaFile.getType() == IMetaFile.MetaFileType.TEST_FILE) {
if (metaFile.getType() == IMetaFile.MetaFileType.TEST_FILE && acceptMetaFile(metaFile)) {
File file = new File(metaFile.getFilename());
TestFilePattern matchedPattern = matches(file.toPath(), patterns);
if (matchedPattern != null) {
Expand Down Expand Up @@ -102,15 +108,15 @@ public List<File> findRelatedTestsByFiles(File ... files) throws Exception {

long time0 = System.currentTimeMillis();
for (File file: files) {
results.addAll(findRelatedTestsByFile(file.getAbsoluteFile(), followingDependencies));
results.addAll(findRelatedTestsByFile(file.getAbsoluteFile()));
}
long time1 = System.currentTimeMillis();
log.info("Found {} tests for file {} in {} sec", results.size(), files, (time1 - time0) / 1000.0);

return new Vector<File>(results);
}

private Set<File> findRelatedTestsByFile(File file, boolean followingDependencies) throws Exception {
private Set<File> findRelatedTestsByFile(File file) throws Exception {

Set<File> results = new HashSet<File>();

Expand All @@ -123,7 +129,9 @@ private Set<File> findRelatedTestsByFile(File file, boolean followingDependencie

// the file is a test file
if (metaFile.getType() == IMetaFile.MetaFileType.TEST_FILE){
results.add(new File(metaFile.getFilename()));
if (acceptMetaFile(metaFile)) {
results.add(new File(metaFile.getFilename()));
}
return results;
}

Expand All @@ -134,21 +142,24 @@ private Set<File> findRelatedTestsByFile(File file, boolean followingDependencie

if (dependency.getType() == IMetaFile.MetaFileType.TEST_FILE) {
// is a test file --> return
results.add(dependencyFile);
if (acceptMetaFile(dependency)) {
results.add(dependencyFile);
}
} else {
// if a source file
DependencyGraph.Node node = graph.getNode(dependency.getFilename());
//TODO: add && !followingDependencies
if (node.hasDependencyOfType(IMetaFile.MetaFileType.TEST_FILE) && !followingDependencies) {
//has a test --> add all test and then stop
for (DependencyGraph.Node dependencyOfDependency: node.getDependencies()) {
if (dependencyOfDependency.getMetaFile().getType() == IMetaFile.MetaFileType.TEST_FILE) {
results.add(new File(dependencyOfDependency.getFilename()));
if (acceptMetaFile(dependencyOfDependency.getMetaFile())) {
results.add(new File(dependencyOfDependency.getFilename()));
}
}
}
} else {
//has no tests --> find related tests in a recursive way
results.addAll(findRelatedTestsByFile(dependencyFile, followingDependencies));
results.addAll(findRelatedTestsByFile(dependencyFile));
}
}
}
Expand Down Expand Up @@ -266,5 +277,11 @@ public boolean matches2(Path path, List<PathMatcher> ignorePatterns) {
return false;
}

public boolean acceptMetaFile(IMetaFile file) {
if (targets == null || targets.isEmpty()) {
return true;
}
return targets.contains(file.getTarget());
}

}
21 changes: 21 additions & 0 deletions src/main/java/com/askimed/nf/test/lang/dependencies/IMetaFile.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.askimed.nf.test.lang.dependencies;

import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

public interface IMetaFile {
Expand All @@ -9,6 +10,8 @@ public interface IMetaFile {

public MetaFileType getType();

public TargetType getTarget();

public String getFilename();

public void parseDependencies() throws IOException;
Expand All @@ -17,4 +20,22 @@ public static enum MetaFileType{
SOURCE_FILE, TEST_FILE, SNAPSHOT_FILE
}

public static enum TargetType{
PROCESS, WORKFLOW, PIPELINE, FUNCTION, UNDEFINED;

public static Set<TargetType> parse(String targets) {
Set<TargetType> result = new HashSet<TargetType>();
String cleaned = targets.trim().toUpperCase();
if (cleaned.isEmpty() || cleaned.equalsIgnoreCase("ALL")) {
return result;
}
for (String target: cleaned.split(",")) {
String cleanedTarget = target.trim();
result.add(TargetType.valueOf(cleanedTarget));
}
return result;
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,9 @@ public Set<String> getDependencies() {
return dependencies;
}

@Override
public TargetType getTarget() {
return TargetType.UNDEFINED;
}

}
48 changes: 48 additions & 0 deletions src/main/java/com/askimed/nf/test/lang/dependencies/TestFile.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
package com.askimed.nf.test.lang.dependencies;

import com.askimed.nf.test.util.FileUtil;
import org.apache.tools.ant.taskdefs.Tar;

import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

Expand All @@ -19,13 +22,54 @@ public class TestFile implements IMetaFile {

private Set<String> dependencies = new HashSet<String>();

private TargetType target = TargetType.UNDEFINED;

public TestFile(File baseDir, File file) {
this.baseDir = baseDir;
this.file = file;
}

public void parseDependencies() throws IOException {
String script = FileUtil.readFileAsString(file);
target = parseType(script);
parseDependencies(script);
}

private TargetType parseType(String script) {

TargetType type = parseType(script, "nextflow_pipeline", TargetType.PIPELINE);
if (type != TargetType.UNDEFINED) {
return type;
}
type = parseType(script, "nextflow_process", TargetType.PROCESS);
if (type != TargetType.UNDEFINED) {
return type;
}
type = parseType(script, "nextflow_workflow", TargetType.WORKFLOW);
if (type != TargetType.UNDEFINED) {
return type;
}
type = parseType(script, "nextflow_function", TargetType.FUNCTION);
return type;

}

private TargetType parseType(String script, String type, TargetType targetType) {

String patternType = "(?i)^\\s*" + type + "\\s*(.+)(\\s*\\{|\\{)";

Pattern r = Pattern.compile(patternType, Pattern.MULTILINE);

Matcher m = r.matcher(script);
if (m.find()) {
return targetType;
} else {
return TargetType.UNDEFINED;
}
}

private void parseDependencies(String script) throws IOException {

String regex = "(?i)script\\s+\"(.+)\"";

Pattern pattern = Pattern.compile(regex);
Expand Down Expand Up @@ -73,4 +117,8 @@ public Set<String> getDependencies() {
return dependencies;
}

@Override
public TargetType getTarget() {
return target;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,11 @@ public static Set<String> getDependencies(File file, String content) {

}

@Override
public TargetType getTarget() {
return TargetType.UNDEFINED;
}

protected static Path resolve(File file, String dependency) {
if (dependency.startsWith("./") || dependency.startsWith("../")) {
return Paths.get(file.getParentFile().getAbsolutePath()).resolve(dependency);
Expand Down
Loading

0 comments on commit 826b010

Please sign in to comment.