This repository has been archived by the owner on Jan 18, 2022. It is now read-only.
/
TestLogDispatcher.cs
144 lines (123 loc) · 4.84 KB
/
TestLogDispatcher.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
using System;
using System.Collections.Generic;
using System.Linq;
using Improbable.Gdk.Core;
using Improbable.Worker.CInterop;
using NUnit.Framework;
using UnityEngine;
namespace Improbable.Gdk.TestUtils
{
/// <summary>
/// A ILogDispatcher implementation designed to be used in testing. This replaces the LogAssert approach with
/// a more specialised one.
///
/// The expected usage is to use EnterExpectingScope() with a using block. This methods returns a Disposable
/// object which you can mark logs as expected. When the object is disposed - it will assert against any logs.
/// </summary>
public class TestLogDispatcher : ILogDispatcher
{
private ExpectingScope currentExpectingScope;
public Connection Connection { get; set; }
public string WorkerType { get; set; }
public void HandleLog(LogType type, LogEvent logEvent)
{
if (currentExpectingScope != null)
{
currentExpectingScope.CheckIncomingLog(type, logEvent);
}
else if (type == LogType.Error || type == LogType.Exception)
{
Assert.Fail($"Encountered error log outside of an expecting scope: [{type}] - {logEvent}");
}
}
/// <summary>
/// Creates and returns an disposable ExpectingScope object. This is intended to be used with a using block.
/// </summary>
/// <returns>An ExpectingScope instance.</returns>
/// <exception cref="InvalidOperationException">
/// Throws if you already have an un-disposed ExpectingScope from this logger.
/// </exception>
public ExpectingScope EnterExpectingScope()
{
if (currentExpectingScope != null)
{
throw new InvalidOperationException(
"Cannot enter an ExpectingScope while there is an outstanding ExpectingScope");
}
currentExpectingScope = new ExpectingScope(this);
return currentExpectingScope;
}
public void ExitExpectingScope()
{
if (currentExpectingScope == null)
{
throw new InvalidOperationException(
"Cannot exit an ExpectingScope if you have not entered one!");
}
currentExpectingScope.Dispose();
}
public void Dispose()
{
currentExpectingScope = null;
}
public class ExpectingScope : IDisposable
{
private readonly Queue<LogStructure> expectedLogs = new Queue<LogStructure>();
private readonly Queue<(LogType, LogEvent)> unexpectedLogs = new Queue<(LogType, LogEvent)>();
private TestLogDispatcher dispatcher;
internal ExpectingScope(TestLogDispatcher dispatcher)
{
this.dispatcher = dispatcher;
}
public void Expect(LogType type, params string[] dataKeys)
{
expectedLogs.Enqueue(new LogStructure(type, dataKeys));
}
public void Dispose()
{
dispatcher.currentExpectingScope = null;
Assert.AreEqual(0, expectedLogs.Count);
var unexpectedLogsString = string.Join("\n", unexpectedLogs.Select(log =>
{
var (logType, logEvent) = log;
return $"[{logType}] - {logEvent}";
}));
Assert.AreEqual(0, unexpectedLogs.Count,
$"Received unexpected errors or exceptions : {unexpectedLogsString}");
}
internal void CheckIncomingLog(LogType type, LogEvent logEvent)
{
if (expectedLogs.Count > 0 && expectedLogs.Peek().DoesMatchLog(type, logEvent))
{
expectedLogs.Dequeue();
return;
}
if (type == LogType.Error || type == LogType.Exception)
{
unexpectedLogs.Enqueue((type, logEvent));
}
}
/// <summary>
/// A struct which defines the shape of an expected log.
/// </summary>
private struct LogStructure
{
public LogType Type;
public string[] DataKeys;
public LogStructure(LogType type, params string[] dataKeys)
{
Type = type;
DataKeys = dataKeys;
}
public bool DoesMatchLog(LogType type, LogEvent logEvent)
{
if (type != Type)
{
return false;
}
return !logEvent.Data.Keys.Except(DataKeys).Any();
}
}
}
}
}