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

zorbathut / dec / 11709205202

06 Nov 2024 05:57PM UTC coverage: 90.198% (-0.4%) from 90.637%
11709205202

push

github

zorbathut
Added the ability to reference class objects contained within Decs.

4748 of 5264 relevant lines covered (90.2%)

194623.74 hits per line

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

86.09
/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,410✔
11

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

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

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

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

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

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

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

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

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

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

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

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

63
        public WriterNode StartValidation()
64
        {
540✔
65
            return new WriterNodeValidation(this, $"input", new PathRoot("RECORD"));
540✔
66
        }
540✔
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)
150,360✔
73
        {
150,360✔
74
        }
150,360✔
75

76
        public abstract void WriteToken(string token);
77

78
        public override void WritePrimitive(object value)
79
        {
76,975✔
80
            if (value.GetType() == typeof(bool))
76,975✔
81
            {
11,245✔
82
                WriteToken(value.ToString().ToLower());
11,245✔
83
            }
11,245✔
84
            else if (value.GetType() == typeof(float))
65,730✔
85
            {
8,580✔
86
                var val = (float)value;
8,580✔
87
                if (float.IsNaN(val))
8,580✔
88
                {
15✔
89
                    WriteToken("float.NaN");
15✔
90
                }
15✔
91
                else if (float.IsPositiveInfinity(val))
8,565✔
92
                {
15✔
93
                    WriteToken("float.PositiveInfinity");
15✔
94
                }
15✔
95
                else if (float.IsNegativeInfinity(val))
8,550✔
96
                {
15✔
97
                    WriteToken("float.NegativeInfinity");
15✔
98
                }
15✔
99
                else
100
                {
8,535✔
101
                    WriteToken(((float)value).ToString("G17") + 'f');
8,535✔
102
                }
8,535✔
103
            }
8,580✔
104
            else if (value.GetType() == typeof(double))
57,150✔
105
            {
905✔
106
                var val = (double)value;
905✔
107
                if (double.IsNaN(val))
905✔
108
                {
685✔
109
                    WriteToken("double.NaN");
685✔
110
                }
685✔
111
                else if (double.IsPositiveInfinity(val))
220✔
112
                {
15✔
113
                    WriteToken("double.PositiveInfinity");
15✔
114
                }
15✔
115
                else if (double.IsNegativeInfinity(val))
205✔
116
                {
15✔
117
                    WriteToken("double.NegativeInfinity");
15✔
118
                }
15✔
119
                else
120
                {
190✔
121
                    WriteToken(((double)value).ToString("G17") + 'd');
190✔
122
                }
190✔
123
            }
905✔
124
            else
125
            {
56,245✔
126
                WriteToken(value.ToString());
56,245✔
127
            }
56,245✔
128
        }
76,975✔
129

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

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

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

145
        public override void WriteDec(Dec value)
146
        {
12,520✔
147
            if (value != null)
12,520✔
148
            {
1,245✔
149
                WriteToken($"Dec.Database<{value.GetType().ComposeCSFormatted()}>.Get(\"{value.DecName}\")");
1,245✔
150
            }
1,245✔
151
            else
152
            {
11,275✔
153
                WriteExplicitNull();
11,275✔
154
            }
11,275✔
155
        }
12,520✔
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; }
18,380✔
164
        public override bool AllowDecPath { get => false; }
112,515✔
165
        public override Recorder.IUserSettings UserSettings { get => writer.UserSettings; }
8,785✔
166

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

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

178
        public override WriterNode CreateReflectionChild(System.Reflection.FieldInfo field, Recorder.Settings settings)
179
        {
98,565✔
180
            if (field.IsPublic)
98,565✔
181
            {
98,545✔
182
                return new WriterNodeValidation(writer, $"{accessor}.{field.Name}", new PathMember(Path, field.Name));
98,545✔
183
            }
184
            else
185
            {
20✔
186
                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));
20✔
187
            }
188
        }
98,565✔
189

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

195
        public override void WriteToken(string token)
196
        {
78,600✔
197
            WriteIsEqual(token);
78,600✔
198
        }
78,600✔
199

200
        public override void TagClass(Type type)
201
        {
255✔
202
            FlagAsClass();
255✔
203

204
            if (type == typeof(Type))
255✔
205
            {
5✔
206
                // 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.
207
                // We handle that conversion here as well.
208
                writer.AppendLine($"Assert.IsTrue({accessor} is System.Type);");
5✔
209
            }
5✔
210
            else if (typeof(Dec).IsAssignableFrom(type))
250✔
211
            {
10✔
212
                // 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.
213
                writer.AppendLine($"Assert.IsTrue(typeof({type.ComposeCSFormatted()}).IsAssignableFrom({accessor}.GetType()));");
10✔
214
            }
10✔
215
            else
216
            {
240✔
217
                writer.AppendLine($"Assert.AreEqual(typeof({type.ComposeCSFormatted()}), {accessor}.GetType());");
240✔
218
            }
240✔
219

220
            accessor = $"(({type.ComposeCSFormatted()}){accessor})";
255✔
221
        }
255✔
222

223
        public override void WriteExplicitNull()
224
        {
28,015✔
225
            writer.AppendLine($"Assert.IsNull({accessor});");
28,015✔
226
        }
28,015✔
227

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

232
            if (writer.referenceLookup.ContainsKey(value))
25,435✔
233
            {
125✔
234
                writer.AppendLine($"Assert.AreSame({writer.referenceLookup[value]}, {accessor});");
125✔
235
                return true;
125✔
236
            }
237
            else
238
            {
25,310✔
239
                writer.referenceLookup[value] = accessor;
25,310✔
240
                return false;
25,310✔
241
            }
242
        }
25,435✔
243

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

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

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

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

265
            writer.AppendLine($"}}");
150✔
266
        }
150✔
267

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

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

276
            for (int i = 0; i < value.Count; ++i)
51,930✔
277
            {
18,140✔
278
                Serialization.ComposeElement(new WriterNodeValidation(writer, $"{accessor}[{i}]", new PathIndex(Path, i)), value[i], referencedType);
18,140✔
279
            }
18,140✔
280

281
            writer.AppendLine($"}}");
7,825✔
282
        }
7,825✔
283

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

289
            writer.AppendLine($"Assert.AreEqual({accessor}.Count, {value.Count});");
3,915✔
290

291
            IDictionaryEnumerator iterator = value.GetEnumerator();
3,915✔
292
            while (iterator.MoveNext())
11,960✔
293
            {
8,045✔
294
                var keyNode = new WriterNodeStringize(UserSettings, new PathDictionaryKey(Path));
8,045✔
295
                Serialization.ComposeElement(keyNode, iterator.Key, keyType);
8,045✔
296

297
                writer.AppendLine($"if ({accessor}.ContainsKey({keyNode.SerializedString})) {{");
8,045✔
298
                Serialization.ComposeElement(new WriterNodeValidation(writer, $"{accessor}[{keyNode.SerializedString}]", new PathDictionaryValue(Path)), iterator.Value, valueType);
8,045✔
299
                writer.AppendLine($"}} else {{");
8,045✔
300
                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
8,045✔
301
                writer.AppendLine($"}}");
8,045✔
302
            }
8,045✔
303
        }
3,915✔
304

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

309
            int count = 0;
200✔
310
            IEnumerator iterator = value.GetEnumerator();
200✔
311
            while (iterator.MoveNext())
940✔
312
            {
740✔
313
                var keyNode = new WriterNodeStringize(UserSettings, new PathHashSetElement(Path));
740✔
314
                Serialization.ComposeElement(keyNode, iterator.Current, keyType);
740✔
315

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

319
                ++count;
740✔
320
            }
740✔
321

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

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

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

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

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

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

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

347
            writer.AppendLine($"}}");
10✔
348
        }
10✔
349

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

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

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

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

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

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

372
            writer.AppendLine($"}}");
10✔
373
        }
10✔
374

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

380
            var nameArray = names?.TransformNames ?? UtilMisc.DefaultTupleNames;
65✔
381

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

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

394
            var nameArray = names?.TransformNames ?? UtilMisc.DefaultTupleNames;
115✔
395

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

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

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

417
    // 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.
418
    internal sealed class WriterNodeStringize : WriterNodeCS
419
    {
420
        public override bool AllowReflection { get => false; }
×
421
        public override bool AllowDecPath { get => false; }
8,590✔
422
        public override Recorder.IUserSettings UserSettings { get; }
×
423

424
        public string SerializedString { get; private set; }
42,445✔
425

426
        public WriterNodeStringize(Recorder.IUserSettings userSettings, Path path) : base(path)
8,785✔
427
        {
8,785✔
428
            UserSettings = userSettings;
8,785✔
429
        }
8,785✔
430

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

438
            SerializedString = token;
8,785✔
439
        }
8,785✔
440

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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