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

Filter by complex query #283

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion src/main/java/com/askimed/nf/test/commands/RunTestsCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,11 @@ public class RunTestsCommand extends AbstractCommand {
"--tag" }, split = ",", description = "Execute only tests with this tag", required = false, showDefaultValue = Visibility.ALWAYS)
private List<String> tags = new Vector<String>();

@Option(names = {
"--query" }, description = "Execute only tests that match this expression", required = false)
private String query = null;


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

@Override
Expand Down Expand Up @@ -254,7 +259,13 @@ public Integer execute() throws Exception {
environment.setPluginManager(manager);

TestSuiteResolver testSuiteResolver = new TestSuiteResolver(environment);
List<ITestSuite> testSuits = testSuiteResolver.parse(scripts, new TagQuery(tags));

TagQuery tagQuery = new TagQuery(tags);
if (query != null) {
tagQuery = new TagQueryExpression(query);
}

List<ITestSuite> testSuits = testSuiteResolver.parse(scripts, tagQuery);

testSuits.sort(TestSuiteSorter.getDefault());
if (shard != null && !testSuits.isEmpty()) {
Expand Down
31 changes: 27 additions & 4 deletions src/main/java/com/askimed/nf/test/core/TagQuery.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,36 @@ public boolean matches(ITaggable taggable) {
return true;
}

if (tags.contains(taggable.getName().toLowerCase())) {
// Separate positive and negative tags
List<String> positiveTags = new Vector<String>();
List<String> negativeTags = new Vector<String>();

for (String tag : tags) {
if (tag.startsWith("!")) {
negativeTags.add(tag.substring(1).toLowerCase()); // Remove '!' prefix for negative tags
} else {
positiveTags.add(tag.toLowerCase());
}
}

// Check for negative matches
if (negativeTags.contains(taggable.getName().toLowerCase())) {
return false;
}

for (String tag : taggable.getTags()) {
if (negativeTags.contains(tag.toLowerCase())) {
return false;
}
}

// Check for positive matches
if (positiveTags.contains(taggable.getName().toLowerCase())) {
return true;
}

for (String tag : taggable.getTags()) {
if (tags.contains(tag.toLowerCase())) {
if (positiveTags.contains(tag.toLowerCase())) {
return true;
}
}
Expand All @@ -48,12 +72,11 @@ public boolean matches(ITaggable taggable) {
return matches(taggable.getParent());
}

return false;
return !negativeTags.isEmpty();
}

@Override
public String toString() {
return tags.toString();
}

}
85 changes: 85 additions & 0 deletions src/main/java/com/askimed/nf/test/core/TagQueryExpression.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package com.askimed.nf.test.core;

import groovy.lang.Binding;
import groovy.lang.GroovyShell;
import groovy.util.Eval;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class TagQueryExpression extends TagQuery {

private String query;

public TagQueryExpression(String query) {
this.query = query;
}

public boolean matches(ITaggable taggable) {
if (query == null || query.trim().isEmpty()) {
return true;
}

Binding binding = createBindingContext(taggable);

try {
GroovyShell shell = new GroovyShell(binding);
return (Boolean) shell.evaluate(query); } catch (Exception e) {
throw new IllegalArgumentException("Invalid query: " + query, e);
}
}

private Binding createBindingContext(ITaggable taggable) {
Map<String, Boolean> tagMap = new HashMap<>();
Binding binding = new groovy.lang.Binding();

// Add tags from the current taggable
taggable.getTags().forEach(tag -> {
tagMap.put(tag.toLowerCase(), true);
});
tagMap.put(taggable.getName(), true);

// Add parent tags recursively
ITaggable parent = taggable.getParent();
while (parent != null) {
parent.getTags().forEach(tag -> {
tagMap.put(tag.toLowerCase(), true);
});
tagMap.put(parent.getName(), true);
parent = parent.getParent();
}

binding.setVariable("tags", new DefaultTagMap(tagMap)); // Map for key-based access

if (taggable instanceof AbstractTest) {
binding.setVariable("test", taggable.getName());
binding.setVariable("name", taggable.getParent().getName());
} else {
binding.setVariable("test", "");
binding.setVariable("name", "");
}


return binding;
}

@Override
public String toString() {
return query;
}


// Custom map to handle missing keys
private static class DefaultTagMap extends HashMap<String, Boolean> {
public DefaultTagMap(Map<String, Boolean> map) {
super(map);
}

@Override
public Boolean get(Object key) {
return super.getOrDefault(key, false);
}
}
}
111 changes: 93 additions & 18 deletions src/test/java/com/askimed/nf/test/core/TestSuiteResolverTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,30 @@ public void executeTestByName() throws Throwable {

}

@Test
public void executeTestByTestNameQuery() throws Throwable {

TagQuery query = new TagQueryExpression("test == 'test 1'");
List<String> tests = collectTests(query);
Assertions.assertEquals(1, tests.size());
Assertions.assertTrue(tests.contains("test 1"));
Assertions.assertFalse(tests.contains("test 2"));
Assertions.assertFalse(tests.contains("test 3"));

}

@Test
public void executeTestByNameQuery() throws Throwable {

TagQuery query = new TagQueryExpression("name == 'suite 1'");
List<String> tests = collectTests(query);
Assertions.assertEquals(2, tests.size());
Assertions.assertTrue(tests.contains("test 1"));
Assertions.assertTrue(tests.contains("test 2"));
Assertions.assertFalse(tests.contains("test 3"));

}

@Test
public void executeTestByWrongHash() throws Throwable {
List<File> scripts = new Vector<File>();
Expand Down Expand Up @@ -79,6 +103,13 @@ public void executeTestSuiteByName() throws Throwable {
Assertions.assertTrue(tests.contains("test 1"));
Assertions.assertTrue(tests.contains("test 2"));
}
{
TagQuery query = new TagQueryExpression("tags['suite 1']");
List<String> tests = collectTests(query);
Assertions.assertEquals(2, tests.size());
Assertions.assertTrue(tests.contains("test 1"));
Assertions.assertTrue(tests.contains("test 2"));
}
}

@Test
Expand All @@ -95,36 +126,80 @@ public void executeTestsByTag() throws Throwable {
Assertions.assertEquals(1, tests.size());
Assertions.assertTrue(tests.contains("test 1"));
}
{
TagQuery query = new TagQueryExpression("tags['tag2']");
List<String> tests = collectTests(query);
Assertions.assertEquals(1, tests.size());
Assertions.assertTrue(tests.contains("test 1"));
}
}

@Test
public void executeTestsByTagAcrossSuites() throws Throwable {

TagQuery query = new TagQuery("tag5");
List<String> tests = collectTests(query);
Assertions.assertEquals(2, tests.size());
Assertions.assertTrue(tests.contains("test 2"));
Assertions.assertTrue(tests.contains("test 3"));
{
TagQuery query = new TagQuery("tag5");
List<String> tests = collectTests(query);
Assertions.assertEquals(2, tests.size());
Assertions.assertTrue(tests.contains("test 2"));
Assertions.assertTrue(tests.contains("test 3"));
}
{
TagQuery query = new TagQueryExpression("tags['tag5']");
List<String> tests = collectTests(query);
Assertions.assertEquals(2, tests.size());
Assertions.assertTrue(tests.contains("test 2"));
Assertions.assertTrue(tests.contains("test 3"));
}
{
TagQuery query = new TagQuery("!tag5");
List<String> tests = collectTests(query);
Assertions.assertEquals(1, tests.size());
Assertions.assertTrue(!tests.contains("test 2"));
Assertions.assertTrue(!tests.contains("test 3"));
}
{
TagQuery query = new TagQueryExpression("!tags['tag5']");
List<String> tests = collectTests(query);
Assertions.assertEquals(1, tests.size());
Assertions.assertTrue(!tests.contains("test 2"));
Assertions.assertTrue(!tests.contains("test 3"));
}
}

@Test
public void executeTestsBySuiteTag() throws Throwable {

TagQuery query = new TagQuery("tag1");
List<String> tests = collectTests(query);
Assertions.assertEquals(2, tests.size());
Assertions.assertTrue(tests.contains("test 1"));
Assertions.assertTrue(tests.contains("test 2"));
{
TagQuery query = new TagQuery("tag1");
List<String> tests = collectTests(query);
Assertions.assertEquals(2, tests.size());
Assertions.assertTrue(tests.contains("test 1"));
Assertions.assertTrue(tests.contains("test 2"));
}
{
TagQuery query = new TagQueryExpression("tags['tag1']");
List<String> tests = collectTests(query);
Assertions.assertEquals(2, tests.size());
Assertions.assertTrue(tests.contains("test 1"));
Assertions.assertTrue(tests.contains("test 2"));
}
}

@Test
public void executeTestsByMultipleTags() throws Throwable {

TagQuery query = new TagQuery("tag3", "tag4");
List<String> tests = collectTests(query);
Assertions.assertEquals(2, tests.size());
Assertions.assertTrue(tests.contains("test 1"));
Assertions.assertTrue(tests.contains("test 2"));
{
TagQuery query = new TagQuery("tag3", "tag4");
List<String> tests = collectTests(query);
Assertions.assertEquals(2, tests.size());
Assertions.assertTrue(tests.contains("test 1"));
Assertions.assertTrue(tests.contains("test 2"));
}
{
TagQueryExpression query = new TagQueryExpression("tags['tag3'] || tags['tag4']");
List<String> tests = collectTests(query);
Assertions.assertEquals(2, tests.size());
Assertions.assertTrue(tests.contains("test 1"));
Assertions.assertTrue(tests.contains("test 2"));
}
}

protected List<String> collectTests(TagQuery query) throws Throwable {
Expand Down
Loading