Ecng.UnitTesting
1.0.243
dotnet add package Ecng.UnitTesting --version 1.0.243
NuGet\Install-Package Ecng.UnitTesting -Version 1.0.243
<PackageReference Include="Ecng.UnitTesting" Version="1.0.243" />
<PackageVersion Include="Ecng.UnitTesting" Version="1.0.243" />
<PackageReference Include="Ecng.UnitTesting" />
paket add Ecng.UnitTesting --version 1.0.243
#r "nuget: Ecng.UnitTesting, 1.0.243"
#:package Ecng.UnitTesting@1.0.243
#addin nuget:?package=Ecng.UnitTesting&version=1.0.243
#tool nuget:?package=Ecng.UnitTesting&version=1.0.243
Ecng.UnitTesting
Enhanced assertion helpers and base test classes for MSTest unit testing with fluent extensions, collection comparisons, and CI environment detection.
Purpose
Ecng.UnitTesting extends MSTest with additional assertion methods, fluent extension syntax, and helpful utilities for writing more expressive and maintainable unit tests. It provides a base test class with built-in CI detection, secrets management, and numerous assertion helpers.
Key Features
- Fluent assertion syntax with extension methods
- Enhanced collection and enumerable comparisons
- Range and comparison assertions
- String assertion helpers
- Numeric assertions (positive, negative, zero)
- CI environment detection
- Secrets management for tests
- Base test class with common functionality
- Support for deep object comparisons
- Type assertions
Installation
Add a reference to the Ecng.UnitTesting package in your project.
Base Test Class
The BaseTestClass provides common functionality for all tests, including CI detection and secrets management.
Basic Usage
using Ecng.UnitTesting;
using Microsoft.VisualStudio.TestTools.UnitTesting;
[TestClass]
public class MyTests : BaseTestClass
{
[TestMethod]
public void SimpleTest()
{
int actual = 5;
int expected = 5;
AreEqual(expected, actual);
}
[TestMethod]
public void TestWithCancellation()
{
// Access test context cancellation token
var task = SomeAsyncOperation(CancellationToken);
task.Wait();
}
}
CI Environment Detection
Skip tests when running in CI environments.
using Ecng.UnitTesting;
using Microsoft.VisualStudio.TestTools.UnitTesting;
[TestClass]
public class IntegrationTests : BaseTestClass
{
// Skip all tests in this class when running in CI
protected override bool SkipInCI => true;
protected override string SkipInCIReason => "Requires local database";
[TestMethod]
public void TestWithLocalDatabase()
{
// This test only runs on localhost, not in CI
if (IsLocalHost)
{
Console.WriteLine("Running on localhost");
}
else
{
Console.WriteLine("Running in CI");
}
}
}
Secrets Management
Access secrets from environment variables or a secrets.json file.
using Ecng.UnitTesting;
using Microsoft.VisualStudio.TestTools.UnitTesting;
[TestClass]
public class ApiTests : BaseTestClass
{
[TestMethod]
public void TestApiWithCredentials()
{
// Get secret from environment variable or secrets.json
string apiKey = GetSecret("API_KEY");
string endpoint = GetSecret("API_ENDPOINT");
// Use the secrets
var client = new ApiClient(endpoint, apiKey);
// ...
}
[TestMethod]
public void TestWithOptionalSecret()
{
// Try to get secret without failing
string optionalKey = TryGetSecret("OPTIONAL_KEY");
if (optionalKey != null)
{
// Use the optional key
}
else
{
// Skip or use default
}
}
}
Custom Secrets File
using Ecng.UnitTesting;
using Microsoft.VisualStudio.TestTools.UnitTesting;
[TestClass]
public class TestInitializer
{
[AssemblyInitialize]
public static void Initialize(TestContext context)
{
// Change secrets file location
BaseTestClass.SecretsFile = "custom-secrets.json";
}
}
Fluent Assertion Extensions
The AssertHelper class provides extension methods for fluent assertions.
Boolean Assertions
using Ecng.UnitTesting;
// Assert true
bool condition = true;
condition.AssertTrue();
condition.AssertTrue("Condition should be true");
// Assert false
bool flag = false;
flag.AssertFalse();
flag.AssertFalse("Flag should be false");
Null Assertions
using Ecng.UnitTesting;
// Assert null
object obj = null;
obj.AssertNull();
obj.AssertNull("Object should be null");
// Assert not null
string str = "test";
str.AssertNotNull();
str.AssertNotNull("String should not be null");
// Special case for exceptions
Exception error = null;
error.AssertNull(); // Throws the exception if not null
Equality Assertions
using Ecng.UnitTesting;
// Assert equal
int value = 42;
value.AssertEqual(42);
value.AreEqual(42); // Alternative syntax
// Assert equal with message
string name = "John";
name.AssertEqual("John", "Names should match");
// Assert not equal
int x = 5;
x.AssertNotEqual(10);
Floating Point Comparisons
using Ecng.UnitTesting;
// Compare doubles with delta
double value = 3.14159;
value.AssertEqual(3.14, delta: 0.01);
// Compare floats with delta
float f = 2.5f;
f.AssertEqual(2.5f, delta: 0.001f);
String Assertions
using Ecng.UnitTesting;
// String equality with null-as-empty option
string str1 = "";
string str2 = null;
str1.AssertEqual(str2, nullAsEmpty: true); // Passes
// Contains substring
string message = "Hello World";
message.AssertContains("World");
message.AssertContains("llo", "Should contain 'llo'");
Type Assertions
using Ecng.UnitTesting;
// Assert type
object obj = "test";
obj.AssertOfType<string>();
// Assert not of type
object number = 42;
number.AssertNotOfType<string>();
Reference Assertions
using Ecng.UnitTesting;
// Same instance
var obj1 = new object();
var obj2 = obj1;
obj1.AssertSame(obj2);
// Not same instance
var obj3 = new object();
var obj4 = new object();
obj3.AssertNotSame(obj4);
Collection Assertions
Enumerable Equality
using Ecng.UnitTesting;
using System.Collections.Generic;
// Compare enumerables element by element
var list1 = new List<int> { 1, 2, 3 };
var list2 = new List<int> { 1, 2, 3 };
list1.AssertEqual(list2);
// Works with arrays
int[] arr1 = { 1, 2, 3 };
int[] arr2 = { 1, 2, 3 };
arr1.AssertEqual(arr2);
// Handles null collections
List<int> nullList = null;
nullList.AssertEqual(null); // Passes
Contains Assertions
using Ecng.UnitTesting;
using System.Collections.Generic;
// Assert collection contains element
var numbers = new List<int> { 1, 2, 3, 4, 5 };
numbers.AssertContains(3);
numbers.AssertContains(5, "Should contain 5");
Comparison Assertions
Greater/Less Than
using Ecng.UnitTesting;
// Assert greater than
int value = 10;
value.AssertGreater(5);
value.AssertGreater(5, "Value should be greater than 5");
// Assert less than
int small = 3;
small.AssertLess(10);
small.AssertLess(10, "Value should be less than 10");
Range Assertions
using Ecng.UnitTesting;
// Assert value is in range (exclusive)
int value = 5;
value.AssertInRange(min: 1, max: 10);
// Works with any IComparable<T>
double price = 99.99;
price.AssertInRange(50.0, 150.0);
DateTime date = DateTime.Now;
date.AssertInRange(DateTime.Today, DateTime.Today.AddDays(1));
Base Test Class Methods
The BaseTestClass provides static methods mirroring standard MSTest assertions.
Basic Assertions
using Ecng.UnitTesting;
using Microsoft.VisualStudio.TestTools.UnitTesting;
[TestClass]
public class CalculatorTests : BaseTestClass
{
[TestMethod]
public void TestAddition()
{
int result = 2 + 2;
AreEqual(4, result);
IsTrue(result == 4);
IsNotNull(result);
}
[TestMethod]
public void TestDivision()
{
double result = 10.0 / 3.0;
AreEqual(3.333, result, delta: 0.001);
}
}
Exception Assertions
using Ecng.UnitTesting;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
[TestClass]
public class ExceptionTests : BaseTestClass
{
[TestMethod]
public void TestThrows()
{
// Assert that an exception is thrown
var ex = Throws<ArgumentNullException>(() =>
{
string test = null;
test.ToString();
});
IsNotNull(ex);
}
[TestMethod]
public void TestThrowsExactType()
{
// Assert exact exception type (not derived)
ThrowsExactly<InvalidOperationException>(() =>
{
throw new InvalidOperationException("Test");
});
}
[TestMethod]
public async Task TestThrowsAsync()
{
// Assert async exception
await ThrowsAsync<ArgumentException>(async () =>
{
await Task.Delay(10);
throw new ArgumentException("Test");
});
}
}
String Assertions
using Ecng.UnitTesting;
using Microsoft.VisualStudio.TestTools.UnitTesting;
[TestClass]
public class StringTests : BaseTestClass
{
[TestMethod]
public void TestStringMethods()
{
string text = "Hello World";
Contains("World", text);
StartsWith("Hello", text);
EndsWith("World", text);
IsNotNullOrEmpty(text);
IsNotNullOrWhiteSpace(text);
}
[TestMethod]
public void TestEmptyStrings()
{
string empty = "";
IsEmpty(empty);
IsNullOrEmpty(empty);
string whitespace = " ";
IsNullOrWhiteSpace(whitespace);
}
}
Regex Assertions
using Ecng.UnitTesting;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Text.RegularExpressions;
[TestClass]
public class RegexTests : BaseTestClass
{
[TestMethod]
public void TestEmailPattern()
{
var emailPattern = new Regex(@"^[^@]+@[^@]+\.[^@]+$");
string validEmail = "user@example.com";
MatchesRegex(emailPattern, validEmail);
string invalidEmail = "not-an-email";
DoesNotMatch(emailPattern, invalidEmail);
}
}
Collection Assertions
using Ecng.UnitTesting;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Generic;
[TestClass]
public class CollectionTests : BaseTestClass
{
[TestMethod]
public void TestCollections()
{
var list1 = new List<int> { 1, 2, 3 };
var list2 = new List<int> { 1, 2, 3 };
// Equal collections
AreEqual(list1, list2);
// Count assertion
HasCount(3, list1);
// Contains
Contains(list1, 2);
DoesNotContain(list1, 5);
// All items not null
var objects = new List<object> { new(), new(), new() };
AllItemsAreNotNull(objects);
// All unique
var unique = new List<int> { 1, 2, 3 };
AllItemsAreUnique(unique);
// All same type
var strings = new List<object> { "a", "b", "c" };
AllItemsAreInstancesOfType(strings, typeof(string));
}
[TestMethod]
public void TestSubsets()
{
var superset = new List<int> { 1, 2, 3, 4, 5 };
var subset = new List<int> { 2, 3, 4 };
IsSubsetOf(subset, superset);
}
[TestMethod]
public void TestEquivalence()
{
var list1 = new List<int> { 1, 2, 3 };
var list2 = new List<int> { 3, 2, 1 }; // Different order
AreEquivalent(list1, list2); // Passes - same elements
}
}
Numeric Assertions
using Ecng.UnitTesting;
using Microsoft.VisualStudio.TestTools.UnitTesting;
[TestClass]
public class NumericTests : BaseTestClass
{
[TestMethod]
public void TestNumericValues()
{
int positive = 42;
IsPositive(positive);
int negative = -5;
IsNegative(negative);
int zero = 0;
IsZero(zero);
int nonZero = 100;
IsNotZero(nonZero);
}
[TestMethod]
public void TestComparisons()
{
int value = 10;
IsGreater(value, 5);
IsGreaterOrEqual(value, 10);
IsLess(value, 20);
IsLessOrEqual(value, 10);
IsInRange(value, min: 5, max: 15);
IsNotInRange(value, min: 20, max: 30);
}
[TestMethod]
public void TestDoublesAndDecimals()
{
double dbl = 3.14;
IsPositive(dbl);
decimal dec = -2.5m;
IsNegative(dec);
long lng = 0L;
IsZero(lng);
}
}
Advanced Examples
Custom Test Base with Setup
using Ecng.UnitTesting;
using Microsoft.VisualStudio.TestTools.UnitTesting;
public abstract class DatabaseTestBase : BaseTestClass
{
protected Database Database { get; private set; }
[TestInitialize]
public void SetupDatabase()
{
// Runs before each test
Database = new Database(GetSecret("DB_CONNECTION"));
Database.Connect();
}
[TestCleanup]
public void CleanupDatabase()
{
// Runs after each test
Database?.Dispose();
}
}
[TestClass]
public class UserRepositoryTests : DatabaseTestBase
{
[TestMethod]
public void TestCreateUser()
{
var repo = new UserRepository(Database);
var user = repo.CreateUser("test@example.com");
IsNotNull(user);
IsPositive(user.Id);
}
}
Complex Object Comparison
using Ecng.UnitTesting;
using Microsoft.VisualStudio.TestTools.UnitTesting;
[TestClass]
public class ObjectTests : BaseTestClass
{
[TestMethod]
public void TestComplexObjects()
{
var person1 = new Person
{
Name = "John",
Age = 30,
Emails = new[] { "john@example.com" }
};
var person2 = new Person
{
Name = "John",
Age = 30,
Emails = new[] { "john@example.com" }
};
// Deep comparison using AssertEqual
person1.Name.AssertEqual(person2.Name);
person1.Age.AssertEqual(person2.Age);
person1.Emails.AssertEqual(person2.Emails);
}
}
Environment-Specific Tests
using Ecng.UnitTesting;
using Microsoft.VisualStudio.TestTools.UnitTesting;
[TestClass]
public class EnvironmentTests : BaseTestClass
{
[TestMethod]
public void TestEnvironmentVariable()
{
string value = Env("PATH");
IsNotNullOrEmpty(value);
}
[TestMethod]
public void TestLocalHostOnly()
{
if (!IsLocalHost)
{
Inconclusive("This test only runs on localhost");
return;
}
// Test code that should only run locally
IsTrue(true);
}
[TestMethod]
public void TestCIOnly()
{
if (IsLocalHost)
{
Inconclusive("This test only runs in CI");
return;
}
// Test code that should only run in CI
IsTrue(true);
}
}
Fluent Test Style
using Ecng.UnitTesting;
using Microsoft.VisualStudio.TestTools.UnitTesting;
[TestClass]
public class FluentTests : BaseTestClass
{
[TestMethod]
public void TestFluentStyle()
{
var result = Calculate(10, 5);
result.AssertNotNull()
.AssertGreater(0)
.AssertLess(100)
.AssertEqual(50);
}
private int? Calculate(int a, int b)
{
return a * b;
}
}
// Extension to chain assertions
public static class AssertExtensions
{
public static T AssertNotNull<T>(this T value) where T : class
{
value.AssertNotNull();
return value;
}
public static T AssertGreater<T>(this T value, T min) where T : IComparable<T>
{
value.AssertGreater(min);
return value;
}
public static T AssertLess<T>(this T value, T max) where T : IComparable<T>
{
value.AssertLess(max);
return value;
}
public static T AssertEqual<T>(this T value, T expected)
{
value.AssertEqual(expected);
return value;
}
}
Secrets File Format
Create a secrets.json file in your test project or solution root:
{
"API_KEY": "your-api-key-here",
"API_ENDPOINT": "https://api.example.com",
"DB_CONNECTION": "Server=localhost;Database=test;",
"BACKUP_AWS_REGION": "us-east-1"
}
The file will be automatically discovered by searching up to 8 parent directories from the test assembly location.
CI Environment Variables Detected
The library detects the following CI environments:
- GitHub Actions (GITHUB_ACTIONS)
- GitLab CI (GITLAB_CI)
- Jenkins (JENKINS_URL)
- Azure Pipelines (TF_BUILD)
- Travis CI (TRAVIS)
- CircleCI (CIRCLECI)
- TeamCity (TEAMCITY_VERSION)
- Buildkite (BUILDKITE)
- Generic CI flag (CI)
Platform Support
- .NET Standard 2.0+
- .NET 6.0+
- .NET 10.0+
Dependencies
- Microsoft.VisualStudio.TestTools.UnitTesting (MSTest)
- Ecng.Common
- System.Text.Json (for secrets management)
Best Practices
- Use Fluent Assertions: Prefer extension methods for more readable tests
- Handle Secrets Safely: Use environment variables or secrets.json, never commit secrets
- Skip CI When Needed: Use SkipInCI for tests requiring local resources
- Provide Clear Messages: Always add descriptive messages to assertions
- Use BaseTestClass: Inherit from BaseTestClass for consistent test infrastructure
- Test Cleanup: Use TestCleanup or implement IDisposable for resource cleanup
- Environment Detection: Use IsLocalHost to conditionally run tests
See Also
- MSTest Documentation
- Ecng.Common - Common utilities used by this library
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Ecng.Common (>= 1.0.231)
- MSTest.TestFramework (>= 4.0.2)
-
net6.0
- Ecng.Common (>= 1.0.231)
- MSTest.TestFramework (>= 3.11.1)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories (1)
Showing the top 1 popular GitHub repositories that depend on Ecng.UnitTesting:
| Repository | Stars |
|---|---|
|
StockSharp/StockSharp
Algorithmic trading and quantitative trading open source platform to develop trading robots (stock markets, forex, crypto, bitcoins, and options).
|
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.243 | 50 | 12/22/2025 |
| 1.0.242 | 51 | 12/21/2025 |
| 1.0.241 | 206 | 12/19/2025 |
| 1.0.240 | 194 | 12/19/2025 |
| 1.0.239 | 252 | 12/18/2025 |
| 1.0.238 | 287 | 12/17/2025 |
| 1.0.237 | 304 | 12/15/2025 |
| 1.0.236 | 213 | 12/14/2025 |
| 1.0.235 | 154 | 12/12/2025 |
| 1.0.234 | 105 | 12/12/2025 |
| 1.0.233 | 107 | 12/12/2025 |
| 1.0.232 | 117 | 12/12/2025 |
| 1.0.231 | 301 | 12/7/2025 |
| 1.0.230 | 210 | 11/29/2025 |
| 1.0.229 | 123 | 11/28/2025 |
| 1.0.228 | 134 | 11/28/2025 |
| 1.0.227 | 189 | 11/27/2025 |
| 1.0.226 | 288 | 11/24/2025 |
| 1.0.225 | 189 | 11/24/2025 |
| 1.0.224 | 183 | 11/23/2025 |
| 1.0.223 | 241 | 11/22/2025 |
| 1.0.222 | 476 | 11/20/2025 |
| 1.0.221 | 436 | 11/18/2025 |
| 1.0.220 | 389 | 11/18/2025 |
| 1.0.219 | 368 | 11/13/2025 |
| 1.0.218 | 260 | 11/10/2025 |
| 1.0.217 | 1,125 | 11/1/2025 |
| 1.0.216 | 242 | 10/28/2025 |
| 1.0.215 | 238 | 10/27/2025 |
| 1.0.214 | 194 | 10/27/2025 |
| 1.0.213 | 182 | 10/27/2025 |
| 1.0.212 | 120 | 10/25/2025 |
| 1.0.211 | 1,090 | 10/3/2025 |
| 1.0.210 | 409 | 9/25/2025 |
| 1.0.209 | 833 | 8/30/2025 |
| 1.0.208 | 310 | 8/19/2025 |
| 1.0.207 | 1,299 | 7/13/2025 |
| 1.0.206 | 206 | 7/13/2025 |
| 1.0.205 | 188 | 7/12/2025 |
| 1.0.204 | 222 | 7/8/2025 |
| 1.0.203 | 209 | 7/4/2025 |
| 1.0.202 | 549 | 6/16/2025 |
| 1.0.201 | 369 | 6/9/2025 |
| 1.0.200 | 292 | 6/8/2025 |
| 1.0.199 | 221 | 5/21/2025 |
| 1.0.198 | 214 | 5/17/2025 |
| 1.0.197 | 306 | 5/12/2025 |
| 1.0.196 | 247 | 5/12/2025 |
| 1.0.195 | 293 | 4/17/2025 |
| 1.0.194 | 198 | 4/12/2025 |
| 1.0.193 | 263 | 3/20/2025 |
| 1.0.192 | 257 | 3/19/2025 |
| 1.0.191 | 197 | 2/26/2025 |
| 1.0.190 | 175 | 2/26/2025 |
| 1.0.189 | 196 | 2/13/2025 |
| 1.0.188 | 201 | 2/5/2025 |
| 1.0.187 | 188 | 1/21/2025 |
| 1.0.186 | 149 | 1/14/2025 |
| 1.0.185 | 168 | 1/12/2025 |
| 1.0.184 | 168 | 1/10/2025 |
| 1.0.183 | 189 | 12/22/2024 |
| 1.0.182 | 211 | 11/18/2024 |
| 1.0.181 | 207 | 11/7/2024 |
| 1.0.180 | 230 | 10/19/2024 |
| 1.0.179 | 245 | 10/5/2024 |
| 1.0.178 | 264 | 9/18/2024 |
| 1.0.177 | 237 | 9/17/2024 |
| 1.0.176 | 229 | 9/1/2024 |
| 1.0.175 | 227 | 6/12/2024 |
| 1.0.174 | 213 | 5/28/2024 |
| 1.0.173 | 253 | 5/4/2024 |
| 1.0.172 | 234 | 4/14/2024 |
| 1.0.171 | 230 | 3/28/2024 |
| 1.0.170 | 252 | 3/17/2024 |
| 1.0.169 | 243 | 2/23/2024 |
| 1.0.168 | 218 | 2/23/2024 |
| 1.0.167 | 254 | 2/18/2024 |
| 1.0.166 | 229 | 2/16/2024 |
| 1.0.165 | 237 | 2/13/2024 |
| 1.0.164 | 241 | 2/8/2024 |
| 1.0.163 | 233 | 2/4/2024 |
| 1.0.162 | 248 | 1/23/2024 |
| 1.0.161 | 256 | 1/12/2024 |
| 1.0.160 | 248 | 1/2/2024 |
| 1.0.159 | 267 | 12/29/2023 |
| 1.0.158 | 274 | 11/12/2023 |
| 1.0.157 | 198 | 11/10/2023 |
| 1.0.156 | 193 | 11/10/2023 |
| 1.0.155 | 180 | 11/9/2023 |
| 1.0.154 | 206 | 11/3/2023 |
| 1.0.153 | 209 | 11/1/2023 |
| 1.0.152 | 218 | 11/1/2023 |
| 1.0.151 | 264 | 9/8/2023 |
| 1.0.150 | 254 | 9/8/2023 |
| 1.0.149 | 234 | 9/3/2023 |
| 1.0.148 | 282 | 8/21/2023 |
| 1.0.147 | 270 | 8/14/2023 |
| 1.0.146 | 267 | 8/10/2023 |
| 1.0.145 | 270 | 6/29/2023 |
| 1.0.144 | 317 | 5/27/2023 |
| 1.0.143 | 306 | 5/19/2023 |
| 1.0.142 | 288 | 5/8/2023 |
| 1.0.141 | 338 | 4/21/2023 |
| 1.0.140 | 326 | 4/20/2023 |
| 1.0.139 | 345 | 4/3/2023 |
| 1.0.138 | 415 | 3/13/2023 |
| 1.0.137 | 408 | 3/6/2023 |
| 1.0.136 | 431 | 2/26/2023 |
| 1.0.135 | 404 | 2/9/2023 |
| 1.0.134 | 427 | 2/7/2023 |
| 1.0.133 | 397 | 2/4/2023 |
| 1.0.132 | 429 | 2/2/2023 |
| 1.0.131 | 430 | 1/30/2023 |
| 1.0.130 | 460 | 1/18/2023 |
| 1.0.129 | 435 | 12/30/2022 |
| 1.0.128 | 434 | 12/23/2022 |
| 1.0.127 | 474 | 12/12/2022 |
| 1.0.126 | 500 | 12/4/2022 |
| 1.0.125 | 489 | 12/4/2022 |
| 1.0.124 | 493 | 11/30/2022 |
| 1.0.123 | 489 | 11/28/2022 |
| 1.0.122 | 516 | 11/18/2022 |
| 1.0.121 | 500 | 11/11/2022 |
| 1.0.120 | 506 | 11/11/2022 |
| 1.0.119 | 480 | 11/10/2022 |
| 1.0.118 | 544 | 11/5/2022 |
| 1.0.117 | 558 | 11/4/2022 |
| 1.0.116 | 546 | 11/1/2022 |
| 1.0.115 | 583 | 10/16/2022 |
| 1.0.114 | 625 | 9/10/2022 |
| 1.0.113 | 593 | 9/8/2022 |
| 1.0.112 | 640 | 9/8/2022 |
| 1.0.111 | 614 | 9/8/2022 |
| 1.0.110 | 599 | 9/4/2022 |
| 1.0.109 | 585 | 8/24/2022 |
| 1.0.108 | 663 | 8/8/2022 |
| 1.0.107 | 649 | 7/26/2022 |
| 1.0.106 | 625 | 7/26/2022 |
| 1.0.105 | 648 | 7/19/2022 |
| 1.0.104 | 647 | 7/18/2022 |
| 1.0.103 | 646 | 7/8/2022 |
| 1.0.102 | 627 | 6/18/2022 |
| 1.0.101 | 635 | 6/6/2022 |
| 1.0.100 | 678 | 4/30/2022 |
| 1.0.99 | 666 | 4/20/2022 |
| 1.0.98 | 637 | 4/10/2022 |
| 1.0.97 | 681 | 4/7/2022 |
| 1.0.96 | 668 | 4/7/2022 |
| 1.0.95 | 649 | 4/2/2022 |
| 1.0.94 | 640 | 3/29/2022 |
| 1.0.93 | 674 | 3/27/2022 |
| 1.0.92 | 717 | 1/24/2022 |
| 1.0.91 | 495 | 12/29/2021 |
| 1.0.90 | 500 | 12/20/2021 |
| 1.0.89 | 511 | 12/13/2021 |
| 1.0.88 | 847 | 12/6/2021 |
| 1.0.87 | 490 | 12/2/2021 |
| 1.0.86 | 1,073 | 11/29/2021 |
| 1.0.85 | 523 | 11/22/2021 |
| 1.0.84 | 570 | 11/17/2021 |
| 1.0.83 | 567 | 11/13/2021 |
| 1.0.82 | 529 | 11/10/2021 |
| 1.0.81 | 534 | 11/9/2021 |
| 1.0.80 | 540 | 11/5/2021 |
| 1.0.79 | 532 | 11/4/2021 |
| 1.0.78 | 557 | 11/4/2021 |
| 1.0.77 | 581 | 11/3/2021 |
| 1.0.76 | 637 | 10/30/2021 |
| 1.0.75 | 536 | 10/21/2021 |
| 1.0.74 | 651 | 10/17/2021 |
| 1.0.73 | 532 | 10/14/2021 |
| 1.0.72 | 569 | 10/13/2021 |
| 1.0.71 | 527 | 10/12/2021 |
| 1.0.70 | 583 | 10/11/2021 |
| 1.0.69 | 542 | 10/9/2021 |
| 1.0.68 | 600 | 10/7/2021 |
| 1.0.67 | 596 | 10/7/2021 |
| 1.0.66 | 562 | 10/7/2021 |
| 1.0.65 | 576 | 10/6/2021 |
| 1.0.64 | 587 | 9/28/2021 |
| 1.0.63 | 515 | 9/23/2021 |
| 1.0.62 | 557 | 9/10/2021 |
| 1.0.61 | 585 | 9/9/2021 |
| 1.0.60 | 569 | 9/8/2021 |
| 1.0.59 | 553 | 9/8/2021 |
| 1.0.58 | 558 | 9/6/2021 |
| 1.0.57 | 555 | 8/31/2021 |
| 1.0.56 | 568 | 8/30/2021 |
| 1.0.55 | 664 | 7/31/2021 |
| 1.0.54 | 661 | 7/30/2021 |
| 1.0.53 | 719 | 7/26/2021 |
| 1.0.52 | 602 | 7/5/2021 |
| 1.0.51 | 650 | 7/1/2021 |
| 1.0.50 | 626 | 6/4/2021 |
| 1.0.49 | 623 | 4/26/2021 |
| 1.0.48 | 631 | 4/19/2021 |
| 1.0.47 | 607 | 4/7/2021 |
| 1.0.46 | 651 | 4/3/2021 |
| 1.0.45 | 648 | 3/22/2021 |
| 1.0.44 | 621 | 3/4/2021 |
| 1.0.43 | 636 | 2/26/2021 |
| 1.0.42 | 640 | 2/2/2021 |
| 1.0.41 | 676 | 1/24/2021 |
| 1.0.40 | 721 | 1/23/2021 |
| 1.0.39 | 649 | 1/20/2021 |
| 1.0.38 | 674 | 1/20/2021 |
| 1.0.37 | 620 | 1/18/2021 |
| 1.0.36 | 708 | 1/16/2021 |
| 1.0.35 | 682 | 12/16/2020 |
| 1.0.34 | 654 | 12/14/2020 |
| 1.0.33 | 613 | 12/9/2020 |
| 1.0.32 | 779 | 12/6/2020 |
| 1.0.31 | 694 | 12/2/2020 |
| 1.0.30 | 690 | 12/1/2020 |
| 1.0.29 | 732 | 11/12/2020 |
| 1.0.29-atestpub | 545 | 11/11/2020 |
| 1.0.28 | 742 | 10/11/2020 |
| 1.0.27 | 765 | 9/9/2020 |
| 1.0.26 | 754 | 9/3/2020 |
| 1.0.25 | 741 | 8/20/2020 |
| 1.0.24 | 804 | 8/9/2020 |
| 1.0.23 | 856 | 7/28/2020 |
| 1.0.22 | 808 | 7/19/2020 |
| 1.0.21 | 720 | 7/6/2020 |
| 1.0.20 | 829 | 6/6/2020 |
| 1.0.19 | 789 | 6/4/2020 |
| 1.0.18 | 749 | 5/29/2020 |
| 1.0.17 | 798 | 5/21/2020 |
| 1.0.16 | 793 | 5/17/2020 |
| 1.0.15 | 779 | 5/12/2020 |
| 1.0.14 | 796 | 5/4/2020 |
| 1.0.13 | 784 | 4/24/2020 |
| 1.0.12 | 752 | 4/22/2020 |
| 1.0.11 | 721 | 4/22/2020 |
| 1.0.10 | 765 | 4/21/2020 |
| 1.0.9 | 752 | 4/18/2020 |
| 1.0.8 | 774 | 4/16/2020 |
| 1.0.7 | 781 | 4/16/2020 |
| 1.0.6 | 739 | 4/15/2020 |
| 1.0.5 | 789 | 4/11/2020 |
| 1.0.4 | 843 | 4/3/2020 |
| 1.0.3 | 808 | 4/1/2020 |
| 1.0.2 | 790 | 3/27/2020 |
| 1.0.1 | 903 | 3/22/2020 |
| 1.0.0 | 870 | 3/22/2020 |