1 module testquark.testquark;
2 import std.stdio;
3 import std.json;
4 import std.algorithm;
5 import std.traits;
6 import std.array;
7 import std.math;
8 
9 ///Required to indicate what is measurable.
10 struct NamedBenchmark
11 {
12 
13 	string name;
14 	string module_;
15 	int line;
16 	this(string n, int l = __LINE__, string f = __MODULE__)
17 	{
18 		name = n;
19 		line = l;
20 		module_ = f;
21 	}
22 
23 	JSONValue toJson()
24 	{
25 		return JSONValue([
26 				"name": JSONValue(name),
27 				"module": JSONValue(module_),
28 				"defined": JSONValue(line)
29 				]);
30 	}
31 }
32 ///Enables timing of unittests, call runTests
33 template TimeTests(alias that)
34 {
35 	version (unittest)
36 	{
37 		alias tests = __traits(getUnitTests, that);
38 
39 		/** 
40 		 * Measure tests in a naive manner
41 
42 		 * Params:
43 		 *   iters = How many iterations to time each test
44 
45 		 * Returns: JSONValue containing collected data
46 		 */
47 		JSONValue runTests(size_t iters)
48 		{
49 			struct TestResult
50 			{
51 				NamedBenchmark src;
52 				float mean;
53 				float min, max;
54 				float stdandardDeviation;
55 				this(NamedBenchmark s)
56 				{
57 					src = s;
58 				}
59 
60 				JSONValue toJson()
61 				{
62 					return JSONValue([
63 							"src": src.toJson,
64 							"timeUnit": JSONValue("us"),
65 							"mean": JSONValue(mean),
66 							"min": JSONValue(min),
67 							"max": JSONValue(max),
68 							"standardDeviation": JSONValue(stdandardDeviation)
69 							]);
70 				}
71 			}
72 
73 			TestResult[] tmp;
74 
75 			foreach (test; tests)
76 			{
77 				if (hasUDA!(test, NamedBenchmark))
78 				{
79 					float[] usEachRun = new float[iters];
80 
81 					const tag = getUDAs!(test, NamedBenchmark)[0];
82 					writef!("Measuring test %s from %s now\n")(tag.name, tag.module_);
83 					auto res = TestResult(tag);
84 
85 					//Get data
86 					foreach (ref i; usEachRun)
87 					{
88 						import std.datetime.stopwatch;
89 						//Time the fucker
90 						auto sw = StopWatch(AutoStart.no);
91 						sw.start();
92 
93 						test();
94 
95 						sw.stop();
96 
97 						i = cast(float) sw.peek.total!"usecs";
98 						
99 					}
100 
101 					//Process data
102 					res.mean = sum(usEachRun) / iters;
103 					res.min = minElement(usEachRun);
104 					res.max = maxElement(usEachRun);
105 					//Mean of the squares minus the square of the mean is wot i was taught init
106 					import dstats.summary : stdev;
107 					res.stdandardDeviation = usEachRun.stdev;
108 					//writeln(JSONValue(usEachRun).toPrettyString);
109 					tmp ~= res;
110 				}
111 			}
112 			return JSONValue(tmp.map!(x => x.toJson).array);
113 		}
114 	}
115 }
116 
117 
118 /** Hijacks your main function and runs the tests in mod.
119  *  mixin this to inject a C main that initializes the runtime and prints the tests etc.
120  * 
121  * Params:
122  *   mod = Where to gets the tests from
123  */
124 template HijackMain(alias mod)
125 {
126 	pragma(mangle, "main")
127 	extern(C)
128 	int main()
129 	{
130 		import core.runtime;
131 		import std.stdio;
132 		rt_init;
133 		alias runThis = TimeTests!mod;
134 		write("Iterations: \n");
135 		size_t iters;
136 		readf!"%d\n"(iters);
137 		
138 		puts("\n");
139 		
140 
141 		writeln(runThis.runTests(iters).toPrettyString());
142 
143 		
144 		rt_term();
145 		return 0;
146 	}
147 }
148 ///just to get some compilation errors
149 @NamedBenchmark("test1") unittest
150 {
151 	foreach(_; 0..10_000)
152 	{
153 
154 	}
155 }
156 
157 ///
158 @NamedBenchmark("main") unittest
159 {
160 	//alias g = TimeTests!testquark;
161 }
162 
163 //version(unittest)
164 	//mixin HijackMain!testquark;