diff --git a/test/Dapper.AOT.Test.Integration/Dapper.AOT.Test.Integration.csproj b/test/Dapper.AOT.Test.Integration/Dapper.AOT.Test.Integration.csproj index d1013c0..89f817e 100644 --- a/test/Dapper.AOT.Test.Integration/Dapper.AOT.Test.Integration.csproj +++ b/test/Dapper.AOT.Test.Integration/Dapper.AOT.Test.Integration.csproj @@ -9,9 +9,14 @@ - - - + + + + + + InterceptionExecutables/IncludedTypes/Poco.txt + PreserveNewest + diff --git a/test/Dapper.AOT.Test.Integration/DbStringTests.cs b/test/Dapper.AOT.Test.Integration/DbStringTests.cs index 829782e..cfe3a99 100644 --- a/test/Dapper.AOT.Test.Integration/DbStringTests.cs +++ b/test/Dapper.AOT.Test.Integration/DbStringTests.cs @@ -1,4 +1,5 @@ using System.Data; +using System.Diagnostics; using System.Threading.Tasks; using Dapper.AOT.Test.Integration.Setup; using Xunit; @@ -12,11 +13,11 @@ public class DbStringTests : InterceptedCodeExecutionTestsBase public DbStringTests(PostgresqlFixture fixture, ITestOutputHelper log) : base(fixture, log) { Fixture.NpgsqlConnection.Execute(""" - CREATE TABLE IF NOT EXISTS dbStringTable( + CREATE TABLE IF NOT EXISTS dbStringTestsTable( id integer PRIMARY KEY, - name varchar(40) NOT NULL CHECK (name <> '') + name varchar(40) NOT NULL ); - TRUNCATE dbStringTable; + TRUNCATE dbStringTestsTable; """); } @@ -24,9 +25,18 @@ name varchar(40) NOT NULL CHECK (name <> '') [DapperAot] public async Task Test() { + await Fixture.NpgsqlConnection.ExecuteAsync(""" + INSERT INTO dbStringTestsTable(id, name) + VALUES (1, 'me testing!') + """); + var sourceCode = PrepareSourceCodeFromFile("DbString"); - var executionResults = BuildAndExecuteInterceptedUserCode(sourceCode, methodName: "ExecuteAsync"); - - // TODO DO THE CHECK HERE + var executionResults = BuildAndExecuteInterceptedUserCode(sourceCode, methodName: "Execute"); + + var list = Trace.Listeners; + Trace.Write("qwe"); + + Assert.NotNull(executionResults); + // TODO check that stack trace contains call to `Dapper.Aot.Generated.DbStringHelpers`. probably can be done like here https://stackoverflow.com/a/33939304 } } \ No newline at end of file diff --git a/test/Dapper.AOT.Test.Integration/InterceptionExecutables/DbString.cs b/test/Dapper.AOT.Test.Integration/InterceptionExecutables/DbString.cs index aae57a2..76d009f 100644 --- a/test/Dapper.AOT.Test.Integration/InterceptionExecutables/DbString.cs +++ b/test/Dapper.AOT.Test.Integration/InterceptionExecutables/DbString.cs @@ -8,13 +8,29 @@ namespace InterceptionExecutables using System.IO; using Dapper; using System.Threading.Tasks; - + using InterceptionExecutables.IncludedTypes; + + [DapperAot] // Enabling Dapper AOT! public static class Program { - public static async Task ExecuteAsync(IDbConnection dbConnection) + public static Poco Execute(IDbConnection dbConnection) + => ExecuteAsync(dbConnection).GetAwaiter().GetResult(); + + public static async Task ExecuteAsync(IDbConnection dbConnection) { - var res = await dbConnection.QueryAsync("SELECT count(*) FROM dbStringTable"); - return res.First(); + var results = await dbConnection.QueryAsync("select * from dbStringTestsTable where id = @Id and Name = @Name", new + { + Name = new DbString + { + Value = "me testing!", + IsFixedLength = false, + Length = 11 + }, + + Id = 1, + }); + + return results.First(); } } } \ No newline at end of file diff --git a/test/Dapper.AOT.Test.Integration/InterceptionExecutables/IncludedTypes/Poco.cs b/test/Dapper.AOT.Test.Integration/InterceptionExecutables/IncludedTypes/Poco.cs new file mode 100644 index 0000000..224b84e --- /dev/null +++ b/test/Dapper.AOT.Test.Integration/InterceptionExecutables/IncludedTypes/Poco.cs @@ -0,0 +1,7 @@ +namespace InterceptionExecutables.IncludedTypes; + +public class Poco +{ + public int Id { get; set; } + public string Name { get; set; } +} \ No newline at end of file diff --git a/test/Dapper.AOT.Test.Integration/InterceptionExecutables/_Template.cs b/test/Dapper.AOT.Test.Integration/InterceptionExecutables/_Template.cs index 9df4023..f083508 100644 --- a/test/Dapper.AOT.Test.Integration/InterceptionExecutables/_Template.cs +++ b/test/Dapper.AOT.Test.Integration/InterceptionExecutables/_Template.cs @@ -4,8 +4,9 @@ using System.IO; using Dapper; using System.Threading.Tasks; + using InterceptionExecutables.IncludedTypes; - // this is just a sample for easy test-writing + [DapperAot] // Enabling Dapper AOT! public static class Program { public static async Task ExecuteAsync(IDbConnection dbConnection) diff --git a/test/Dapper.AOT.Test.Integration/Setup/InterceptedCodeExecutionTestsBase.cs b/test/Dapper.AOT.Test.Integration/Setup/InterceptedCodeExecutionTestsBase.cs index 1b5408a..9b60163 100644 --- a/test/Dapper.AOT.Test.Integration/Setup/InterceptedCodeExecutionTestsBase.cs +++ b/test/Dapper.AOT.Test.Integration/Setup/InterceptedCodeExecutionTestsBase.cs @@ -6,7 +6,7 @@ using System.Reflection; using System.Runtime.InteropServices; using System.Text; -using System.Threading.Tasks; +using System.Text.Json; using Dapper.CodeAnalysis; using Dapper.TestCommon; using Microsoft.CodeAnalysis; @@ -15,7 +15,6 @@ using Microsoft.CodeAnalysis.Text; using Xunit; using Xunit.Abstractions; -using Xunit.Sdk; namespace Dapper.AOT.Test.Integration.Setup; @@ -41,23 +40,40 @@ protected static string PrepareSourceCodeFromFile(string inputFileName, string e } protected T BuildAndExecuteInterceptedUserCode( - string userSourceCode, + string sourceCode, string className = "Program", - string methodName = "ExecuteAsync") + string methodName = "Execute") { - var inputCompilation = RoslynTestHelpers.CreateCompilation("Assembly", syntaxTrees: [ - BuildInterceptorSupportedSyntaxTree(filename: "Program.cs", userSourceCode) - ]); + var syntaxTrees = new List + { + RoslynTestHelpers.CreateSyntaxTree(sourceCode, fileName: $"{className}.cs") + }; - var diagnosticsOutputStringBuilder = new StringBuilder(); - var (compilation, generatorDriverRunResult, diagnostics, errorCount) = Execute(inputCompilation, diagnosticsOutputStringBuilder, initializer: g => + var includedTypesPath = Path.Combine("InterceptionExecutables", "IncludedTypes"); + foreach (var file in Directory.GetFiles(includedTypesPath, "*.txt")) // we have copied all same files but in txt format not to have ambiguos references { - g.Log += message => Log(message); - }); + syntaxTrees.Add(RoslynTestHelpers.CreateSyntaxTree(File.ReadAllText(file), fileName: Path.GetFileName(file))); + } + + var inputCompilation = RoslynTestHelpers.CreateCompilation(assemblyName: "MyAssembly", syntaxTrees); + + var diagnosticsOutputStringBuilder = new StringBuilder(); + var (compilation, generatorDriverRunResult, diagnostics, errorCount) = Execute( + inputCompilation, + diagnosticsOutputStringBuilder, + initializer: g => { g.Log += message => Log(message); } + ); var results = Assert.Single(generatorDriverRunResult.Results); + Log($$""" + Generated code: + --- + {{results.GeneratedSources.First().SourceText}} + --- + """); + Assert.NotNull(compilation); - Assert.True(errorCount == 0, "User code should not report errors"); + Assert.True(errorCount == 0, $"Compilation errors: {diagnosticsOutputStringBuilder}"); var assembly = Compile(compilation!); var type = assembly.GetTypes().Single(t => t.FullName == $"InterceptionExecutables.{className}"); @@ -66,12 +82,9 @@ protected T BuildAndExecuteInterceptedUserCode( var result = mainMethod!.Invoke(obj: null, [ Fixture.NpgsqlConnection ]); Assert.NotNull(result); - if (result is not Task taskResult) - { - throw new XunitException($"expected execution result is '{typeof(Task)}' but got {result!.GetType()}"); - } - return taskResult.GetAwaiter().GetResult(); + var data = JsonSerializer.Serialize(result); + return JsonSerializer.Deserialize(data)!; } SyntaxTree BuildInterceptorSupportedSyntaxTree(string filename, string text) @@ -123,18 +136,14 @@ static void TryThrowErrors(IEnumerable items) class CompilationException : Exception { - public IEnumerable Errors { get; private set; } - public CompilationException(IEnumerable errors) : base(string.Join(Environment.NewLine, errors)) { - this.Errors = errors; } public CompilationException(params string[] errors) : base(string.Join(Environment.NewLine, errors)) { - this.Errors = errors; } } } \ No newline at end of file diff --git a/test/Dapper.AOT.Test/GeneratorTestBase.cs b/test/Dapper.AOT.Test/GeneratorTestBase.cs index acec3af..d8095f6 100644 --- a/test/Dapper.AOT.Test/GeneratorTestBase.cs +++ b/test/Dapper.AOT.Test/GeneratorTestBase.cs @@ -32,7 +32,8 @@ protected GeneratorTestBase(ITestOutputHelper? log) // input from https://github.com/dotnet/roslyn/blob/main/docs/features/source-generators.cookbook.md#unit-testing-of-generators - protected (Compilation? Compilation, GeneratorDriverRunResult Result, ImmutableArray Diagnostics, int ErrorCount) Execute(string source, + protected (Compilation? Compilation, GeneratorDriverRunResult Result, ImmutableArray Diagnostics, int ErrorCount) Execute( + string source, StringBuilder? diagnosticsTo = null, [CallerMemberName] string? name = null, string? fileName = null, diff --git a/test/Dapper.AOT.Test/TestCommon/RoslynTestHelpers.cs b/test/Dapper.AOT.Test/TestCommon/RoslynTestHelpers.cs index abb8d53..cd76f1b 100644 --- a/test/Dapper.AOT.Test/TestCommon/RoslynTestHelpers.cs +++ b/test/Dapper.AOT.Test/TestCommon/RoslynTestHelpers.cs @@ -12,6 +12,7 @@ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Serialization; +using System.Text; using System.Threading.Tasks; namespace Dapper.TestCommon; @@ -43,44 +44,23 @@ public static class RoslynTestHelpers "RELEASE", #endif }) - .WithFeatures(new[] { DapperInterceptorGenerator.FeatureKeys.InterceptorsPreviewNamespacePair }); + .WithFeatures([ DapperInterceptorGenerator.FeatureKeys.InterceptorsPreviewNamespacePair ]); + + public static SyntaxTree CreateSyntaxTree(string source, string fileName) + => CSharpSyntaxTree.ParseText(source, ParseOptionsLatestLangVer, encoding: Encoding.UTF8).WithFilePath(fileName); - public static Compilation CreateCompilation(string assemblyName, SyntaxTree[] syntaxTrees, OutputKind outputKind = OutputKind.DynamicallyLinkedLibrary) - => CSharpCompilation.Create(assemblyName, - syntaxTrees: syntaxTrees, - references: new[] { - MetadataReference.CreateFromFile(typeof(Binder).Assembly.Location), -#if !NET48 - MetadataReference.CreateFromFile(Assembly.Load("System.Runtime").Location), - MetadataReference.CreateFromFile(Assembly.Load("System.Data").Location), - MetadataReference.CreateFromFile(Assembly.Load("netstandard").Location), - MetadataReference.CreateFromFile(Assembly.Load("System.Collections").Location), - MetadataReference.CreateFromFile(typeof(System.ComponentModel.DataAnnotations.Schema.ColumnAttribute).Assembly.Location), -#endif - MetadataReference.CreateFromFile(typeof(Console).Assembly.Location), - MetadataReference.CreateFromFile(typeof(DbConnection).Assembly.Location), - MetadataReference.CreateFromFile(typeof(System.Data.SqlClient.SqlConnection).Assembly.Location), - MetadataReference.CreateFromFile(typeof(Microsoft.Data.SqlClient.SqlConnection).Assembly.Location), - MetadataReference.CreateFromFile(typeof(OracleConnection).Assembly.Location), - MetadataReference.CreateFromFile(typeof(ValueTask).Assembly.Location), - MetadataReference.CreateFromFile(typeof(Component).Assembly.Location), - MetadataReference.CreateFromFile(typeof(DapperAotExtensions).Assembly.Location), - MetadataReference.CreateFromFile(typeof(SqlMapper).Assembly.Location), - MetadataReference.CreateFromFile(typeof(ImmutableList).Assembly.Location), - MetadataReference.CreateFromFile(typeof(ImmutableArray).Assembly.Location), - MetadataReference.CreateFromFile(typeof(Enumerable).Assembly.Location), - MetadataReference.CreateFromFile(typeof(IAsyncEnumerable).Assembly.Location), - MetadataReference.CreateFromFile(typeof(Span).Assembly.Location), - MetadataReference.CreateFromFile(typeof(IgnoreDataMemberAttribute).Assembly.Location), - MetadataReference.CreateFromFile(typeof(SqlMapper).Assembly.Location), - MetadataReference.CreateFromFile(typeof(DynamicAttribute).Assembly.Location), - MetadataReference.CreateFromFile(typeof(IValidatableObject).Assembly.Location), - }, - options: new CSharpCompilationOptions(outputKind, allowUnsafe: true)); - public static Compilation CreateCompilation(string source, string assemblyName, string fileName) + { + var syntaxTree = CreateSyntaxTree(source, fileName); + return CreateCompilation(assemblyName, syntaxTree); + } + + public static Compilation CreateCompilation(string assemblyName, SyntaxTree syntaxTree) + => CreateCompilation(assemblyName, [syntaxTree]); + + public static Compilation CreateCompilation(string assemblyName, IEnumerable syntaxTrees) => CSharpCompilation.Create(assemblyName, - syntaxTrees: new[] { CSharpSyntaxTree.ParseText(source, ParseOptionsLatestLangVer).WithFilePath(fileName) }, + syntaxTrees: syntaxTrees, references: new[] { MetadataReference.CreateFromFile(typeof(Binder).Assembly.Location), #if !NET48 @@ -109,5 +89,5 @@ public static Compilation CreateCompilation(string source, string assemblyName, MetadataReference.CreateFromFile(typeof(DynamicAttribute).Assembly.Location), MetadataReference.CreateFromFile(typeof(IValidatableObject).Assembly.Location), }, - options: new CSharpCompilationOptions(OutputKind.ConsoleApplication, allowUnsafe: true)); + options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, allowUnsafe: true)); } \ No newline at end of file