diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 3487467..32be250 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -27,12 +27,12 @@ jobs:
- uses: actions/setup-dotnet@v4
with:
dotnet-version: 9.x
- - name: Install dependencies
- run: dotnet restore
- - name: Build
- run: dotnet build --configuration Release --no-restore
+ - uses: gerlero/apt-install@v1
+ if: startsWith(matrix.os, 'ubuntu') == true
+ with:
+ packages: libhwy-dev liblcms2-dev libjpeg-turbo8-dev libpng-dev libjpeg-dev zlib1g-dev libgif-dev
- name: Test
- run: dotnet test --no-restore --configuration Release
+ run: dotnet test --no-restore --configuration Release --filter CI!=false --logger "console;verbosity=detailed"
- name: Upload Test Results
if: failure()
uses: actions/upload-artifact@v4
diff --git a/Compressonator.NET.Tests/Compressonator.NET.Tests.csproj b/Compressonator.NET.Tests/Compressonator.NET.Tests.csproj
index d3050a7..88e296d 100644
--- a/Compressonator.NET.Tests/Compressonator.NET.Tests.csproj
+++ b/Compressonator.NET.Tests/Compressonator.NET.Tests.csproj
@@ -11,6 +11,7 @@
+
diff --git a/Compressonator.NET.Tests/EnumTests.cs b/Compressonator.NET.Tests/EnumTests.cs
index d4218e5..ddea104 100644
--- a/Compressonator.NET.Tests/EnumTests.cs
+++ b/Compressonator.NET.Tests/EnumTests.cs
@@ -7,7 +7,8 @@ public class EnumTests
// If there's an enum mismatch, it can lead to unreliable undefined behavior.
// We test both classes, because they use different compilation chains, and could get out of sync.
[TestEnum(typeof(CMP_FORMAT))]
- [DataTestMethod]
+ [TestMethod]
+ [TestProperty("CI", "true")]
public void TestCMP_FORMAT(CMP_FORMAT format)
{
Assert.IsTrue(SDK_NativeMethods.CMP_IsValidFormat(format), $"CMP_Format must be valid for: {format}");
diff --git a/Compressonator.NET.Tests/GCAsset.cs b/Compressonator.NET.Tests/GCAsset.cs
new file mode 100644
index 0000000..bcb555c
--- /dev/null
+++ b/Compressonator.NET.Tests/GCAsset.cs
@@ -0,0 +1,30 @@
+namespace Compressonator.NET.Tests;
+
+// Please don't use this outside of tests.
+// Largely from: https://stackoverflow.com/a/9827349 + other answers on the same question.
+// Also: https://stackoverflow.com/questions/3829928/under-what-circumstances-we-need-to-call-gc-collect-twice from why we GC.Collect Twice.
+internal static class GCAssert
+{
+ public static void AssertGarbageCollectable(Func create)
+ where T : class
+ {
+ WeakReference reference = CreateAndDropReference(create);
+
+ GC.Collect();
+ GC.WaitForPendingFinalizers();
+ GC.Collect();
+
+ Assert.IsFalse(reference.IsAlive, $"{typeof(T).Name} should have been garbage collected");
+ }
+
+ private static WeakReference CreateAndDropReference(Func create)
+ where T : class
+ {
+ var obj = create();
+
+ if (obj is IDisposable disposable)
+ disposable.Dispose();
+
+ return new WeakReference(obj);
+ }
+}
diff --git a/Compressonator.NET.Tests/Marshaling/MarshalTests.TestStructs_t=Compressonator.NET.CMP_Texture.verified.txt b/Compressonator.NET.Tests/Marshaling/MarshalTests.TestStructs_t=Compressonator.NET.CMP_Texture.verified.txt
new file mode 100644
index 0000000..c59653a
--- /dev/null
+++ b/Compressonator.NET.Tests/Marshaling/MarshalTests.TestStructs_t=Compressonator.NET.CMP_Texture.verified.txt
@@ -0,0 +1,78 @@
+{
+ FieldName: CMP_Texture,
+ Size: 48,
+ Nested: [
+ {
+ FieldName: size,
+ Size: 4,
+ Type: UInt32
+ },
+ {
+ FieldName: width,
+ Size: 4,
+ Offset: 4,
+ Type: UInt32
+ },
+ {
+ FieldName: height,
+ Size: 4,
+ Offset: 8,
+ Type: UInt32
+ },
+ {
+ FieldName: pitch,
+ Size: 4,
+ Offset: 12,
+ Type: UInt32
+ },
+ {
+ FieldName: format,
+ Size: 4,
+ Offset: 16,
+ Type: CMP_FORMAT
+ },
+ {
+ FieldName: transcodeFormat,
+ Size: 4,
+ Offset: 20,
+ Type: CMP_FORMAT
+ },
+ {
+ FieldName: blockHeight,
+ Size: 1,
+ Offset: 24,
+ Type: Byte
+ },
+ {
+ FieldName: blockWidth,
+ Size: 1,
+ Offset: 25,
+ Type: Byte
+ },
+ {
+ FieldName: blockDepth,
+ Size: 1,
+ Offset: 26,
+ Type: Byte
+ },
+ {
+ FieldName: dataSize,
+ Size: 4,
+ Offset: 28,
+ Type: UInt32
+ },
+ {
+ FieldName: data,
+ Size: 8,
+ Offset: 32,
+ Type: IntPtr
+ },
+ {
+ FieldName: mipSet,
+ Size: 8,
+ Offset: 40,
+ Type: IntPtr
+ }
+ ],
+ Type: struct
+}
\ No newline at end of file
diff --git a/Compressonator.NET.Tests/Marshaling/MarshalTests.cs b/Compressonator.NET.Tests/Marshaling/MarshalTests.cs
index 6f6e6c0..e83ad6e 100644
--- a/Compressonator.NET.Tests/Marshaling/MarshalTests.cs
+++ b/Compressonator.NET.Tests/Marshaling/MarshalTests.cs
@@ -33,11 +33,11 @@ public void TestCMP_FORMAT()
[DataRow(12, typeof(KernelPerformanceStats))]
[DataRow(388, typeof(KernelDeviceInfo))]
[DataRow(48, typeof(AMD_CMD))]
+ [DataRow(48, typeof(CMP_Texture))]
[DataTestMethod]
public async Task TestStructs(int correctSize, Type t)
{
Assert.AreEqual(correctSize, Marshal.SizeOf(t), $"{nameof(t)} must marshal as {correctSize} bytes");
-
await VerifyMemoryLayout(t);
}
}
diff --git a/Compressonator.NET.Tests/NativeTests.cs b/Compressonator.NET.Tests/NativeTests.cs
index 9f90a61..0536f65 100644
--- a/Compressonator.NET.Tests/NativeTests.cs
+++ b/Compressonator.NET.Tests/NativeTests.cs
@@ -1,15 +1,20 @@
namespace Compressonator.NET.Tests;
+[TestCategory("Smoke")]
[TestClass]
public sealed class NativeTests
{
[TestMethod]
+ [Priority(1)]
+ [TestProperty("CI", "true")]
public void TestSDKNative()
{
Assert.IsTrue(SDK_NativeMethods.IsSupported, "SDK Native methods should be supported");
}
[TestMethod]
+ [Priority(1)]
+ [TestProperty("CI", "true")]
public void TestFrameworkNative()
{
Assert.IsTrue(FrameworkNativeMethods.IsSupported, "Framework Native Methods should be supported.");
@@ -18,9 +23,63 @@ public void TestFrameworkNative()
///
/// Test that this method just doesn't throw any exceptions.
///
+ [TestProperty("CI", "true")]
[TestMethod]
public void TestLibraryInitialization()
{
FrameworkNativeMethods.CMP_InitFramework();
}
+
+ [TestProperty("CI", "true")]
+ [TestMethod]
+ // This was causing some CI Crashes, let's write a test
+ public void TestFreeingMipMaps()
+ {
+ var mip = new CMP_MipSet();
+ mip.Dispose();
+
+ // We don't have any assertions, it just must not throw.
+ }
+
+ [TestProperty("CI", "true")]
+ [TestMethod]
+ // This was causing some CI Crashes, let's write a test
+ public void TestFreeingMipMapsMultipleTimes()
+ {
+ var mip = new CMP_MipSet();
+ FrameworkNativeMethods.CMP_FreeMipSet(mip);
+ FrameworkNativeMethods.CMP_FreeMipSet(mip);
+
+ // We don't have any assertions, it just must not throw.
+ }
+
+ [TestProperty("CI", "true")]
+ [TestMethod]
+ // This was causing some CI Crashes, let's write a test
+ public void TestMipSetDisposeTwice()
+ {
+ var mip = new CMP_MipSet();
+ mip.Dispose();
+ mip.Dispose();
+
+ // We don't have any assertions, it just must not throw.
+ }
+
+ // The structure of GCAssert.AssertGarbageCollectable requires a factory due to its use of WeakReferences
+ // This structure, wraps the factory in a nice way, that's easier to type :)
+ public static IEnumerable