• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

zorbathut / dec / 15122828327

19 May 2025 08:44PM UTC coverage: 90.096% (-0.2%) from 90.272%
15122828327

push

github

zorbathut
Added Intent property to Recorder to indicate Serialization/Cloning/Checksum purpose.

5076 of 5634 relevant lines covered (90.1%)

221085.88 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.53
/src/WriterValidation.cs
1
using System;
2
using System.Collections;
3
using System.Collections.Generic;
4
using System.Text;
5

6
namespace Dec
7
{
8
    internal abstract class WriterValidation
9
    {
10
        private StringBuilder sb = new StringBuilder();
2,904✔
11

12
        internal Dictionary<object, string> referenceLookup = new Dictionary<object, string>();
2,904✔
13
        private WriterUtil.PendingWriteCoordinator pendingWriteCoordinator = new WriterUtil.PendingWriteCoordinator();
2,904✔
14

15
        public abstract bool AllowReflection { get; }
16
        public Recorder.IUserSettings UserSettings { get; }
10,542✔
17

18
        public WriterValidation(Recorder.IUserSettings userSettings)
2,904✔
19
        {
2,904✔
20
            UserSettings = userSettings;
2,904✔
21
        }
2,904✔
22

23
        public void AppendLine(string line)
24
        {
201,666✔
25
            sb.AppendLine(line);
201,666✔
26
        }
201,666✔
27

28
        public void RegisterPendingWrite(Action action)
29
        {
5,016✔
30
            pendingWriteCoordinator.RegisterPendingWrite(action);
5,016✔
31
        }
5,016✔
32

33
        public string Finish()
34
        {
2,904✔
35
            pendingWriteCoordinator.DequeuePendingWrites();
2,904✔
36

37
            return sb.ToString();
2,904✔
38
        }
2,904✔
39
    }
40

41
    internal class WriterValidationCompose : WriterValidation
42
    {
43
        public override bool AllowReflection { get => true; }
22,056✔
44

45
        public WriterValidationCompose(Recorder.IUserSettings userSettings) : base(userSettings)
2,244✔
46
        {
2,244✔
47
        }
2,244✔
48

49
        public WriterNode StartDec(Type type, string decName)
50
        {
8,682✔
51
            return new WriterNodeValidation(this, $"Dec.Database<{type.ComposeCSFormatted()}>.Get(\"{decName}\")", new PathDec(type, decName));
8,682✔
52
        }
8,682✔
53
    }
54

55
    internal class WriterValidationRecord : WriterValidation
56
    {
57
        public override bool AllowReflection { get => false; }
×
58

59
        public WriterValidationRecord(Recorder.IUserSettings userSettings) : base(userSettings)
660✔
60
        {
660✔
61
        }
660✔
62

63
        public WriterNode StartValidation()
64
        {
660✔
65
            return new WriterNodeValidation(this, $"input", new PathRoot("RECORD"));
660✔
66
        }
660✔
67
    }
68

69
    // This is used for things that can be expressed as an easy inline string, which is used as part of the Dictionary-handling code.
70
    internal abstract class WriterNodeCS : WriterNode
71
    {
72
        public WriterNodeCS(Path path) : base(new Recorder.Settings(), path)
180,444✔
73
        {
180,444✔
74
        }
180,444✔
75

76
        public abstract void WriteToken(string token);
77

78
        public override void WritePrimitive(object value)
79
        {
92,370✔
80
            if (value.GetType() == typeof(bool))
92,370✔
81
            {
13,494✔
82
                WriteToken(value.ToString().ToLower());
13,494✔
83
            }
13,494✔
84
            else if (value.GetType() == typeof(float))
78,876✔
85
            {
10,296✔
86
                var val = (float)value;
10,296✔
87
                if (float.IsNaN(val))
10,296✔
88
                {
18✔
89
                    WriteToken("float.NaN");
18✔
90
                }
18✔
91
                else if (float.IsPositiveInfinity(val))
10,278✔
92
                {
18✔
93
                    WriteToken("float.PositiveInfinity");
18✔
94
                }
18✔
95
                else if (float.IsNegativeInfinity(val))
10,260✔
96
                {
18✔
97
                    WriteToken("float.NegativeInfinity");
18✔
98
                }
18✔
99
                else
100
                {
10,242✔
101
                    WriteToken(((float)value).ToString("G17") + 'f');
10,242✔
102
                }
10,242✔
103
            }
10,296✔
104
            else if (value.GetType() == typeof(double))
68,580✔
105
            {
1,086✔
106
                var val = (double)value;
1,086✔
107
                if (double.IsNaN(val))
1,086✔
108
                {
822✔
109
                    WriteToken("double.NaN");
822✔
110
                }
822✔
111
                else if (double.IsPositiveInfinity(val))
264✔
112
                {
18✔
113
                    WriteToken("double.PositiveInfinity");
18✔
114
                }
18✔
115
                else if (double.IsNegativeInfinity(val))
246✔
116
                {
18✔
117
                    WriteToken("double.NegativeInfinity");
18✔
118
                }
18✔
119
                else
120
                {
228✔
121
                    WriteToken(((double)value).ToString("G17") + 'd');
228✔
122
                }
228✔
123
            }
1,086✔
124
            else
125
            {
67,494✔
126
                WriteToken(value.ToString());
67,494✔
127
            }
67,494✔
128
        }
92,370✔
129

130
        public override void WriteEnum(object value)
131
        {
78✔
132
            WriteToken($"{value.GetType().ComposeCSFormatted()}.{value}");
78✔
133
        }
78✔
134

135
        public override void WriteString(string value)
136
        {
10,842✔
137
            WriteToken($"\"{value}\"");
10,842✔
138
        }
10,842✔
139

140
        public override void WriteType(Type value)
141
        {
78✔
142
            WriteToken($"typeof({value.ComposeCSFormatted()})");
78✔
143
        }
78✔
144

145
        public override void WriteDec(Dec value)
146
        {
15,024✔
147
            if (value != null)
15,024✔
148
            {
1,494✔
149
                WriteToken($"Dec.Database<{value.GetType().ComposeCSFormatted()}>.Get(\"{value.DecName}\")");
1,494✔
150
            }
1,494✔
151
            else
152
            {
13,530✔
153
                WriteExplicitNull();
13,530✔
154
            }
13,530✔
155
        }
15,024✔
156
    }
157

158
    internal sealed class WriterNodeValidation : WriterNodeCS
159
    {
160
        private WriterValidation writer;
161
        private string accessor;
162

163
        public override bool AllowReflection { get => writer.AllowReflection; }
22,056✔
164
        public override bool AllowDecPath { get => false; }
135,030✔
165
        public override Recorder.Purpose Intent { get => Recorder.Purpose.Serialization; }  // I mean, sorta
×
166
        public override Recorder.IUserSettings UserSettings { get => writer.UserSettings; }
10,542✔
167

168
        public WriterNodeValidation(WriterValidation writer, string accessor, Path path) : base(path)
169,902✔
169
        {
169,902✔
170
            this.writer = writer;
169,902✔
171
            this.accessor = accessor;
169,902✔
172
        }
169,902✔
173

174
        public override WriterNode CreateRecorderChild(string label, Recorder.Settings settings)
175
        {
9,366✔
176
            return new WriterNodeValidation(writer, $"{accessor}.{label}", new PathMember(Path, label));
9,366✔
177
        }
9,366✔
178

179
        public override WriterNode CreateReflectionChild(System.Reflection.FieldInfo field, Recorder.Settings settings)
180
        {
118,278✔
181
            if (field.IsPublic)
118,278✔
182
            {
118,254✔
183
                return new WriterNodeValidation(writer, $"{accessor}.{field.Name}", new PathMember(Path, field.Name));
118,254✔
184
            }
185
            else
186
            {
24✔
187
                return new WriterNodeValidation(writer, $"(({field.FieldType.ComposeCSFormatted()})typeof({field.DeclaringType.ComposeCSFormatted()}).GetField(\"{field.Name}\", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).GetValue({accessor}))", new PathMember(Path, field.Name));
24✔
188
            }
189
        }
118,278✔
190

191
        private void WriteIsEqual(string value)
192
        {
94,320✔
193
            writer.AppendLine($"Assert.AreEqual({value}, {accessor});");
94,320✔
194
        }
94,320✔
195

196
        public override void WriteToken(string token)
197
        {
94,320✔
198
            WriteIsEqual(token);
94,320✔
199
        }
94,320✔
200

201
        public override void TagClass(Type type)
202
        {
306✔
203
            FlagAsClass();
306✔
204

205
            if (type == typeof(Type))
306✔
206
            {
6✔
207
                // Special case: Sometimes we convert System.RuntimeType to System.Type, because System.RuntimeType is a compatible C# implementation detail that we don't want to preserve.
208
                // We handle that conversion here as well.
209
                writer.AppendLine($"Assert.IsTrue({accessor} is System.Type);");
6✔
210
            }
6✔
211
            else if (typeof(Dec).IsAssignableFrom(type))
300✔
212
            {
12✔
213
                // Decs are a little weird; sometimes we specify the root of a dec hierarchy with full expectation that it'll grab a child in that hierarchy.
214
                writer.AppendLine($"Assert.IsTrue(typeof({type.ComposeCSFormatted()}).IsAssignableFrom({accessor}.GetType()));");
12✔
215
            }
12✔
216
            else
217
            {
288✔
218
                writer.AppendLine($"Assert.AreEqual(typeof({type.ComposeCSFormatted()}), {accessor}.GetType());");
288✔
219
            }
288✔
220

221
            accessor = $"(({type.ComposeCSFormatted()}){accessor})";
306✔
222
        }
306✔
223

224
        public override void WriteExplicitNull()
225
        {
33,618✔
226
            writer.AppendLine($"Assert.IsNull({accessor});");
33,618✔
227
        }
33,618✔
228

229
        public override bool WriteReference(object value, Path path)
230
        {
30,534✔
231
            // We're just going to ignore whether a reference is "allowed" here; this is not the place for metavalidation.
232

233
            if (writer.referenceLookup.ContainsKey(value))
30,534✔
234
            {
150✔
235
                writer.AppendLine($"Assert.AreSame({writer.referenceLookup[value]}, {accessor});");
150✔
236
                return true;
150✔
237
            }
238
            else
239
            {
30,384✔
240
                writer.referenceLookup[value] = accessor;
30,384✔
241
                return false;
30,384✔
242
            }
243
        }
30,534✔
244

245
        public override void WriteRecord(IRecordable value)
246
        {
5,016✔
247
            // For performance's sake we shouldn't always be doing this.
248
            // But the validation layer is already incredibly slow and none of this is relevant in terms of performance anyway.
249
            // So, whatever.
250
            writer.RegisterPendingWrite(() => value.Record(new RecorderWriter(this)));
10,032✔
251
        }
5,016✔
252

253
        public override void WriteArray(Array value)
254
        {
180✔
255
            Type referencedType = value.GetType().GetElementType();
180✔
256

257
            // Maybe this should just be a giant AreEqual with a dynamically allocated array?
258
            writer.AppendLine($"Assert.AreEqual({accessor}.Length, {value.Length});");
180✔
259
            writer.AppendLine($"if ({accessor}.Length == {value.Length}) {{");
180✔
260

261
            for (int i = 0; i < value.Length; ++i)
1,956✔
262
            {
798✔
263
                Serialization.ComposeElement(new WriterNodeValidation(writer, $"{accessor}[{i}]", new PathIndex(Path, i)), value.GetValue(i), referencedType);
798✔
264
            }
798✔
265

266
            writer.AppendLine($"}}");
180✔
267
        }
180✔
268

269
        public override void WriteList(IList value)
270
        {
9,390✔
271
            Type referencedType = value.GetType().GetGenericArguments()[0];
9,390✔
272

273
            // Maybe this should just be a giant AreEqual with a dynamically allocated list?
274
            writer.AppendLine($"Assert.AreEqual({accessor}.Count, {value.Count});");
9,390✔
275
            writer.AppendLine($"if ({accessor}.Count == {value.Count}) {{");
9,390✔
276

277
            for (int i = 0; i < value.Count; ++i)
62,316✔
278
            {
21,768✔
279
                Serialization.ComposeElement(new WriterNodeValidation(writer, $"{accessor}[{i}]", new PathIndex(Path, i)), value[i], referencedType);
21,768✔
280
            }
21,768✔
281

282
            writer.AppendLine($"}}");
9,390✔
283
        }
9,390✔
284

285
        public override void WriteDictionary(IDictionary value)
286
        {
4,698✔
287
            Type keyType = value.GetType().GetGenericArguments()[0];
4,698✔
288
            Type valueType = value.GetType().GetGenericArguments()[1];
4,698✔
289

290
            writer.AppendLine($"Assert.AreEqual({accessor}.Count, {value.Count});");
4,698✔
291

292
            IDictionaryEnumerator iterator = value.GetEnumerator();
4,698✔
293
            while (iterator.MoveNext())
14,352✔
294
            {
9,654✔
295
                var keyNode = new WriterNodeStringize(UserSettings, new PathDictionaryKey(Path));
9,654✔
296
                Serialization.ComposeElement(keyNode, iterator.Key, keyType);
9,654✔
297

298
                writer.AppendLine($"if ({accessor}.ContainsKey({keyNode.SerializedString})) {{");
9,654✔
299
                Serialization.ComposeElement(new WriterNodeValidation(writer, $"{accessor}[{keyNode.SerializedString}]", new PathDictionaryValueUnpathable(Path)), iterator.Value, valueType);
9,654✔
300
                writer.AppendLine($"}} else {{");
9,654✔
301
                writer.AppendLine($"Assert.IsTrue({accessor}.ContainsKey({keyNode.SerializedString}));");   // this is unnecessary - it could just be .Fail() - but this gives you a *much* better error message
9,654✔
302
                writer.AppendLine($"}}");
9,654✔
303
            }
9,654✔
304
        }
4,698✔
305

306
        public override void WriteHashSet(IEnumerable value)
307
        {
240✔
308
            Type keyType = value.GetType().GetGenericArguments()[0];
240✔
309

310
            int count = 0;
240✔
311
            IEnumerator iterator = value.GetEnumerator();
240✔
312
            while (iterator.MoveNext())
1,128✔
313
            {
888✔
314
                var keyNode = new WriterNodeStringize(UserSettings, new PathHashSetElement(Path));
888✔
315
                Serialization.ComposeElement(keyNode, iterator.Current, keyType);
888✔
316

317
                // You might think "Assert.Contains" would do what we want, but it doesn't - it requires an ICollection and HashSet isn't an ICollection.
318
                writer.AppendLine($"Assert.IsTrue({accessor}.Contains({keyNode.SerializedString}));");
888✔
319

320
                ++count;
888✔
321
            }
888✔
322

323
            writer.AppendLine($"Assert.AreEqual({accessor}.Count, {count});");
240✔
324
        }
240✔
325

326
        public override void WriteQueue(IEnumerable value)
327
        {
12✔
328
            Type referencedType = value.GetType().GetGenericArguments()[0];
12✔
329

330
            var count = (int)value.GetType().GetProperty("Count").GetValue(value);
12✔
331

332
            // Maybe this should just be a giant AreEqual with a dynamically allocated list?
333
            writer.AppendLine($"Assert.AreEqual({accessor}.Count, {count});");
12✔
334
            writer.AppendLine($"if ({accessor}.Count == {count}) {{");
12✔
335

336
            // this is just easier
337
            writer.AppendLine($"var tempArray = new {referencedType.ComposeCSFormatted()}[{count}];");
12✔
338
            writer.AppendLine($"{accessor}.CopyTo(tempArray, 0);");
12✔
339

340
            // and meanwhile . . .
341
            var array = value.GetType().GetMethod("ToArray").Invoke(value, new object[] { }) as Array;
12✔
342

343
            for (int i = 0; i < count; ++i)
108✔
344
            {
42✔
345
                Serialization.ComposeElement(new WriterNodeValidation(writer, $"tempArray[{i}]", new PathIndex(Path, i)), array.GetValue(i), referencedType);
42✔
346
            }
42✔
347

348
            writer.AppendLine($"}}");
12✔
349
        }
12✔
350

351
        public override void WriteStack(IEnumerable value)
352
        {
12✔
353
            Type referencedType = value.GetType().GetGenericArguments()[0];
12✔
354

355
            var count = (int)value.GetType().GetProperty("Count").GetValue(value);
12✔
356

357
            // Maybe this should just be a giant AreEqual with a dynamically allocated list?
358
            writer.AppendLine($"Assert.AreEqual({accessor}.Count, {count});");
12✔
359
            writer.AppendLine($"if ({accessor}.Count == {count}) {{");
12✔
360

361
            // this is just easier
362
            writer.AppendLine($"var tempArray = new {referencedType.ComposeCSFormatted()}[{count}];");
12✔
363
            writer.AppendLine($"{accessor}.CopyTo(tempArray, 0);");
12✔
364

365
            // and meanwhile . . .
366
            var array = value.GetType().GetMethod("ToArray").Invoke(value, new object[] { }) as Array;
12✔
367

368
            for (int i = 0; i < count; ++i)
108✔
369
            {
42✔
370
                Serialization.ComposeElement(new WriterNodeValidation(writer, $"tempArray[{i}]", new PathIndex(Path, i)), array.GetValue(i), referencedType);
42✔
371
            }
42✔
372

373
            writer.AppendLine($"}}");
12✔
374
        }
12✔
375

376
        public override void WriteTuple(object value, System.Runtime.CompilerServices.TupleElementNamesAttribute names)
377
        {
78✔
378
            var args = value.GetType().GenericTypeArguments;
78✔
379
            var length = args.Length;
78✔
380

381
            var nameArray = names?.TransformNames ?? UtilMisc.DefaultTupleNames;
78✔
382

383
            for (int i = 0; i < length; ++i)
648✔
384
            {
246✔
385
                var propertyName = nameArray[i];
246✔
386
                Serialization.ComposeElement(new WriterNodeValidation(writer, $"{accessor}.{propertyName}", new PathIndex(Path, i)), value.GetType().GetProperty(UtilMisc.DefaultTupleNames[i]).GetValue(value), args[i]);
246✔
387
            }
246✔
388
        }
78✔
389

390
        public override void WriteValueTuple(object value, System.Runtime.CompilerServices.TupleElementNamesAttribute names)
391
        {
138✔
392
            var args = value.GetType().GenericTypeArguments;
138✔
393
            var length = args.Length;
138✔
394

395
            var nameArray = names?.TransformNames ?? UtilMisc.DefaultTupleNames;
138✔
396

397
            for (int i = 0; i < length; ++i)
1,008✔
398
            {
366✔
399
                var propertyName = nameArray[i];
366✔
400
                Serialization.ComposeElement(new WriterNodeValidation(writer, $"{accessor}.{propertyName}", new PathIndex(Path, i)), value.GetType().GetField(UtilMisc.DefaultTupleNames[i]).GetValue(value), args[i]);
366✔
401
            }
366✔
402
        }
138✔
403

404
        public override void WriteConvertible(Converter converter, object value)
405
        {
×
406
            // this isn't really a thing I can implement because the entire point of this is to compare the output to known values
407
            // and if we're going through Converter, we don't know what the underlying known values will be
408
            throw new NotImplementedException();
×
409
        }
410

411
        public override void WriteDecPathRef(object value)
412
        {
×
413
            // not really viable honestly
414
            throw new NotImplementedException();
×
415
        }
416
    }
417

418
    // This is used solely for dict keys, because we want to get a reasonably-stringized version of this without having to jump through hideous hoops.
419
    internal sealed class WriterNodeStringize : WriterNodeCS
420
    {
421
        public override bool AllowReflection { get => false; }
×
422
        public override bool AllowDecPath { get => false; }
10,308✔
423
        public override Recorder.Purpose Intent { get => Recorder.Purpose.Serialization; }
×
424
        public override Recorder.IUserSettings UserSettings { get; }
×
425

426
        public string SerializedString { get; private set; }
50,934✔
427

428
        public WriterNodeStringize(Recorder.IUserSettings userSettings, Path path) : base(path)
10,542✔
429
        {
10,542✔
430
            UserSettings = userSettings;
10,542✔
431
        }
10,542✔
432

433
        public override void WriteToken(string token)
434
        {
10,542✔
435
            if (SerializedString != null)
10,542✔
436
            {
×
437
                Dbg.Err("String is already set!");
×
438
            }
×
439

440
            SerializedString = token;
10,542✔
441
        }
10,542✔
442

443
        public override WriterNode CreateRecorderChild(string label, Recorder.Settings settings)
444
        {
×
445
            throw new NotImplementedException();
×
446
        }
447

448
        public override WriterNode CreateReflectionChild(System.Reflection.FieldInfo field, Recorder.Settings settings)
449
        {
×
450
            throw new NotImplementedException();
×
451
        }
452

453
        public override void TagClass(Type type)
454
        {
×
455
            throw new NotImplementedException();
×
456
        }
457

458
        public override void WriteExplicitNull()
459
        {
×
460
            throw new NotImplementedException();
×
461
        }
462

463
        public override bool WriteReference(object value, Path path)
464
        {
×
465
            throw new NotImplementedException();
×
466
        }
467

468
        public override void WriteRecord(IRecordable value)
469
        {
×
470
            throw new NotImplementedException();
×
471
        }
472

473
        public override void WriteArray(Array value)
474
        {
×
475
            throw new NotImplementedException();
×
476
        }
477

478
        public override void WriteList(IList value)
479
        {
×
480
            throw new NotImplementedException();
×
481
        }
482

483
        public override void WriteDictionary(IDictionary value)
484
        {
×
485
            throw new NotImplementedException();
×
486
        }
487

488
        public override void WriteHashSet(IEnumerable value)
489
        {
×
490
            throw new NotImplementedException();
×
491
        }
492

493
        public override void WriteStack(IEnumerable value)
494
        {
×
495
            throw new NotImplementedException();
×
496
        }
497

498
        public override void WriteQueue(IEnumerable value)
499
        {
×
500
            throw new NotImplementedException();
×
501
        }
502

503
        public override void WriteTuple(object value, System.Runtime.CompilerServices.TupleElementNamesAttribute names)
504
        {
×
505
            throw new NotImplementedException();
×
506
        }
507

508
        public override void WriteValueTuple(object value, System.Runtime.CompilerServices.TupleElementNamesAttribute names)
509
        {
×
510
            throw new NotImplementedException();
×
511
        }
512

513
        public override void WriteConvertible(Converter converter, object value)
514
        {
×
515
            throw new NotImplementedException();
×
516
        }
517

518
        public override void WriteDecPathRef(object value)
519
        {
×
520
            throw new NotImplementedException();
×
521
        }
522
    }
523
}
524

STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc