-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDisplay.cs
More file actions
179 lines (173 loc) · 8.35 KB
/
Display.cs
File metadata and controls
179 lines (173 loc) · 8.35 KB
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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
using Perfy.Testing;
namespace Perfy.Display
{
static class DisplayMethods
{
public static void HelpMenu()
{
StrikeThrough();
Console.WriteLine("Welcome to the help menu.");
StrikeThrough();
Console.WriteLine("\nThis cmd toolkit expects command-line arguments, most often flags");
Console.WriteLine("Any argument not passed as the value of a flag, is assumed to be a script name by the program");
Console.WriteLine("For example, if you have Python installed, and the \"demonstrations\" scripts and data are in the same folder as this executable, you can run this to compare the speed of a few Python scripts:\n");
Console.WriteLine("\tPerfy.exe -rcmd \"py.exe :script :inputs\" -data jsfile:data.json primeFinder1.py primeFinder2.py\n");
(string, string)[] Explanations =
[
("-h/--help", "Display this help menu"),
("-rcmd/-runCommand", "Specify the command used to execute a specific script. Takes a value of \":script :inputs\" by default (script is replaced with the script argument, and inputs with the test case inputs)"),
("-time/-timeout", "Specify the maximum timeout for your script in milliseconds (used to stop potential infinite loops from crashing this program). Is 10 seconds by default"),
("-data/-d", "Specify data source. Currently only supports jsfile:filename.json (object array with Inputs and Outputs as string arrays)"),
("-batch","\tSpecify a batch size (for n concurrent test cases per script)"),
("-input/-onecase", "Provide a single test case to test the script(s) against"),
];
foreach((string, string) explanation in Explanations) Console.WriteLine("{0}\t\t{1}", explanation.Item1, explanation.Item2);
}
public static void StrikeThrough(char c = '=')
{
string s = "";
for (int i = 0; i < Console.WindowWidth; i++)
s += c;
Console.WriteLine(s);
}
public static void PrintInColor(string message, ConsoleColor color)
{
ConsoleColor currentColor = Console.ForegroundColor;
Console.ForegroundColor = color;
Console.Write(message);
Console.ForegroundColor = currentColor;
}
public static void PrintWarning(string message) => PrintInColor(message + "\n", ConsoleColor.Yellow);
public static void PrintError(string message) => PrintInColor(message + "\n", ConsoleColor.Red);
}
abstract class DisplayHandler(Tester[] testers, int caseCount)
{
protected Tester[] Testers = testers;
protected int CaseCount = caseCount;
public abstract void Listen();
}
class Racer : DisplayHandler
{
readonly TestResult[,] Results;
readonly TesterEndArgs[] EndArgs;
int PrintedResults = 0;
readonly int[] ReceivedResults;
readonly bool[] TestingEnded;
volatile bool Process = false;
public Racer(Tester[] testers, int caseCount) : base(testers, caseCount)
{
TestingEnded = new bool[Testers.Length];
ReceivedResults = new int[Testers.Length];
Results = new TestResult[Testers.Length,CaseCount];
EndArgs = new TesterEndArgs[Testers.Length];
for(int i = 0;i < Testers.Length;i++)
{
int localIndex = i;
Testers[i].OnTestCaseReturned += (test) => { Results[localIndex, ReceivedResults[localIndex]++] = test; Process = true; };
Testers[i].TestingEnded += (self, args) => { EndArgs[localIndex] = args; Process = true; TestingEnded[localIndex] = true; };
}
}
bool Finished()
{
foreach (bool flag in TestingEnded)
if (!flag)
return false;
return true;
}
public override void Listen()
{
while(!Finished())
{
if (Process)
{
while (ReceivedResults.Min() > PrintedResults)
PrintComparison(PrintedResults++);
Process = false;
}
Thread.Sleep(100);
}
PrintResults();
}
readonly List<(int, int)> ErrorCases = [];
void PrintComparison(int caseId)
{
Console.WriteLine();
DisplayMethods.StrikeThrough('=');
bool[] isAccurate = new bool[Testers.Length];
for (int i = 0; i < Testers.Length; i++)
isAccurate[i] = Results[i, caseId].Accuracy > 0.5 && Results[i, caseId].Errors.Length == 0;
int minIndex = -1;
string[] messages = new string[Testers.Length];
for(int i = 0;i < Results.GetLength(0); i++)
{
if (isAccurate[i] && (minIndex == -1 || Results[i, caseId].ElapsedMs < Results[minIndex, caseId].ElapsedMs))
{
minIndex = i;
}
messages[i] = $"Accuracy: {Results[i, caseId].Accuracy * 100}%, Time taken: {Results[i, caseId].ElapsedMs}ms\t";
}
for (int i = 0; i < Testers.Length; i++)
if (!isAccurate[i])
{
DisplayMethods.PrintInColor(messages[i], ConsoleColor.Red);
ErrorCases.Add((i, caseId));
}
else if (i == minIndex)
DisplayMethods.PrintInColor(messages[i], ConsoleColor.Green);
else
Console.Write(messages[i]);
Console.WriteLine();
DisplayMethods.StrikeThrough('=');
}
void PrintResults()
{
while (ReceivedResults.Min() > PrintedResults)
PrintComparison(PrintedResults++);
Console.WriteLine();
DisplayMethods.StrikeThrough('=');
Console.WriteLine("\nTesting ended.\n");
Console.WriteLine();
int minIndex = -1;
string[] messages = new string[Testers.Length];
for (int i = 0; i < Testers.Length; i++)
{
if (minIndex == -1 || EndArgs[i].TotalElapsedMs < EndArgs[minIndex].TotalElapsedMs)
{
minIndex = i;
}
messages[i] = $"Average Accuracy: {EndArgs[i].AverageAccuracy * 100}%, Time taken: {EndArgs[i].TotalElapsedMs}ms";
}
for (int i = 0;i < EndArgs.Length;i++)
if(i == minIndex)
DisplayMethods.PrintInColor(messages[i] + ": fastest\t", ConsoleColor.Green);
else
Console.Write(messages[i] + ": {0:F2}% slower\t", (EndArgs[i].TotalElapsedMs / (double)EndArgs[minIndex].TotalElapsedMs) * 100 - 100);
Console.WriteLine("\n");
DisplayMethods.StrikeThrough('=');
if (ErrorCases.Count > 0)
{
Console.ForegroundColor = ConsoleColor.Red;
DisplayMethods.StrikeThrough('=');
Console.WriteLine("Issues found in some test cases:\n");
foreach((int, int) index in ErrorCases)
{
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine("Test result {0} from script {1}", index.Item2 + 1, index.Item1 + 1);
Console.WriteLine("Script input: {0}", String.Join(',', Results[index.Item1, index.Item2].Test.Inputs));
if (Results[index.Item1, index.Item2].Errors.Length > 0)
{
Console.WriteLine("Errors were thrown:");
DisplayMethods.PrintError(Results[index.Item1, index.Item2].Errors);
}
else
{
Console.WriteLine("Wrong output returned (Accuracy {0}%)", Results[index.Item1, index.Item2].Accuracy * 100);
Console.WriteLine("\nExpected output: {0}\n", String.Join(',', Results[index.Item1, index.Item2].Test.Outputs));
DisplayMethods.PrintError($"Script output: {String.Join(',', Results[index.Item1, index.Item2].ScriptOutputs)}");
}
}
}
}
}
}