Skip to content

Commit 2bedad6

Browse files
authored
Enable Native AOT compatibility (trimming support)
This is a squashed merge of PR #1070.
1 parent bb478ee commit 2bedad6

10 files changed

+456
-56
lines changed
+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net8.0</TargetFramework>
5+
<OutputType>exe</OutputType>
6+
<IsPackable>false</IsPackable>
7+
<PublishAot>true</PublishAot>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<PackageReference Include="MSTest.Engine" Version="1.0.0-alpha.24266.1" />
12+
<PackageReference Include="MSTest.SourceGeneration" Version="1.0.0-alpha.24266.1" />
13+
<PackageReference Include="Microsoft.CodeCoverage.MSBuild" Version="17.10.4" />
14+
<PackageReference Include="Microsoft.Testing.Extensions.CodeCoverage" Version="17.10.4" />
15+
<PackageReference Include="Microsoft.Testing.Extensions.TrxReport" Version="1.2.0" />
16+
<PackageReference Include="Microsoft.Testing.Platform.MSBuild" Version="1.2.0" />
17+
<PackageReference Include="MSTest.TestFramework" Version="3.3.1" />
18+
<PackageReference Include="MSTest.Analyzers" Version="3.3.1">
19+
<PrivateAssets>all</PrivateAssets>
20+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
21+
</PackageReference>
22+
</ItemGroup>
23+
24+
<ItemGroup>
25+
<ProjectReference Include="..\MoreLinq\MoreLinq.csproj" />
26+
</ItemGroup>
27+
28+
<ItemGroup>
29+
<Using Include="Microsoft.VisualStudio.TestTools.UnitTesting" />
30+
</ItemGroup>
31+
32+
</Project>

MoreLinq.Test.Aot/ToDataTableTest.cs

+235
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
#region License and Terms
2+
// MoreLINQ - Extensions to LINQ to Objects
3+
// Copyright (c) 2024 Atif Aziz. All rights reserved.
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
#endregion
17+
18+
namespace MoreLinq.Test
19+
{
20+
using System;
21+
using System.Collections;
22+
using System.Collections.Generic;
23+
using System.Data;
24+
using System.Diagnostics.CodeAnalysis;
25+
using System.Linq;
26+
using System.Linq.Expressions;
27+
28+
[TestClass]
29+
public class ToDataTableTest
30+
{
31+
sealed class TestObject(int key)
32+
{
33+
public int KeyField = key;
34+
public Guid? ANullableGuidField = Guid.NewGuid();
35+
36+
public string AString { get; } = "ABCDEFGHIKKLMNOPQRSTUVWXYSZ";
37+
public decimal? ANullableDecimal { get; } = key / 3;
38+
public object Unreadable { set => throw new NotImplementedException(); }
39+
40+
public object this[int index] { get => new(); set { } }
41+
42+
public override string ToString() => nameof(TestObject);
43+
}
44+
45+
readonly IReadOnlyCollection<TestObject> testObjects;
46+
47+
public ToDataTableTest() =>
48+
this.testObjects = Enumerable.Range(0, 3)
49+
.Select(i => new TestObject(i))
50+
.ToArray();
51+
52+
[TestMethod]
53+
public void ToDataTableNullMemberExpressionMethod()
54+
{
55+
Expression<Func<TestObject, object?>>? expression = null;
56+
57+
[UnconditionalSuppressMessage("Aot", "IL2026")]
58+
void Act() => _ = this.testObjects.ToDataTable(expression!);
59+
60+
var e = Assert.ThrowsException<ArgumentException>(Act);
61+
Assert.AreEqual("expressions", e.ParamName);
62+
}
63+
64+
[TestMethod]
65+
public void ToDataTableTableWithWrongColumnNames()
66+
{
67+
using var dt = new DataTable();
68+
_ = dt.Columns.Add("Test");
69+
70+
[UnconditionalSuppressMessage("Aot", "IL2026")]
71+
void Act() => _ = this.testObjects.ToDataTable(dt);
72+
73+
var e = Assert.ThrowsException<ArgumentException>(Act);
74+
Assert.AreEqual("table", e.ParamName);
75+
}
76+
77+
[TestMethod]
78+
public void ToDataTableTableWithWrongColumnDataType()
79+
{
80+
using var dt = new DataTable();
81+
_ = dt.Columns.Add("AString", typeof(int));
82+
83+
[UnconditionalSuppressMessage("Aot", "IL2026")]
84+
void Act() => _ = this.testObjects.ToDataTable(dt, t => t.AString);
85+
86+
var e = Assert.ThrowsException<ArgumentException>(Act);
87+
Assert.AreEqual("table", e.ParamName);
88+
}
89+
90+
void TestDataTableMemberExpression(Expression<Func<TestObject, object?>> expression)
91+
{
92+
[UnconditionalSuppressMessage("Aot", "IL2026")]
93+
void Act() => _ = this.testObjects.ToDataTable(expression);
94+
95+
var e = Assert.ThrowsException<ArgumentException>(Act);
96+
Assert.AreEqual("expressions", e.ParamName);
97+
var innerException = e.InnerException;
98+
Assert.IsNotNull(innerException);
99+
Assert.IsInstanceOfType<ArgumentException>(innerException);
100+
Assert.AreEqual("lambda", ((ArgumentException)innerException).ParamName);
101+
}
102+
103+
[TestMethod]
104+
public void ToDataTableMemberExpressionMethod()
105+
{
106+
TestDataTableMemberExpression(t => t.ToString());
107+
}
108+
109+
[TestMethod]
110+
public void ToDataTableMemberExpressionNonMember()
111+
{
112+
TestDataTableMemberExpression(t => t.ToString().Length);
113+
}
114+
115+
[TestMethod]
116+
public void ToDataTableMemberExpressionIndexer()
117+
{
118+
TestDataTableMemberExpression(t => t[0]);
119+
}
120+
121+
[TestMethod]
122+
public void ToDataTableMemberExpressionStatic()
123+
{
124+
TestDataTableMemberExpression(_ => DateTime.Now);
125+
}
126+
127+
[TestMethod]
128+
public void ToDataTableSchemaInDeclarationOrder()
129+
{
130+
[UnconditionalSuppressMessage("Aot", "IL2026")]
131+
DataTable Act() => this.testObjects.ToDataTable();
132+
133+
var dt = Act();
134+
135+
// Assert properties first, then fields, then in declaration order
136+
137+
Assert.AreEqual("KeyField", dt.Columns[2].Caption);
138+
Assert.AreEqual(typeof(int), dt.Columns[2].DataType);
139+
140+
Assert.AreEqual("ANullableGuidField", dt.Columns[3].Caption);
141+
Assert.AreEqual(typeof(Guid), dt.Columns[3].DataType);
142+
Assert.IsTrue(dt.Columns[3].AllowDBNull);
143+
144+
Assert.AreEqual("AString", dt.Columns[0].Caption);
145+
Assert.AreEqual(typeof(string), dt.Columns[0].DataType);
146+
147+
Assert.AreEqual("ANullableDecimal", dt.Columns[1].Caption);
148+
Assert.AreEqual(typeof(decimal), dt.Columns[1].DataType);
149+
150+
Assert.AreEqual(4, dt.Columns.Count);
151+
}
152+
153+
[TestMethod]
154+
public void ToDataTableContainsAllElements()
155+
{
156+
[UnconditionalSuppressMessage("Aot", "IL2026")]
157+
DataTable Act() => this.testObjects.ToDataTable();
158+
159+
var dt = Act();
160+
161+
Assert.AreEqual(this.testObjects.Count, dt.Rows.Count);
162+
}
163+
164+
[TestMethod]
165+
public void ToDataTableWithExpression()
166+
{
167+
[UnconditionalSuppressMessage("Aot", "IL2026")]
168+
DataTable Act() => this.testObjects.ToDataTable(t => t.AString);
169+
170+
var dt = Act();
171+
172+
Assert.AreEqual("AString", dt.Columns[0].Caption);
173+
Assert.AreEqual(typeof(string), dt.Columns[0].DataType);
174+
175+
Assert.AreEqual(1, dt.Columns.Count);
176+
}
177+
178+
[TestMethod]
179+
public void ToDataTableWithSchema()
180+
{
181+
using var dt = new DataTable();
182+
var columns = dt.Columns;
183+
_ = columns.Add("Column1", typeof(int));
184+
_ = columns.Add("Value", typeof(string));
185+
_ = columns.Add("Column3", typeof(int));
186+
_ = columns.Add("Name", typeof(string));
187+
188+
var vars = Environment.GetEnvironmentVariables()
189+
.Cast<DictionaryEntry>()
190+
.ToArray();
191+
192+
[UnconditionalSuppressMessage("Aot", "IL2026")]
193+
void Act() => _ = vars.Select(e => new { Name = e.Key.ToString(), Value = e.Value!.ToString() })
194+
.ToDataTable(dt, e => e.Name, e => e.Value);
195+
196+
Act();
197+
198+
var rows = dt.Rows.Cast<DataRow>().ToArray();
199+
Assert.AreEqual(vars.Length, rows.Length);
200+
CollectionAssert.AreEqual(vars.Select(e => e.Key).ToArray(), rows.Select(r => r["Name"]).ToArray());
201+
CollectionAssert.AreEqual(vars.Select(e => e.Value).ToArray(), rows.Select(r => r["Value"]).ToArray());
202+
}
203+
204+
readonly struct Point(int x, int y)
205+
{
206+
#pragma warning disable CA1805 // Do not initialize unnecessarily (avoids CS0649)
207+
public static Point Empty = new();
208+
#pragma warning restore CA1805 // Do not initialize unnecessarily
209+
public bool IsEmpty => X == 0 && Y == 0;
210+
public int X { get; } = x;
211+
public int Y { get; } = y;
212+
}
213+
214+
[TestMethod]
215+
public void ToDataTableIgnoresStaticMembers()
216+
{
217+
[UnconditionalSuppressMessage("Aot", "IL2026")]
218+
static DataTable Act() => new[] { new Point(12, 34) }.ToDataTable();
219+
220+
var points = Act();
221+
222+
Assert.AreEqual(3, points.Columns.Count);
223+
var x = points.Columns["X"];
224+
var y = points.Columns["Y"];
225+
var empty = points.Columns["IsEmpty"];
226+
Assert.IsNotNull(x);
227+
Assert.IsNotNull(y);
228+
Assert.IsNotNull(empty);
229+
var row = points.Rows.Cast<DataRow>().Single();
230+
Assert.AreEqual(12, row[x]);
231+
Assert.AreEqual(34, row[y]);
232+
Assert.AreEqual(row[empty], false);
233+
}
234+
}
235+
}

MoreLinq.Test/Throws.cs

+6-3
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,19 @@ public static ExactTypeConstraint TypeOf<T>()
3636
NUnit.Framework.Throws.TypeOf<T>();
3737

3838
public static EqualConstraint ArgumentException(string expectedParamName) =>
39-
NUnit.Framework.Throws.ArgumentException.With.ParamName().EqualTo(expectedParamName);
39+
NUnit.Framework.Throws.ArgumentException.With.ParamName(expectedParamName);
4040

4141
public static EqualConstraint ArgumentNullException(string expectedParamName) =>
42-
NUnit.Framework.Throws.ArgumentNullException.With.ParamName().EqualTo(expectedParamName);
42+
NUnit.Framework.Throws.ArgumentNullException.With.ParamName(expectedParamName);
4343

4444
public static ExactTypeConstraint ArgumentOutOfRangeException() =>
4545
TypeOf<ArgumentOutOfRangeException>();
4646

4747
public static EqualConstraint ArgumentOutOfRangeException(string expectedParamName) =>
48-
ArgumentOutOfRangeException().With.ParamName().EqualTo(expectedParamName);
48+
ArgumentOutOfRangeException().With.ParamName(expectedParamName);
49+
50+
public static EqualConstraint ParamName(this ConstraintExpression constraint, string expectedParamName) =>
51+
constraint.ParamName().EqualTo(expectedParamName);
4952

5053
static ResolvableConstraintExpression ParamName(this ConstraintExpression constraint) =>
5154
constraint.Property(nameof(System.ArgumentException.ParamName));

MoreLinq.Test/ToDataTableTest.cs

+17-15
Original file line numberDiff line numberDiff line change
@@ -77,33 +77,35 @@ public void ToDataTableTableWithWrongColumnDataType()
7777
Throws.ArgumentException("table"));
7878
}
7979

80+
void TestDataTableMemberExpression(Expression<Func<TestObject, object?>> expression)
81+
{
82+
Assert.That(() => this.testObjects.ToDataTable(expression),
83+
Throws.ArgumentException("expressions")
84+
.And.InnerException.With.ParamName("lambda"));
85+
}
86+
8087
[Test]
8188
public void ToDataTableMemberExpressionMethod()
8289
{
83-
Assert.That(() => this.testObjects.ToDataTable(t => t.ToString()),
84-
Throws.ArgumentException("lambda"));
90+
TestDataTableMemberExpression(t => t.ToString());
8591
}
8692

87-
8893
[Test]
8994
public void ToDataTableMemberExpressionNonMember()
9095
{
91-
Assert.That(() => this.testObjects.ToDataTable(t => t.ToString().Length),
92-
Throws.ArgumentException("lambda"));
96+
TestDataTableMemberExpression(t => t.ToString().Length);
9397
}
9498

9599
[Test]
96100
public void ToDataTableMemberExpressionIndexer()
97101
{
98-
Assert.That(() => this.testObjects.ToDataTable(t => t[0]),
99-
Throws.ArgumentException("lambda"));
102+
TestDataTableMemberExpression(t => t[0]);
100103
}
101104

102105
[Test]
103106
public void ToDataTableMemberExpressionStatic()
104107
{
105-
Assert.That(() => _ = this.testObjects.ToDataTable(_ => DateTime.Now),
106-
Throws.ArgumentException("lambda"));
108+
TestDataTableMemberExpression(_ => DateTime.Now);
107109
}
108110

109111
[Test]
@@ -113,19 +115,19 @@ public void ToDataTableSchemaInDeclarationOrder()
113115

114116
// Assert properties first, then fields, then in declaration order
115117

116-
Assert.That(dt.Columns[0].Caption, Is.EqualTo("AString"));
117-
Assert.That(dt.Columns[0].DataType, Is.EqualTo(typeof(string)));
118-
119-
Assert.That(dt.Columns[1].Caption, Is.EqualTo("ANullableDecimal"));
120-
Assert.That(dt.Columns[1].DataType, Is.EqualTo(typeof(decimal)));
121-
122118
Assert.That(dt.Columns[2].Caption, Is.EqualTo("KeyField"));
123119
Assert.That(dt.Columns[2].DataType, Is.EqualTo(typeof(int)));
124120

125121
Assert.That(dt.Columns[3].Caption, Is.EqualTo("ANullableGuidField"));
126122
Assert.That(dt.Columns[3].DataType, Is.EqualTo(typeof(Guid)));
127123
Assert.That(dt.Columns[3].AllowDBNull, Is.True);
128124

125+
Assert.That(dt.Columns[0].Caption, Is.EqualTo("AString"));
126+
Assert.That(dt.Columns[0].DataType, Is.EqualTo(typeof(string)));
127+
128+
Assert.That(dt.Columns[1].Caption, Is.EqualTo("ANullableDecimal"));
129+
Assert.That(dt.Columns[1].DataType, Is.EqualTo(typeof(decimal)));
130+
129131
Assert.That(dt.Columns.Count, Is.EqualTo(4));
130132
}
131133

MoreLinq.sln

+6
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MoreLinq.Test", "MoreLinq.T
2525
EndProject
2626
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MoreLinq.ExtensionsGenerator", "bld\ExtensionsGenerator\MoreLinq.ExtensionsGenerator.csproj", "{5FA8F0E8-648A-4C4F-B1BB-B0C46959A36E}"
2727
EndProject
28+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MoreLinq.Test.Aot", "MoreLinq.Test.Aot\MoreLinq.Test.Aot.csproj", "{776973A3-AC2E-423E-8106-B4E296CE7752}"
29+
EndProject
2830
Global
2931
GlobalSection(SolutionConfigurationPlatforms) = preSolution
3032
Debug|Any CPU = Debug|Any CPU
@@ -43,6 +45,10 @@ Global
4345
{5FA8F0E8-648A-4C4F-B1BB-B0C46959A36E}.Debug|Any CPU.Build.0 = Debug|Any CPU
4446
{5FA8F0E8-648A-4C4F-B1BB-B0C46959A36E}.Release|Any CPU.ActiveCfg = Release|Any CPU
4547
{5FA8F0E8-648A-4C4F-B1BB-B0C46959A36E}.Release|Any CPU.Build.0 = Release|Any CPU
48+
{776973A3-AC2E-423E-8106-B4E296CE7752}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
49+
{776973A3-AC2E-423E-8106-B4E296CE7752}.Debug|Any CPU.Build.0 = Debug|Any CPU
50+
{776973A3-AC2E-423E-8106-B4E296CE7752}.Release|Any CPU.ActiveCfg = Release|Any CPU
51+
{776973A3-AC2E-423E-8106-B4E296CE7752}.Release|Any CPU.Build.0 = Release|Any CPU
4652
EndGlobalSection
4753
GlobalSection(SolutionProperties) = preSolution
4854
HideSolutionNode = FALSE

0 commit comments

Comments
 (0)