using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.CompilerServices; using GameCult.Caching; using GameCult.Caching.MessagePack; using MessagePack; // The generator-on twin of ConsumerBuild: this project compiling is the first test, and running it checks that the generated // formatters are the ones in use and that the bytes are the ones the dynamic formatter wrote. var options = CultDocumentMessagePackSerialization.OptionsFor(typeof(RefDoc).Assembly); var target = new CultRecordRef(new CultRecordKey("p")); var documents = new (Type Type, object Value)[] { (typeof(MemberDoc), new MemberDoc { Name = "r", Target = target }), (typeof(KeyedDoc), new KeyedDoc { Name = "r", Standing = { [target] = 0.5f } }), (typeof(RefDoc), new RefDoc { Name = "r", Target = target, Standing = { [target] = 0.5f } }), }; foreach (var (type, _) in documents) { var formatter = options.Resolver.GetFormatterDynamic(type)!.GetType(); if (formatter.Assembly.IsDynamic || !(formatter.FullName ?? "").Contains("GeneratedMessagePackResolver")) return Fail($"{type.Name} is formatted by {formatter.FullName}, not a generated formatter"); } var aot = !RuntimeFeature.IsDynamicCodeSupported; if (aot) Console.WriteLine("SKIP ref-keyed dictionary under NativeAOT: AOT cut 3 (map A6)"); foreach (var (type, expected) in new[] { (typeof(MemberDoc), "92A172A170"), (typeof(RefDoc), "93A172A17081A170CA3F000000") }) { if (aot && type != typeof(MemberDoc)) continue; var hex = Convert.ToHexString(MessagePackSerializer.Serialize(type, documents.First(d => d.Type == type).Value, options)); Console.WriteLine(hex); if (hex != expected) return Fail($"{type.Name} payload is {hex}"); } foreach (var (type, value) in documents) { if (aot && type != typeof(MemberDoc)) continue; var path = Path.Combine(Path.GetTempPath(), "generator-consumer-" + Guid.NewGuid().ToString("N") + ".cc"); try { var registry = CultDocumentRegistry.ForTypes(new[] { type, typeof(RefTarget) }); using (var cache = new CultCache(registry)) { cache.AddBackingStore(new SingleFileMessagePackBackingStore(path)); cache.UpsertAsync(type, value, new CultRecordKey("k")).GetAwaiter().GetResult(); cache.FlushAllBackingStores(); } using var reopened = new CultCache(registry); reopened.AddBackingStore(new SingleFileMessagePackBackingStore(path)); reopened.PullAllBackingStoresAsync().GetAwaiter().GetResult(); var read = reopened.Get(new CultRecordKey("k")); if (read == null || !Convert.ToHexString(MessagePackSerializer.Serialize(type, read, options)).Equals(Convert.ToHexString(MessagePackSerializer.Serialize(type, value, options)))) return Fail($"{type.Name} did not round-trip through the store"); } finally { File.Delete(path); } } return 0; static int Fail(string message) { Console.Error.WriteLine(message); return 1; } [CultDocument("tests.generator_target", "tests.generator_target.v1")] [MessagePackObject] public sealed class RefTarget { [Key(0)] [CultName] public string Name { get; set; } = string.Empty; } [CultDocument("tests.generator_member", "tests.generator_member.v1")] [MessagePackObject] public sealed class MemberDoc { [Key(0)] [CultName] public string Name { get; set; } = string.Empty; [Key(1)] public CultRecordRef Target { get; set; } } [CultDocument("tests.generator_keyed", "tests.generator_keyed.v1")] [MessagePackObject] public sealed class KeyedDoc { [Key(0)] [CultName] public string Name { get; set; } = string.Empty; [Key(1)] public Dictionary, float> Standing { get; set; } = new(); } [CultDocument("tests.generator_both", "tests.generator_both.v1")] [MessagePackObject] public sealed class RefDoc { [Key(0)] [CultName] public string Name { get; set; } = string.Empty; [Key(1)] public CultRecordRef Target { get; set; } [Key(2)] public Dictionary, float> Standing { get; set; } = new(); }