From c98d58d38d2774e826b5dff73d66aaa081592c77 Mon Sep 17 00:00:00 2001
From: Maksim Stepanov <17935127+delatrie@users.noreply.github.com>
Date: Thu, 21 Sep 2023 18:34:46 +0700
Subject: [PATCH 01/13] Add json schemas for allureConfig and testplan
---
Allure.Features/allureConfig.json | 1 +
Allure.NUnit.Examples/allureConfig.json | 1 +
Allure.NUnit/Schemas/allureConfig.schema.json | 22 ++++
.../Schemas/allureConfig.schema.json | 29 +++++
.../Schemas/testplan.schema.json | 22 ++++
Allure.SpecFlowPlugin.Tests/allureConfig.json | 1 +
.../Schemas/allureConfig.schema.json | 108 ++++++++++++++++++
Allure.XUnit.Examples/allureConfig.json | 1 +
Allure.XUnit/Schemas/allureConfig.schema.json | 33 ++++++
9 files changed, 218 insertions(+)
create mode 100644 Allure.NUnit/Schemas/allureConfig.schema.json
create mode 100644 Allure.Net.Commons/Schemas/allureConfig.schema.json
create mode 100644 Allure.Net.Commons/Schemas/testplan.schema.json
create mode 100644 Allure.SpecFlowPlugin/Schemas/allureConfig.schema.json
create mode 100644 Allure.XUnit/Schemas/allureConfig.schema.json
diff --git a/Allure.Features/allureConfig.json b/Allure.Features/allureConfig.json
index 7af5ca41..59af8ccf 100644
--- a/Allure.Features/allureConfig.json
+++ b/Allure.Features/allureConfig.json
@@ -1,4 +1,5 @@
{
+ "$schema": "https://raw.githubusercontent.com/allure-framework/allure-csharp/selective-run/Allure.SpecFlowPlugin/Schemas/allureConfig.schema.json",
"allure": {
"title": "5994A3F7-AF84-46AD-9393-000BB45553CC",
"directory": "allure-results",
diff --git a/Allure.NUnit.Examples/allureConfig.json b/Allure.NUnit.Examples/allureConfig.json
index 7c59a8f0..9f08d804 100644
--- a/Allure.NUnit.Examples/allureConfig.json
+++ b/Allure.NUnit.Examples/allureConfig.json
@@ -1,4 +1,5 @@
{
+ "$schema": "https://raw.githubusercontent.com/allure-framework/allure-csharp/selective-run/Allure.NUnit/Schemas/allureConfig.schema.json",
"allure": {
"directory": "allure-results",
"links": [
diff --git a/Allure.NUnit/Schemas/allureConfig.schema.json b/Allure.NUnit/Schemas/allureConfig.schema.json
new file mode 100644
index 00000000..9006399a
--- /dev/null
+++ b/Allure.NUnit/Schemas/allureConfig.schema.json
@@ -0,0 +1,22 @@
+{
+ "allOf": [
+ {
+ "$ref": "https://raw.githubusercontent.com/allure-framework/allure-csharp/selective-run/Allure.Net.Commons/Schemas/allureConfig.schema.json"
+ }
+ ],
+ "properties": {
+ "allure": {
+ "properties": {
+ "brokenTestData": {
+ "type": "array",
+ "description": "A list of exceptions type names. If any of them is thrown, the test is marked as broken. Otherwise, the test state is determined by the inner logic of allure-nunit.",
+ "items": {
+ "type": "string",
+ "description": "The full class name of an exception type.",
+ "examples": [ "System.Exception" ]
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Allure.Net.Commons/Schemas/allureConfig.schema.json b/Allure.Net.Commons/Schemas/allureConfig.schema.json
new file mode 100644
index 00000000..adffce16
--- /dev/null
+++ b/Allure.Net.Commons/Schemas/allureConfig.schema.json
@@ -0,0 +1,29 @@
+{
+ "type": "object",
+ "properties": {
+ "allure": {
+ "type": "object",
+ "properties": {
+ "title": {
+ "type": "string"
+ },
+ "directory": {
+ "type": "string",
+ "default": "allure-results",
+ "description": "The absolute or relative path to an output directory where allure result files will be written."
+ },
+ "links": {
+ "type": "array",
+ "description": "An array containing link patterns. Each pattern must contain a link type placeholder in the form of {link-type}.",
+ "examples": [
+ "https://github.com/allure-framework/allure-csharp/issues/{issue}",
+ "https://example.org/{tms}"
+ ],
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Allure.Net.Commons/Schemas/testplan.schema.json b/Allure.Net.Commons/Schemas/testplan.schema.json
new file mode 100644
index 00000000..3b9c82ed
--- /dev/null
+++ b/Allure.Net.Commons/Schemas/testplan.schema.json
@@ -0,0 +1,22 @@
+{
+ "type": "object",
+ "properties": {
+ "tests": {
+ "type": "array",
+ "description": "An array of selectors each specifying a test to run. All other tests will be skipped.",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The ID of a test (usually assigned by TestOps)."
+ },
+ "selector": {
+ "type": "string",
+ "description": "The fullName of a test."
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Allure.SpecFlowPlugin.Tests/allureConfig.json b/Allure.SpecFlowPlugin.Tests/allureConfig.json
index 97f5a0bd..fe335dcc 100644
--- a/Allure.SpecFlowPlugin.Tests/allureConfig.json
+++ b/Allure.SpecFlowPlugin.Tests/allureConfig.json
@@ -1,3 +1,4 @@
{
+ "$schema": "https://raw.githubusercontent.com/allure-framework/allure-csharp/selective-run/Allure.SpecFlowPlugin/Schemas/allureConfig.schema.json",
"allure": {}
}
\ No newline at end of file
diff --git a/Allure.SpecFlowPlugin/Schemas/allureConfig.schema.json b/Allure.SpecFlowPlugin/Schemas/allureConfig.schema.json
new file mode 100644
index 00000000..b412c901
--- /dev/null
+++ b/Allure.SpecFlowPlugin/Schemas/allureConfig.schema.json
@@ -0,0 +1,108 @@
+{
+ "allOf": [
+ {
+ "$ref": "https://raw.githubusercontent.com/allure-framework/allure-csharp/selective-run/Allure.Net.Commons/Schemas/allureConfig.schema.json"
+ }
+ ],
+ "properties": {
+ "specflow": {
+ "type": "object",
+ "properties": {
+ "stepArguments": {
+ "type": "object",
+ "properties": {
+ "convertToParameters": {
+ "anyOf": [
+ {
+ "type": "boolean"
+ },
+ {
+ "type": "string",
+ "enum": [ "true", "false" ]
+ }
+ ]
+ },
+ "paramNameRegex": {
+ "type": "string"
+ },
+ "paramValueRegex": {
+ "type": "string"
+ }
+ }
+ },
+ "grouping": {
+ "type": "object",
+ "properties": {
+ "suites": {
+ "type": "object",
+ "properties": {
+ "parentSuite": {
+ "type": "string"
+ },
+ "suite": {
+ "type": "string"
+ },
+ "subSuite": {
+ "type": "string"
+ }
+ }
+ },
+ "behaviors": {
+ "type": "object",
+ "properties": {
+ "epic": {
+ "type": "string"
+ },
+ "story": {
+ "type": "string"
+ }
+ }
+ },
+ "packages": {
+ "type": "object",
+ "properties": {
+ "package": {
+ "type": "string"
+ },
+ "testClass": {
+ "type": "string"
+ },
+ "testMethod": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ },
+ "labels": {
+ "type": "object",
+ "properties": {
+ "owner": {
+ "type": "string"
+ },
+ "severity": {
+ "type": "string"
+ },
+ "label": {
+ "type": "string"
+ }
+ }
+ },
+ "links": {
+ "type": "object",
+ "properties": {
+ "link": {
+ "type": "string"
+ },
+ "issue": {
+ "type": "string"
+ },
+ "tms": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Allure.XUnit.Examples/allureConfig.json b/Allure.XUnit.Examples/allureConfig.json
index c2ef3daf..bb780baf 100644
--- a/Allure.XUnit.Examples/allureConfig.json
+++ b/Allure.XUnit.Examples/allureConfig.json
@@ -1,4 +1,5 @@
{
+ "$schema": "https://raw.githubusercontent.com/allure-framework/allure-csharp/selective-run/Allure.XUnit/Schemas/allureConfig.schema.json",
"allure": {
"directory": "allure-results",
"links": [
diff --git a/Allure.XUnit/Schemas/allureConfig.schema.json b/Allure.XUnit/Schemas/allureConfig.schema.json
new file mode 100644
index 00000000..d5deb4f9
--- /dev/null
+++ b/Allure.XUnit/Schemas/allureConfig.schema.json
@@ -0,0 +1,33 @@
+{
+ "allOf": [
+ {
+ "$ref": "https://raw.githubusercontent.com/allure-framework/allure-csharp/selective-run/Allure.Net.Commons/Schemas/allureConfig.schema.json"
+ }
+ ],
+ "properties": {
+ "allure": {
+ "properties": {
+ "xunitRunnerReporter": {
+ "description": "An xUnit runner reporter that will be run in addition to allure-xunit.",
+ "examples": [
+ "auto",
+ "none",
+ "teamcity",
+ "xunit.runner.reporters.netcoreapp10, Version=2.4.1.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c"
+ ],
+ "default": "auto",
+ "oneOf": [
+ {
+ "type": "string",
+ "enum": [ "none", "auto" ]
+ },
+ {
+ "type": "string",
+ "description": "The runner switch or the assembly qualified class name of a runner reporter."
+ }
+ ]
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
From 24624f5ef288d6358474b39cac505440db57549a Mon Sep 17 00:00:00 2001
From: Maksim Stepanov <17935127+delatrie@users.noreply.github.com>
Date: Thu, 21 Sep 2023 19:11:27 +0700
Subject: [PATCH 02/13] Fix schema for allure-xunit and allure-specflow config
---
.../Schemas/allureConfig.schema.json | 54 ++++++++++++-------
Allure.XUnit/Schemas/allureConfig.schema.json | 2 +-
2 files changed, 37 insertions(+), 19 deletions(-)
diff --git a/Allure.SpecFlowPlugin/Schemas/allureConfig.schema.json b/Allure.SpecFlowPlugin/Schemas/allureConfig.schema.json
index b412c901..9d2fe028 100644
--- a/Allure.SpecFlowPlugin/Schemas/allureConfig.schema.json
+++ b/Allure.SpecFlowPlugin/Schemas/allureConfig.schema.json
@@ -12,7 +12,8 @@
"type": "object",
"properties": {
"convertToParameters": {
- "anyOf": [
+ "description": "If set to true, allure-specflow converts a step table data to step parameters.",
+ "oneOf": [
{
"type": "boolean"
},
@@ -20,13 +21,16 @@
"type": "string",
"enum": [ "true", "false" ]
}
- ]
+ ],
+ "default": false
},
"paramNameRegex": {
- "type": "string"
+ "type": "string",
+ "description": "If convertToParameters is true, allure-specflow checks if the first column header of a step table matches this regular expression. If it's not, the table is skipped.'"
},
"paramValueRegex": {
- "type": "string"
+ "type": "string",
+ "description": "If convertToParameters is true, allure-specflow checks if the second column header of a step table matches this regular expression. If it's not, the table is skipped.'"
}
}
},
@@ -37,13 +41,16 @@
"type": "object",
"properties": {
"parentSuite": {
- "type": "string"
+ "type": "string",
+ "description": "If a gherkin tag matches this regular expression, it's converted to the parentSuite label. If there is a capture group defined, only the part matching this group is used as parentSuite. Otherwise, the whole tag is used."
},
"suite": {
- "type": "string"
+ "type": "string",
+ "description": "If a gherkin tag matches this regular expression, it's converted to the suite label. If there is a capture group defined, only the part matching this group is used as suite. Otherwise, the whole tag is used."
},
"subSuite": {
- "type": "string"
+ "type": "string",
+ "description": "If a gherkin tag matches this regular expression, it's converted to the subSuite label. If there is a capture group defined, only the part matching this group is used as subSuite. Otherwise, the whole tag is used."
}
}
},
@@ -51,10 +58,12 @@
"type": "object",
"properties": {
"epic": {
- "type": "string"
+ "type": "string",
+ "description": "If a gherkin tag matches this regular expression, it's converted to the epic label. If there is a capture group defined, only the part matching this group is used as epic. Otherwise, the whole tag is used."
},
"story": {
- "type": "string"
+ "type": "string",
+ "description": "If a gherkin tag matches this regular expression, it's converted to the story label. If there is a capture group defined, only the part matching this group is used as story. Otherwise, the whole tag is used."
}
}
},
@@ -62,13 +71,16 @@
"type": "object",
"properties": {
"package": {
- "type": "string"
+ "type": "string",
+ "description": "If a gherkin tag matches this regular expression, it's converted to the package label. If there is a capture group defined, only the part matching this group is used as package. Otherwise, the whole tag is used."
},
"testClass": {
- "type": "string"
+ "type": "string",
+ "description": "If a gherkin tag matches this regular expression, it's converted to the testClass label. If there is a capture group defined, only the part matching this group is used as testClass. Otherwise, the whole tag is used."
},
"testMethod": {
- "type": "string"
+ "type": "string",
+ "description": "If a gherkin tag matches this regular expression, it's converted to the testMethod label. If there is a capture group defined, only the part matching this group is used as testMethod. Otherwise, the whole tag is used."
}
}
}
@@ -78,13 +90,16 @@
"type": "object",
"properties": {
"owner": {
- "type": "string"
+ "type": "string",
+ "description": "If a gherkin tag matches this regular expression, it's converted to the owner label. If there is a capture group defined, only the part matching this group is used as owner. Otherwise, the whole tag is used."
},
"severity": {
- "type": "string"
+ "type": "string",
+ "description": "If a gherkin tag matches this regular expression, it's converted to the severity label. If there is a capture group defined, only the part matching this group is used as severity. Otherwise, the whole tag is used."
},
"label": {
- "type": "string"
+ "type": "string",
+ "description": "If a gherkin tag matches this regular expression, it's converted to the specified label. You must define two capture groups in the expression. The first one is used to match a name of the label, while the second one is used to match a value."
}
}
},
@@ -92,13 +107,16 @@
"type": "object",
"properties": {
"link": {
- "type": "string"
+ "type": "string",
+ "description": "If a gherkin tag matches this regular expression, it's converted to a link. If there is a capture group defined, only the part matching this group is used as a link's value. Otherwise, the whole tag is used."
},
"issue": {
- "type": "string"
+ "type": "string",
+ "description": "If a gherkin tag matches this regular expression, it's converted to an issue link. If there is a capture group defined, only the part matching this group is used as a link's value. Otherwise, the whole tag is used."
},
"tms": {
- "type": "string"
+ "type": "string",
+ "description": "If a gherkin tag matches this regular expression, it's converted to a TMS item link. If there is a capture group defined, only the part matching this group is used as a link's value. Otherwise, the whole tag is used."
}
}
}
diff --git a/Allure.XUnit/Schemas/allureConfig.schema.json b/Allure.XUnit/Schemas/allureConfig.schema.json
index d5deb4f9..7904e56b 100644
--- a/Allure.XUnit/Schemas/allureConfig.schema.json
+++ b/Allure.XUnit/Schemas/allureConfig.schema.json
@@ -16,7 +16,7 @@
"xunit.runner.reporters.netcoreapp10, Version=2.4.1.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c"
],
"default": "auto",
- "oneOf": [
+ "anyOf": [
{
"type": "string",
"enum": [ "none", "auto" ]
From c25b6c5b74ea46d162fb5e4bcb964462d37fc846 Mon Sep 17 00:00:00 2001
From: Maksim Stepanov <17935127+delatrie@users.noreply.github.com>
Date: Tue, 26 Sep 2023 22:08:15 +0700
Subject: [PATCH 03/13] Implement selective run commons
---
.../TestPlanProviderTests.cs | 120 +++++++++
.../SelectiveRunTests/TestPlanTests.cs | 238 ++++++++++++++++++
Allure.Net.Commons/AllureConstants.cs | 6 +
Allure.Net.Commons/TestPlan/AllureTestPlan.cs | 126 ++++++++++
.../TestPlan/AllureTestPlanItem.cs | 10 +
5 files changed, 500 insertions(+)
create mode 100644 Allure.Net.Commons.Tests/SelectiveRunTests/TestPlanProviderTests.cs
create mode 100644 Allure.Net.Commons.Tests/SelectiveRunTests/TestPlanTests.cs
create mode 100644 Allure.Net.Commons/TestPlan/AllureTestPlan.cs
create mode 100644 Allure.Net.Commons/TestPlan/AllureTestPlanItem.cs
diff --git a/Allure.Net.Commons.Tests/SelectiveRunTests/TestPlanProviderTests.cs b/Allure.Net.Commons.Tests/SelectiveRunTests/TestPlanProviderTests.cs
new file mode 100644
index 00000000..bf4a8fe6
--- /dev/null
+++ b/Allure.Net.Commons.Tests/SelectiveRunTests/TestPlanProviderTests.cs
@@ -0,0 +1,120 @@
+using Allure.Net.Commons.TestPlan;
+using NUnit.Framework;
+using System;
+using System.IO;
+using System.Text;
+
+namespace Allure.Net.Commons.Tests.SelectiveRunTests
+{
+ class TestPlanProviderTests
+ {
+ private string testPlanPath;
+
+ [SetUp]
+ public void SetUp()
+ {
+ this.testPlanPath = Path.GetTempFileName();
+ File.WriteAllText(
+ this.testPlanPath,
+ "{\"tests\": [{\"id\": \"100\"}]}",
+ Encoding.UTF8
+ );
+ }
+
+ [TearDown]
+ public void TearDown()
+ {
+ if (File.Exists(this.testPlanPath))
+ {
+ File.Delete(this.testPlanPath);
+ }
+ Environment.SetEnvironmentVariable("ALLURE_TESTPLAN_PATH", null);
+ Environment.SetEnvironmentVariable("AS_TESTPLAN_PATH", null);
+ }
+
+ [Test]
+ public void TestPlanCreatedFromFileByNewEnvName()
+ {
+ Environment.SetEnvironmentVariable(
+ "ALLURE_TESTPLAN_PATH",
+ this.testPlanPath
+ );
+
+ var testplan = AllureTestPlan.FromEnvironment();
+
+ Assert.That(
+ testplan.Tests,
+ Is.EqualTo(
+ new[] { new AllureTestPlanItem() { Id = "100" } }
+ )
+ );
+ }
+
+ [Test]
+ public void TestPlanCreatedFromFileByOldEnvName()
+ {
+ Environment.SetEnvironmentVariable(
+ "AS_TESTPLAN_PATH",
+ this.testPlanPath
+ );
+
+ var testplan = AllureTestPlan.FromEnvironment();
+
+ Assert.That(
+ testplan.Tests,
+ Is.EqualTo(
+ new[] { new AllureTestPlanItem() { Id = "100" } }
+ )
+ );
+ }
+
+ [Test]
+ public void DefaultTestPlanCreatedIfNoEnvVarDefined()
+ {
+ Assert.That(
+ AllureTestPlan.FromEnvironment(),
+ Is.SameAs(
+ AllureTestPlan.DEFAULT_TESTPLAN
+ )
+ );
+ }
+
+ [Test]
+ public void DefaultTestPlanCreatedIfFIleDoesntExist()
+ {
+ Environment.SetEnvironmentVariable(
+ "ALLURE_TESTPLAN_PATH",
+ Guid.NewGuid().ToString()
+ );
+
+ Assert.That(
+ AllureTestPlan.FromEnvironment(),
+ Is.SameAs(
+ AllureTestPlan.DEFAULT_TESTPLAN
+ )
+ );
+ }
+
+ [Test]
+ public void IfBothEnvVarsPresentNewIsPreferred()
+ {
+ Environment.SetEnvironmentVariable(
+ "AS_TESTPLAN_PATH",
+ Guid.NewGuid().ToString()
+ );
+ Environment.SetEnvironmentVariable(
+ "ALLURE_TESTPLAN_PATH",
+ this.testPlanPath
+ );
+
+ var testplan = AllureTestPlan.FromEnvironment();
+
+ Assert.That(
+ testplan.Tests,
+ Is.EqualTo(
+ new[] { new AllureTestPlanItem() { Id = "100" } }
+ )
+ );
+ }
+ }
+}
diff --git a/Allure.Net.Commons.Tests/SelectiveRunTests/TestPlanTests.cs b/Allure.Net.Commons.Tests/SelectiveRunTests/TestPlanTests.cs
new file mode 100644
index 00000000..7fe0c0d6
--- /dev/null
+++ b/Allure.Net.Commons.Tests/SelectiveRunTests/TestPlanTests.cs
@@ -0,0 +1,238 @@
+using Allure.Net.Commons.TestPlan;
+using NUnit.Framework;
+using System.Linq;
+
+#nullable enable
+
+namespace Allure.Net.Commons.Tests.SelectiveRunTests
+{
+ class TestPlanTests
+ {
+ [TestCase("null")]
+ [TestCase("{}")]
+ [TestCase("{\"tests\": []}")]
+ public void EmptyTestPlan(string json)
+ {
+ var testPlan = AllureTestPlan.FromJson(json);
+
+ Assert.That(testPlan, Is.Not.Null);
+ Assert.That(
+ testPlan.IsMatch("", null),
+ Is.False
+ );
+ }
+
+ [TestCase(null, false)]
+ [TestCase("100", true)]
+ [TestCase("101", false)]
+ public void TestPlanWithIdEntry(
+ string allureIdOfTestCase,
+ bool expectedMatch
+ )
+ {
+ var testPlanJson = "{\"tests\": [{\"id\": \"100\"}]}";
+ var testPlan = AllureTestPlan.FromJson(testPlanJson);
+
+ Assert.That(
+ testPlan.IsMatch(
+ "",
+ allureIdOfTestCase
+ ),
+ Is.EqualTo(expectedMatch)
+ );
+ }
+
+ [TestCase("a", true)]
+ [TestCase("A", false)]
+ [TestCase("aa", false)]
+ public void TestPlanWithSelectorEntry(
+ string fullNameOfTestCase,
+ bool expectedMatch
+ )
+ {
+ var testPlanJson =
+ "{\"tests\": [{\"selector\": \"a\"}]}";
+ var testPlan = AllureTestPlan.FromJson(testPlanJson);
+
+ Assert.That(
+ testPlan.IsMatch(
+ fullNameOfTestCase,
+ null
+ ),
+ Is.EqualTo(expectedMatch)
+ );
+ }
+
+ [TestCase("a", "100", true)]
+ [TestCase("b", "100", true)]
+ [TestCase("a", "101", true)]
+ [TestCase("a", null, true)]
+ [TestCase("b", "101", false)]
+ [TestCase("b", null, false)]
+ public void TestPlanWithIdAndSelectorEntry(
+ string fullNameOfTestCase,
+ string allureIdOfTestCase,
+ bool expectedMatch
+ )
+ {
+ var testPlanJson =
+ "{\"tests\": [{\"selector\": \"a\", \"id\": \"100\"}]}";
+ var testPlan = AllureTestPlan.FromJson(testPlanJson);
+
+ Assert.That(
+ testPlan.IsMatch(
+ fullNameOfTestCase,
+ allureIdOfTestCase
+ ),
+ Is.EqualTo(expectedMatch)
+ );
+ }
+
+ [TestCase("100", true)]
+ [TestCase("101", true)]
+ [TestCase("102", false)]
+ public void TestPlanWithMultipleIdEntries(
+ string allureIdOfTestCase,
+ bool expectedMatch
+ )
+ {
+ var testPlanJson =
+ "{\"tests\": [{\"id\": \"100\"}, {\"id\": \"101\"}]}";
+ var testPlan = AllureTestPlan.FromJson(testPlanJson);
+
+ Assert.That(
+ testPlan.IsMatch(
+ "",
+ allureIdOfTestCase
+ ),
+ Is.EqualTo(expectedMatch)
+ );
+ }
+
+ [TestCase("a", true)]
+ [TestCase("b", true)]
+ [TestCase("c", false)]
+ public void TestPlanWithMultipleSelectorEntries(
+ string fullNameOfTestCase,
+ bool expectedMatch
+ )
+ {
+ var testPlanJson =
+ "{\"tests\": [{\"selector\": \"a\"}, {\"selector\": \"b\"}]}";
+ var testPlan = AllureTestPlan.FromJson(testPlanJson);
+
+ Assert.That(
+ testPlan.IsMatch(
+ fullNameOfTestCase,
+ null
+ ),
+ Is.EqualTo(expectedMatch)
+ );
+ }
+
+ [TestCase("a", "100", true)]
+ [TestCase("b", "100", true)]
+ [TestCase("a", "101", true)]
+ [TestCase("a", null, true)]
+ [TestCase("b", "101", false)]
+ [TestCase("b", null, false)]
+ public void TestPlanWithMultipleDifferentEntries(
+ string fullNameOfTestCase,
+ string allureIdOfTestCase,
+ bool expectedMatch
+ )
+ {
+ var testPlanJson =
+ "{\"tests\": [{\"id\": \"100\"}, {\"selector\": \"a\"}]}";
+ var testPlan = AllureTestPlan.FromJson(testPlanJson);
+
+ Assert.That(
+ testPlan.IsMatch(
+ fullNameOfTestCase,
+ allureIdOfTestCase
+ ),
+ Is.EqualTo(expectedMatch)
+ );
+ }
+
+ [TestCase("a", null, true)]
+ [TestCase("a", "100", true)]
+ [TestCase("a", "101", true)]
+ [TestCase("a", "102", true)]
+ [TestCase("b", null, true)]
+ [TestCase("b", "100", true)]
+ [TestCase("b", "101", true)]
+ [TestCase("b", "102", true)]
+ [TestCase("c", null, false)]
+ [TestCase("c", "100", true)]
+ [TestCase("c", "101", true)]
+ [TestCase("c", "102", false)]
+ public void TestPlanWithMultipleFullEntries(
+ string fullNameOfTestCase,
+ string allureIdOfTestCase,
+ bool expectedMatch
+ )
+ {
+ var testPlanJson =
+ "{\"tests\": [{\"selector\": \"a\", \"id\": \"100\"}, {\"selector\": \"b\", \"id\": \"101\"}]}";
+ var testPlan = AllureTestPlan.FromJson(testPlanJson);
+
+ Assert.That(
+ testPlan.IsMatch(
+ fullNameOfTestCase,
+ allureIdOfTestCase
+ ),
+ Is.EqualTo(expectedMatch)
+ );
+ }
+
+ [TestCase(new string[] { }, null)]
+ [TestCase(new[] { "ALLURE_ID", "100" }, "100")]
+ [TestCase(new[] { "l1", "v1" }, null)]
+ [TestCase(new[] { "AS_ID", "100" }, "100")]
+ [TestCase(new[] { "allure_id", "100" }, "100")]
+ [TestCase(new[] { "as_id", "100" }, "100")]
+ [TestCase(new[]
+ {
+ "l", "v",
+ "ALLURE_ID", "100"
+ }, "100")]
+ [TestCase(new[]
+ {
+ "l", "v",
+ "AS_ID", "100"
+ }, "100")]
+ [TestCase(new[]
+ {
+ "allure_id", "100",
+ "ALLURE_ID", "101",
+ "AS_ID", "102"
+ }, "100")]
+ [TestCase(new[]
+ {
+ "AS_ID", "100",
+ "ALLURE_ID", "101"
+ }, "101")]
+ public void GetAllureIdTest(string[] labels, string? expectedAllureId)
+ {
+ Assert.That(
+ AllureTestPlan.GetAllureId(
+ CreateTestCaseWithLabels(labels)
+ ),
+ Is.EqualTo(expectedAllureId)
+ );
+ }
+
+ static TestResult CreateTestCaseWithLabels(params string[] labels) =>
+ new()
+ {
+ labels = Enumerable.Range(0, labels.Length / 2).Select(
+ i => new Label()
+ {
+ name = labels[2 * i],
+ value = labels[2 * i + 1]
+ }
+ ).ToList()
+ };
+ }
+}
diff --git a/Allure.Net.Commons/AllureConstants.cs b/Allure.Net.Commons/AllureConstants.cs
index 33e959be..28fa9321 100644
--- a/Allure.Net.Commons/AllureConstants.cs
+++ b/Allure.Net.Commons/AllureConstants.cs
@@ -10,5 +10,11 @@ public sealed class AllureConstants
public const string TEST_RESULT_CONTAINER_FILE_SUFFIX = "-container.json";
public static string TEST_RUN_FILE_SUFFIX = "-testrun.json";
public const string ATTACHMENT_FILE_SUFFIX = "-attachment";
+
+ public const string OLD_ALLURE_ID_LABEL_NAME = "AS_ID";
+ public const string NEW_ALLURE_ID_LABEL_NAME = "ALLURE_ID";
+
+ public const string OLD_ALLURE_TESTPLAN_ENV_NAME = "AS_TESTPLAN_PATH";
+ public const string NEW_ALLURE_TESTPLAN_ENV_NAME = "ALLURE_TESTPLAN_PATH";
}
}
\ No newline at end of file
diff --git a/Allure.Net.Commons/TestPlan/AllureTestPlan.cs b/Allure.Net.Commons/TestPlan/AllureTestPlan.cs
new file mode 100644
index 00000000..8a13f161
--- /dev/null
+++ b/Allure.Net.Commons/TestPlan/AllureTestPlan.cs
@@ -0,0 +1,126 @@
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+
+#nullable enable
+
+namespace Allure.Net.Commons.TestPlan;
+
+///
+/// Used by integrations to enable selective run of tests.
+///
+public class AllureTestPlan
+{
+ ///
+ /// Testplan entries that denote tests included in the run.
+ ///
+ public List Tests
+ {
+ get => this.tests;
+ set
+ {
+ this.tests = value ?? new();
+ this.RecreateFilters();
+ }
+ }
+
+ ///
+ /// Used by integrations to check if a test is selected by the testplan.
+ ///
+ /// A fullName of the test.
+ ///
+ /// An identifier of the test case (if any).
+ /// Use to get it from the test result.
+ ///
+ /// true if the test should be executed. false otherwise.
+ public bool IsMatch(string fullName, string? allureId) =>
+ this.IsFullNameMatch(fullName) || this.IsAllureIdMatch(allureId);
+
+ ///
+ /// Creates the testplan from a JSON string.
+ ///
+ public static AllureTestPlan FromJson(string jsonContent) =>
+ JsonConvert.DeserializeObject(
+ jsonContent,
+ new JsonSerializerSettings()
+ {
+ ObjectCreationHandling = ObjectCreationHandling.Replace,
+ }
+ ) ?? DEFAULT_TESTPLAN;
+
+ ///
+ /// Creates the testplan from the file pointed by the environment variable.
+ ///
+ ///
+ public static AllureTestPlan FromEnvironment()
+ {
+ var testPlanPath = ResolveTestPlanPath();
+ return GetTestPlanByPath(testPlanPath);
+ }
+
+ ///
+ /// Extracts the id from a test result. If there is no id associated,
+ /// returns null.
+ ///
+ public static string? GetAllureId(TestResult testResult) =>
+ FindLabel(testResult, AllureConstants.NEW_ALLURE_ID_LABEL_NAME)
+ ?? FindLabel(testResult, AllureConstants.OLD_ALLURE_ID_LABEL_NAME);
+
+ ///
+ /// A testplan that doesn't filter any test.
+ ///
+ public static readonly AllureTestPlan DEFAULT_TESTPLAN = new();
+
+ static string? ResolveTestPlanPath() =>
+ new[]
+ {
+ AllureConstants.NEW_ALLURE_TESTPLAN_ENV_NAME,
+ AllureConstants.OLD_ALLURE_TESTPLAN_ENV_NAME
+ }.Select(Environment.GetEnvironmentVariable).Where(
+ ev => !string.IsNullOrWhiteSpace(ev)
+ ).FirstOrDefault();
+
+ static AllureTestPlan GetTestPlanByPath(string? testPlanPath) =>
+ testPlanPath is null || !File.Exists(testPlanPath)
+ ? DEFAULT_TESTPLAN
+ : ReadTestPlanFromFile(testPlanPath);
+
+ static AllureTestPlan ReadTestPlanFromFile(string testPlanPath) =>
+ FromJson(
+ File.ReadAllText(testPlanPath, Encoding.UTF8)
+ );
+
+ static string? FindLabel(TestResult testResult, string labelName) =>
+ testResult.labels.FirstOrDefault(
+ l => labelName.Equals(l.name, StringComparison.OrdinalIgnoreCase)
+ )?.value;
+
+ List tests = new();
+ HashSet allIds = new();
+ HashSet allSelectors = new();
+
+ void RecreateFilters()
+ {
+ this.allIds = new HashSet(
+ from entry in this.tests
+ where entry.Id is not null
+ select entry.Id,
+ StringComparer.Ordinal
+ );
+ this.allSelectors = new HashSet(
+ from entry in this.tests
+ where entry.Selector is not null
+ select entry.Selector,
+ StringComparer.Ordinal
+ );
+ }
+
+ bool IsAllureIdMatch(string? allureId) =>
+ allureId is not null && this.allIds.Contains(allureId);
+
+ bool IsFullNameMatch(string fullName) =>
+ this.allSelectors.Contains(fullName);
+}
diff --git a/Allure.Net.Commons/TestPlan/AllureTestPlanItem.cs b/Allure.Net.Commons/TestPlan/AllureTestPlanItem.cs
new file mode 100644
index 00000000..70adc1fb
--- /dev/null
+++ b/Allure.Net.Commons/TestPlan/AllureTestPlanItem.cs
@@ -0,0 +1,10 @@
+#nullable enable
+
+namespace Allure.Net.Commons.TestPlan;
+
+public record class AllureTestPlanItem
+{
+ public string? Id { get; set; }
+
+ public string? Selector { get; set; }
+}
From 63fe1de73f92399a17bc22f7774fb211378c6c50 Mon Sep 17 00:00:00 2001
From: Maksim Stepanov <17935127+delatrie@users.noreply.github.com>
Date: Thu, 28 Sep 2023 00:01:16 +0700
Subject: [PATCH 04/13] Change GetAllureId to take an enumeration
---
.../SelectiveRunTests/TestPlanTests.cs | 26 +++++++++----------
Allure.Net.Commons/TestPlan/AllureTestPlan.cs | 20 ++++++++------
2 files changed, 24 insertions(+), 22 deletions(-)
diff --git a/Allure.Net.Commons.Tests/SelectiveRunTests/TestPlanTests.cs b/Allure.Net.Commons.Tests/SelectiveRunTests/TestPlanTests.cs
index 7fe0c0d6..4016b012 100644
--- a/Allure.Net.Commons.Tests/SelectiveRunTests/TestPlanTests.cs
+++ b/Allure.Net.Commons.Tests/SelectiveRunTests/TestPlanTests.cs
@@ -1,5 +1,6 @@
using Allure.Net.Commons.TestPlan;
using NUnit.Framework;
+using System.Collections.Generic;
using System.Linq;
#nullable enable
@@ -11,14 +12,14 @@ class TestPlanTests
[TestCase("null")]
[TestCase("{}")]
[TestCase("{\"tests\": []}")]
- public void EmptyTestPlan(string json)
+ public void EmptyTestPlanEnablesAllTests(string json)
{
var testPlan = AllureTestPlan.FromJson(json);
Assert.That(testPlan, Is.Not.Null);
Assert.That(
testPlan.IsMatch("", null),
- Is.False
+ Is.True
);
}
@@ -217,22 +218,19 @@ public void GetAllureIdTest(string[] labels, string? expectedAllureId)
{
Assert.That(
AllureTestPlan.GetAllureId(
- CreateTestCaseWithLabels(labels)
+ CreateLabels(labels)
),
Is.EqualTo(expectedAllureId)
);
}
- static TestResult CreateTestCaseWithLabels(params string[] labels) =>
- new()
- {
- labels = Enumerable.Range(0, labels.Length / 2).Select(
- i => new Label()
- {
- name = labels[2 * i],
- value = labels[2 * i + 1]
- }
- ).ToList()
- };
+ static IEnumerable