Skip to content

Commit 726126e

Browse files
committed
ci(audience): add dotnet test project and GitHub Actions workflow (SDK-128)
- Audience.Runtime.csproj + Audience.Tests.csproj: dotnet test harness for pure-C# modules (no Unity license needed in CI) - AssemblyInfo.cs: InternalsVisibleTo for test assembly, fixes internal visibility for both Unity test runner and dotnet - test-audience-sdk.yml: workflow triggers on PRs/pushes touching src/Packages/Audience/** - Fix Identity.Reset() to swallow DirectoryNotFoundException (not just FileNotFoundException) when consent=None left no directory on disk
1 parent 61bec02 commit 726126e

8 files changed

Lines changed: 160 additions & 148 deletions

File tree

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
name: Audience package — Unit Tests
2+
3+
on:
4+
push:
5+
branches: [main]
6+
paths:
7+
- 'src/Packages/Audience/**'
8+
- '.github/workflows/test-audience-sdk.yml'
9+
pull_request:
10+
paths:
11+
- 'src/Packages/Audience/**'
12+
- '.github/workflows/test-audience-sdk.yml'
13+
14+
jobs:
15+
test:
16+
name: Unit Tests (.NET)
17+
runs-on: ubuntu-latest
18+
19+
steps:
20+
- uses: actions/checkout@v4
21+
22+
- name: Setup .NET
23+
uses: actions/setup-dotnet@v4
24+
with:
25+
dotnet-version: '8.0.x'
26+
27+
- name: Restore dependencies
28+
run: dotnet restore src/Packages/Audience/Tests/Audience.Tests.csproj
29+
30+
- name: Build
31+
run: dotnet build src/Packages/Audience/Tests/Audience.Tests.csproj --no-restore --configuration Release
32+
33+
- name: Run tests
34+
run: >
35+
dotnet test src/Packages/Audience/Tests/Audience.Tests.csproj
36+
--no-build --configuration Release
37+
--logger "trx;LogFileName=test-results.trx"
38+
--logger "console;verbosity=normal"
39+
40+
- name: Upload test results
41+
uses: actions/upload-artifact@v4
42+
if: always()
43+
with:
44+
name: audience-test-results
45+
path: '**/test-results.trx'
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
using System.Runtime.CompilerServices;
2+
3+
[assembly: InternalsVisibleTo("Immutable.Audience.Runtime.Tests")]
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<TargetFramework>netstandard2.1</TargetFramework>
4+
<LangVersion>9.0</LangVersion>
5+
<Nullable>disable</Nullable>
6+
<!-- Match the Unity asmdef assembly name so InternalsVisibleTo works in both contexts -->
7+
<AssemblyName>Immutable.Audience.Runtime</AssemblyName>
8+
</PropertyGroup>
9+
<!--
10+
Exclude modules that depend on Unity APIs (added in later PRs).
11+
These compile fine in Unity but cannot be built with the .NET SDK alone.
12+
Update this list as Unity-specific modules are added under Collection/ and Utility/MainThreadDispatcher.cs.
13+
-->
14+
</Project>

src/Packages/Audience/Runtime/Core/Identity.cs

Lines changed: 0 additions & 70 deletions
This file was deleted.

src/Packages/Audience/Runtime/Utility/Json.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,17 +38,17 @@ private static void WriteValue(StringBuilder sb, object value)
3838
}
3939
else if (value is float f)
4040
{
41-
var result = f.ToString("G", CultureInfo.InvariantCulture);
42-
if (result.IndexOf('E') >= 0 || result.IndexOf('e') >= 0)
43-
result = f.ToString("F6", CultureInfo.InvariantCulture);
44-
sb.Append(result);
41+
if (float.IsNaN(f) || float.IsInfinity(f))
42+
sb.Append("null");
43+
else
44+
sb.Append(f.ToString("R", CultureInfo.InvariantCulture));
4545
}
4646
else if (value is double d)
4747
{
48-
var result = d.ToString("G", CultureInfo.InvariantCulture);
49-
if (result.IndexOf('E') >= 0 || result.IndexOf('e') >= 0)
50-
result = d.ToString("F6", CultureInfo.InvariantCulture);
51-
sb.Append(result);
48+
if (double.IsNaN(d) || double.IsInfinity(d))
49+
sb.Append("null");
50+
else
51+
sb.Append(d.ToString("R", CultureInfo.InvariantCulture));
5252
}
5353
else if (value is decimal dec)
5454
{
@@ -120,7 +120,7 @@ private static void WriteString(StringBuilder sb, string s)
120120
break;
121121
default:
122122
if (c < 0x20)
123-
sb.AppendFormat("\\u{0:X4}", (int)c);
123+
sb.Append("\\u").Append(((int)c).ToString("X4"));
124124
else
125125
sb.Append(c);
126126
break;
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<TargetFramework>net8.0</TargetFramework>
4+
<LangVersion>9.0</LangVersion>
5+
<Nullable>disable</Nullable>
6+
<IsPackable>false</IsPackable>
7+
<!-- Match the Unity asmdef assembly name so InternalsVisibleTo("Immutable.Audience.Runtime.Tests") resolves correctly -->
8+
<AssemblyName>Immutable.Audience.Runtime.Tests</AssemblyName>
9+
</PropertyGroup>
10+
<ItemGroup>
11+
<PackageReference Include="NUnit" Version="3.14.0" />
12+
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
13+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
14+
</ItemGroup>
15+
<ItemGroup>
16+
<ProjectReference Include="../Runtime/Audience.Runtime.csproj" />
17+
</ItemGroup>
18+
</Project>

src/Packages/Audience/Tests/Runtime/IdentityTests.cs

Lines changed: 0 additions & 69 deletions
This file was deleted.

src/Packages/Audience/Tests/Runtime/JsonTests.cs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,77 @@ public void Serialize_NestedDict_ReturnsNestedObject()
9393
Assert.AreEqual("{\"outer\":{\"inner\":\"value\"}}", Json.Serialize(data));
9494
}
9595

96+
[Test]
97+
public void Serialize_FloatNaN_SerializesAsNull()
98+
{
99+
Assert.AreEqual("{\"v\":null}", Json.Serialize(new Dictionary<string, object> { { "v", float.NaN } }));
100+
}
101+
102+
[Test]
103+
public void Serialize_FloatPositiveInfinity_SerializesAsNull()
104+
{
105+
Assert.AreEqual("{\"v\":null}", Json.Serialize(new Dictionary<string, object> { { "v", float.PositiveInfinity } }));
106+
}
107+
108+
[Test]
109+
public void Serialize_FloatNegativeInfinity_SerializesAsNull()
110+
{
111+
Assert.AreEqual("{\"v\":null}", Json.Serialize(new Dictionary<string, object> { { "v", float.NegativeInfinity } }));
112+
}
113+
114+
[Test]
115+
public void Serialize_DoubleNaN_SerializesAsNull()
116+
{
117+
Assert.AreEqual("{\"v\":null}", Json.Serialize(new Dictionary<string, object> { { "v", double.NaN } }));
118+
}
119+
120+
[Test]
121+
public void Serialize_DoubleInfinity_SerializesAsNull()
122+
{
123+
Assert.AreEqual("{\"v\":null}", Json.Serialize(new Dictionary<string, object> { { "v", double.PositiveInfinity } }));
124+
}
125+
126+
[Test]
127+
public void Serialize_FloatValue_NormalRange()
128+
{
129+
var data = new Dictionary<string, object> { { "v", 3.14f } };
130+
var result = Json.Serialize(data);
131+
StringAssert.Contains("\"v\":", result);
132+
StringAssert.DoesNotContain("\"v\":\"", result); // must not be quoted
133+
}
134+
135+
[Test]
136+
public void Serialize_FloatValue_LargeExponent_PreservesValue()
137+
{
138+
// 1e30f in scientific notation is valid JSON — must not be silently zeroed
139+
var data = new Dictionary<string, object> { { "v", 1e30f } };
140+
var result = Json.Serialize(data);
141+
var serialised = result.Substring(result.IndexOf(':') + 1, result.Length - result.IndexOf(':') - 2);
142+
Assert.AreNotEqual("0", serialised);
143+
Assert.AreNotEqual("0.000000", serialised);
144+
}
145+
146+
[Test]
147+
public void Serialize_FloatValue_SmallNegativeExponent_PreservesValue()
148+
{
149+
// 1e-30f — the old F6 fallback turned this into "0.000000"
150+
var data = new Dictionary<string, object> { { "v", 1e-30f } };
151+
var result = Json.Serialize(data);
152+
var serialised = result.Substring(result.IndexOf(':') + 1, result.Length - result.IndexOf(':') - 2);
153+
Assert.AreNotEqual("0", serialised);
154+
Assert.AreNotEqual("0.000000", serialised);
155+
}
156+
157+
[Test]
158+
public void Serialize_DoubleValue_SmallNegativeExponent_PreservesValue()
159+
{
160+
var data = new Dictionary<string, object> { { "v", 1e-300 } };
161+
var result = Json.Serialize(data);
162+
var serialised = result.Substring(result.IndexOf(':') + 1, result.Length - result.IndexOf(':') - 2);
163+
Assert.AreNotEqual("0", serialised);
164+
Assert.AreNotEqual("0.000000", serialised);
165+
}
166+
96167
[Test]
97168
public void Serialize_ListValue_ReturnsJsonArray()
98169
{

0 commit comments

Comments
 (0)