diff --git a/src/ConfigCat.Client.Tests/ConfigCat.Client.Tests.csproj b/src/ConfigCat.Client.Tests/ConfigCat.Client.Tests.csproj
index 4f36397f..6005ee66 100644
--- a/src/ConfigCat.Client.Tests/ConfigCat.Client.Tests.csproj
+++ b/src/ConfigCat.Client.Tests/ConfigCat.Client.Tests.csproj
@@ -32,6 +32,9 @@
Always
+
+ Always
+
Always
@@ -53,6 +56,9 @@
Always
+
+ Always
+
diff --git a/src/ConfigCat.Client.Tests/SemanticVersion2ConfigEvaluatorTests.cs b/src/ConfigCat.Client.Tests/SemanticVersion2ConfigEvaluatorTests.cs
new file mode 100644
index 00000000..0fee5fc0
--- /dev/null
+++ b/src/ConfigCat.Client.Tests/SemanticVersion2ConfigEvaluatorTests.cs
@@ -0,0 +1,12 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace ConfigCat.Client.Tests
+{
+ [TestClass]
+ public class SemanticVersion2ConfigEvaluatorTests : ConfigEvaluatorTestsBase
+ {
+ protected override string SampleJsonFileName => "sample_semantic_2_v3.json";
+
+ protected override string MatrixResultFileName => "testmatrix_semantic_2.csv";
+ }
+}
diff --git a/src/ConfigCat.Client.Tests/data/sample_semantic_2_v3.json b/src/ConfigCat.Client.Tests/data/sample_semantic_2_v3.json
new file mode 100644
index 00000000..3d97f125
--- /dev/null
+++ b/src/ConfigCat.Client.Tests/data/sample_semantic_2_v3.json
@@ -0,0 +1 @@
+{"precedenceTests":{"v":"DEFAULT-FROM-CC-APP","t":1,"p":[],"r":[{"o":0,"a":"AppVersion","t":6,"c":"1.9.1-2","v":"< 1.9.1-2"},{"o":1,"a":"AppVersion","t":6,"c":"1.9.1-10","v":"< 1.9.1-10"},{"o":2,"a":"AppVersion","t":6,"c":"1.9.1-10a","v":"< 1.9.1-10a"},{"o":3,"a":"AppVersion","t":6,"c":"1.9.1-1a","v":"< 1.9.1-1a"},{"o":4,"a":"AppVersion","t":6,"c":"1.9.1-alpha","v":"< 1.9.1-alpha"},{"o":5,"a":"AppVersion","t":6,"c":"1.9.99-alpha","v":"< 1.9.99-alpha"},{"o":6,"a":"AppVersion","t":4,"c":"1.9.99-alpha","v":"= 1.9.99-alpha"},{"o":7,"a":"AppVersion","t":6,"c":"1.9.99-beta","v":"< 1.9.99-beta"},{"o":8,"a":"AppVersion","t":6,"c":"1.9.99-rc","v":"< 1.9.99-rc"},{"o":9,"a":"AppVersion","t":6,"c":"1.9.99-rc.1","v":"< 1.9.99-rc.1"},{"o":10,"a":"AppVersion","t":6,"c":"1.9.99-rc.2","v":"< 1.9.99-rc.2"},{"o":11,"a":"AppVersion","t":6,"c":"1.9.99-rc.20","v":"< 1.9.99-rc.20"},{"o":12,"a":"AppVersion","t":6,"c":"1.9.99-rc.20a","v":"< 1.9.99-rc.20a"},{"o":13,"a":"AppVersion","t":6,"c":"1.9.99-rc.2a","v":"< 1.9.99-rc.2a"},{"o":14,"a":"AppVersion","t":6,"c":"1.9.99","v":"< 1.9.99"},{"o":15,"a":"AppVersion","t":6,"c":"1.9.100","v":"< 1.9.100"},{"o":16,"a":"AppVersion","t":6,"c":"1.10.0-alpha","v":"< 1.10.0-alpha"},{"o":17,"a":"AppVersion","t":7,"c":"1.10.0-alpha","v":"<= 1.10.0-alpha"},{"o":18,"a":"AppVersion","t":6,"c":"1.10.0","v":"< 1.10.0"},{"o":19,"a":"AppVersion","t":7,"c":"1.10.0","v":"<= 1.10.0"},{"o":20,"a":"AppVersion","t":7,"c":"1.10.1","v":"<= 1.10.1"},{"o":21,"a":"AppVersion","t":7,"c":"1.10.3","v":"<= 1.10.3"},{"o":22,"a":"AppVersion","t":6,"c":"2.0.0","v":"< 2.0.0"},{"o":23,"a":"AppVersion","t":4,"c":"2.0.0","v":"= 2.0.0"},{"o":24,"a":"AppVersion","t":4,"c":"3.0.0+build3","v":"= 3.0.0+build3"},{"o":25,"a":"AppVersion","t":4,"c":"4.0.0+001","v":"= 4.0.0+001"},{"o":26,"a":"AppVersion","t":4,"c":"5.0.0+20130313144700","v":"= 5.0.0+20130313144700"},{"o":27,"a":"AppVersion","t":4,"c":"6.0.0+exp.sha.5114f85","v":"= 6.0.0+exp.sha.5114f85"},{"o":28,"a":"AppVersion","t":4,"c":"7.0.0-patch","v":"= 7.0.0-patch"},{"o":29,"a":"AppVersion","t":4,"c":"8.0.0-patch+anothermetadata","v":"= 8.0.0-patch+anothermetadata"},{"o":30,"a":"AppVersion","t":4,"c":"9.0.0-patch+metadata","v":"= 9.0.0-patch+metadata"},{"o":31,"a":"AppVersion","t":8,"c":"103.0.0","v":"> 103.0.0"},{"o":32,"a":"AppVersion","t":9,"c":"103.0.0","v":">= 103.0.0"},{"o":33,"a":"AppVersion","t":9,"c":"101.0.0","v":">= 101.0.0"},{"o":34,"a":"AppVersion","t":8,"c":"90.103.0","v":"> 90.103.0"},{"o":35,"a":"AppVersion","t":9,"c":"90.103.0","v":">= 90.103.0"},{"o":36,"a":"AppVersion","t":9,"c":"90.101.0","v":">= 90.101.0"},{"o":37,"a":"AppVersion","t":8,"c":"80.0.103","v":"> 80.0.103"},{"o":38,"a":"AppVersion","t":9,"c":"80.0.103","v":">= 80.0.103"},{"o":39,"a":"AppVersion","t":9,"c":"80.0.101","v":">= 80.0.101"},{"o":40,"a":"AppVersion","t":9,"c":"73.0.0-beta.2","v":">= 73.0.0-beta.2"},{"o":41,"a":"AppVersion","t":8,"c":"72.0.0-beta.2","v":"> 72.0.0-beta.2"},{"o":42,"a":"AppVersion","t":8,"c":"72.0.0-beta.1","v":"> 72.0.0-beta.1"},{"o":43,"a":"AppVersion","t":8,"c":"72.0.0-beta","v":"> 72.0.0-beta"},{"o":44,"a":"AppVersion","t":8,"c":"72.0.0-alpha","v":"> 72.0.0-alpha"},{"o":45,"a":"AppVersion","t":8,"c":"72.0.0-1a","v":"> 72.0.0-1a"},{"o":46,"a":"AppVersion","t":8,"c":"72.0.0-10a","v":"> 72.0.0-10a"},{"o":47,"a":"AppVersion","t":8,"c":"72.0.0-2","v":"> 72.0.0-2"},{"o":48,"a":"AppVersion","t":8,"c":"72.0.0-1","v":"> 72.0.0-1"},{"o":49,"a":"AppVersion","t":9,"c":"71.0.0+anothermetadata","v":">= 71.0.0+anothermetadata"},{"o":50,"a":"AppVersion","t":9,"c":"71.0.0-patch3+anothermetadata","v":">= 71.0.0-patch3+anothermetadata"},{"o":51,"a":"AppVersion","t":9,"c":"71.0.0-patch2","v":">= 71.0.0-patch2"},{"o":52,"a":"AppVersion","t":9,"c":"71.0.0-patch1+metadata","v":">= 71.0.0-patch1+metadata"},{"o":53,"a":"AppVersion","t":9,"c":"60.73.0-beta.2","v":">= 60.73.0-beta.2"},{"o":54,"a":"AppVersion","t":8,"c":"60.72.0-beta.2","v":"> 60.72.0-beta.2"},{"o":55,"a":"AppVersion","t":8,"c":"60.72.0-beta.1","v":"> 60.72.0-beta.1"},{"o":56,"a":"AppVersion","t":8,"c":"60.72.0-beta","v":"> 60.72.0-beta"},{"o":57,"a":"AppVersion","t":8,"c":"60.72.0-alpha","v":"> 60.72.0-alpha"},{"o":58,"a":"AppVersion","t":8,"c":"60.72.0-1a","v":"> 60.72.0-1a"},{"o":59,"a":"AppVersion","t":8,"c":"60.72.0-10a","v":"> 60.72.0-10a"},{"o":60,"a":"AppVersion","t":8,"c":"60.72.0-2","v":"> 60.72.0-2"},{"o":61,"a":"AppVersion","t":8,"c":"60.72.0-1","v":"> 60.72.0-1"},{"o":62,"a":"AppVersion","t":9,"c":"60.71.0+anothermetadata","v":">= 60.71.0+anothermetadata"},{"o":63,"a":"AppVersion","t":9,"c":"60.71.0-patch3+anothermetadata","v":">= 60.71.0-patch3+anothermetadata"},{"o":64,"a":"AppVersion","t":9,"c":"60.71.0-patch2","v":">= 60.71.0-patch2"},{"o":65,"a":"AppVersion","t":9,"c":"60.71.0-patch1+metadata","v":">= 60.71.0-patch1+metadata"},{"o":66,"a":"AppVersion","t":9,"c":"50.60.73-beta.2","v":">= 50.60.73-beta.2"},{"o":67,"a":"AppVersion","t":8,"c":"50.60.72-beta.2","v":"> 50.60.72-beta.2"},{"o":68,"a":"AppVersion","t":8,"c":"50.60.72-beta.1","v":"> 50.60.72-beta.1"},{"o":69,"a":"AppVersion","t":8,"c":"50.60.72-beta","v":"> 50.60.72-beta"},{"o":70,"a":"AppVersion","t":8,"c":"50.60.72-alpha","v":"> 50.60.72-alpha"},{"o":71,"a":"AppVersion","t":8,"c":"50.60.72-1a","v":"> 50.60.72-1a"},{"o":72,"a":"AppVersion","t":8,"c":"50.60.72-10a","v":"> 50.60.72-10a"},{"o":73,"a":"AppVersion","t":8,"c":"50.60.72-2","v":"> 50.60.72-2"},{"o":74,"a":"AppVersion","t":8,"c":"50.60.72-1","v":"> 50.60.72-1"},{"o":75,"a":"AppVersion","t":9,"c":"50.60.71+anothermetadata","v":">= 50.60.71+anothermetadata"},{"o":76,"a":"AppVersion","t":9,"c":"50.60.71-patch3+anothermetadata","v":">= 50.60.71-patch3+anothermetadata"},{"o":77,"a":"AppVersion","t":9,"c":"50.60.71-patch2","v":">= 50.60.71-patch2"},{"o":78,"a":"AppVersion","t":9,"c":"50.60.71-patch1+metadata","v":">= 50.60.71-patch1+metadata"},{"o":79,"a":"AppVersion","t":9,"c":"40.0.0-patch","v":">= 40.0.0-patch"},{"o":80,"a":"AppVersion","t":9,"c":"30.0.0-alpha","v":">= 30.0.0-alpha"}]}}
\ No newline at end of file
diff --git a/src/ConfigCat.Client.Tests/data/testmatrix_semantic_2.csv b/src/ConfigCat.Client.Tests/data/testmatrix_semantic_2.csv
new file mode 100644
index 00000000..e0da30c9
--- /dev/null
+++ b/src/ConfigCat.Client.Tests/data/testmatrix_semantic_2.csv
@@ -0,0 +1,95 @@
+Identifier;Email;Country;AppVersion;precedenceTests
+dontcare;;;1.9.1-1;< 1.9.1-2
+dontcare;;;1.9.1-2;< 1.9.1-10
+dontcare;;;1.9.1-10;< 1.9.1-10a
+dontcare;;;1.9.1-10a;< 1.9.1-1a
+dontcare;;;1.9.1-1a;< 1.9.1-alpha
+dontcare;;;1.9.1-alpha;< 1.9.99-alpha
+dontcare;;;1.9.99-alpha;= 1.9.99-alpha
+dontcare;;;1.9.99-alpha+build1;= 1.9.99-alpha
+dontcare;;;1.9.99-alpha+build2;= 1.9.99-alpha
+dontcare;;;1.9.99-alpha2;< 1.9.99-beta
+dontcare;;;1.9.99-beta;< 1.9.99-rc
+dontcare;;;1.9.99-rc;< 1.9.99-rc.1
+dontcare;;;1.9.99-rc.1;< 1.9.99-rc.2
+dontcare;;;1.9.99-rc.2;< 1.9.99-rc.20
+dontcare;;;1.9.99-rc.9;< 1.9.99-rc.20
+dontcare;;;1.9.99-rc.20;< 1.9.99-rc.20a
+dontcare;;;1.9.99-rc.20a;< 1.9.99-rc.2a
+dontcare;;;1.9.99-rc.2a;< 1.9.99
+dontcare;;;1.9.99;< 1.9.100
+dontcare;;;1.9.100;< 1.10.0-alpha
+dontcare;;;1.10.0-alpha;<= 1.10.0-alpha
+dontcare;;;1.10.0;<= 1.10.0
+dontcare;;;1.10.1;<= 1.10.1
+dontcare;;;1.10.2;<= 1.10.3
+dontcare;;;2.0.0;= 2.0.0
+dontcare;;;2.0.0+build3;= 2.0.0
+dontcare;;;2.0.0+001;= 2.0.0
+dontcare;;;2.0.0+20130313144700;= 2.0.0
+dontcare;;;2.0.0+exp.sha.5114f85;= 2.0.0
+dontcare;;;3.0.0;= 3.0.0+build3
+dontcare;;;4.0.0;= 4.0.0+001
+dontcare;;;5.0.0;= 5.0.0+20130313144700
+dontcare;;;6.0.0;= 6.0.0+exp.sha.5114f85
+dontcare;;;7.0.0-patch+metadata;= 7.0.0-patch
+dontcare;;;8.0.0-patch+metadata;= 8.0.0-patch+anothermetadata
+dontcare;;;9.0.0-patch;= 9.0.0-patch+metadata
+dontcare;;;10.0.0;DEFAULT-FROM-CC-APP
+dontcare;;;104.0.0;> 103.0.0
+dontcare;;;103.0.0;>= 103.0.0
+dontcare;;;102.0.0;>= 101.0.0
+dontcare;;;101.0.0;>= 101.0.0
+dontcare;;;90.104.0;> 90.103.0
+dontcare;;;90.103.0;>= 90.103.0
+dontcare;;;90.102.0;>= 90.101.0
+dontcare;;;90.101.0;>= 90.101.0
+dontcare;;;80.0.104;> 80.0.103
+dontcare;;;80.0.103;>= 80.0.103
+dontcare;;;80.0.102;>= 80.0.101
+dontcare;;;80.0.101;>= 80.0.101
+dontcare;;;73.0.0;>= 73.0.0-beta.2
+dontcare;;;72.0.0;> 72.0.0-beta.2
+dontcare;;;72.0.0-beta.2;> 72.0.0-beta.1
+dontcare;;;72.0.0-beta.1;> 72.0.0-beta
+dontcare;;;72.0.0-beta;> 72.0.0-alpha
+dontcare;;;72.0.0-alpha;> 72.0.0-1a
+dontcare;;;72.0.0-1a;> 72.0.0-10a
+dontcare;;;72.0.0-10aa;> 72.0.0-10a
+dontcare;;;72.0.0-10a;> 72.0.0-2
+dontcare;;;72.0.0-2;> 72.0.0-1
+dontcare;;;71.0.0+metadata;>= 71.0.0+anothermetadata
+dontcare;;;71.0.0-patch3+metadata;>= 71.0.0-patch3+anothermetadata
+dontcare;;;71.0.0-patch2+metadata;>= 71.0.0-patch2
+dontcare;;;71.0.0-patch1;>= 71.0.0-patch1+metadata
+dontcare;;;60.73.0;>= 60.73.0-beta.2
+dontcare;;;60.72.0;> 60.72.0-beta.2
+dontcare;;;60.72.0-beta.2;> 60.72.0-beta.1
+dontcare;;;60.72.0-beta.1;> 60.72.0-beta
+dontcare;;;60.72.0-beta;> 60.72.0-alpha
+dontcare;;;60.72.0-alpha;> 60.72.0-1a
+dontcare;;;60.72.0-1a;> 60.72.0-10a
+dontcare;;;60.72.0-10aa;> 60.72.0-10a
+dontcare;;;60.72.0-10a;> 60.72.0-2
+dontcare;;;60.72.0-2;> 60.72.0-1
+dontcare;;;60.71.0+metadata;>= 60.71.0+anothermetadata
+dontcare;;;60.71.0-patch3+metadata;>= 60.71.0-patch3+anothermetadata
+dontcare;;;60.71.0-patch2+metadata;>= 60.71.0-patch2
+dontcare;;;60.71.0-patch1;>= 60.71.0-patch1+metadata
+dontcare;;;50.60.73;>= 50.60.73-beta.2
+dontcare;;;50.60.72;> 50.60.72-beta.2
+dontcare;;;50.60.72-beta.2;> 50.60.72-beta.1
+dontcare;;;50.60.72-beta.1;> 50.60.72-beta
+dontcare;;;50.60.72-beta;> 50.60.72-alpha
+dontcare;;;50.60.72-alpha;> 50.60.72-1a
+dontcare;;;50.60.72-1a;> 50.60.72-10a
+dontcare;;;50.60.72-10aa;> 50.60.72-10a
+dontcare;;;50.60.72-10a;> 50.60.72-2
+dontcare;;;50.60.72-2;> 50.60.72-1
+dontcare;;;50.60.71+metadata;>= 50.60.71+anothermetadata
+dontcare;;;50.60.71-patch3+metadata;>= 50.60.71-patch3+anothermetadata
+dontcare;;;50.60.71-patch2+metadata;>= 50.60.71-patch2
+dontcare;;;50.60.71-patch1;>= 50.60.71-patch1+metadata
+dontcare;;;50.60.71-patch1+anothermetadata;>= 50.60.71-patch1+metadata
+dontcare;;;40.0.0-patch;>= 40.0.0-patch
+dontcare;;;30.0.0-beta;>= 30.0.0-alpha
\ No newline at end of file
diff --git a/src/ConfigCatClient/Evaluate/RolloutEvaluator.cs b/src/ConfigCatClient/Evaluate/RolloutEvaluator.cs
index 6d52a405..f8cef10f 100644
--- a/src/ConfigCatClient/Evaluate/RolloutEvaluator.cs
+++ b/src/ConfigCatClient/Evaluate/RolloutEvaluator.cs
@@ -308,9 +308,10 @@ private static bool EvaluateSemVer(string s1, string s2, ComparatorEnum comparat
}
return null;
- });
+ })
+ .ToList();
- return !rsvi.Contains(null) && rsvi.Contains(v1);
+ return !rsvi.Contains(null) && rsvi.Any(v => v.PrecedenceMatches(v1));
case ComparatorEnum.SemVerNotIn:
@@ -324,15 +325,16 @@ private static bool EvaluateSemVer(string s1, string s2, ComparatorEnum comparat
}
return null;
- });
+ })
+ .ToList();
- return !rsvni.Contains(null) && !rsvni.Contains(v1);
+ return !rsvni.Contains(null) && !rsvni.Any(v => v.PrecedenceMatches(v1));
case ComparatorEnum.SemVerLessThan:
if (SemVersion.TryParse(s2, out SemVersion v20, true))
{
- return v1 < v20;
+ return v1.CompareByPrecedence(v20) < 0;
}
break;
@@ -340,7 +342,7 @@ private static bool EvaluateSemVer(string s1, string s2, ComparatorEnum comparat
if (SemVersion.TryParse(s2, out SemVersion v21, true))
{
- return v1 <= v21;
+ return v1.CompareByPrecedence(v21) <= 0;
}
break;
@@ -348,7 +350,7 @@ private static bool EvaluateSemVer(string s1, string s2, ComparatorEnum comparat
if (SemVersion.TryParse(s2, out SemVersion v22, true))
{
- return v1 > v22;
+ return v1.CompareByPrecedence(v22) > 0;
}
break;
@@ -356,7 +358,7 @@ private static bool EvaluateSemVer(string s1, string s2, ComparatorEnum comparat
if (SemVersion.TryParse(s2, out SemVersion v23, true))
{
- return v1 >= v23;
+ return v1.CompareByPrecedence(v23) >= 0;
}
break;