1 module test.utils;
2 
3 import std.algorithm: map;
4 import std.array: join;
5 import std.format: format;
6 import std..string: splitLines;
7 import std.range: zip, enumerate;
8 import std..string: toUpper;
9 import std.traits: EnumMembers;
10 import std.conv: to;
11 
12 import capstone.api;
13 
14 /** Most relevant disassembly options
15 
16 Used for concise configuration of the big regression tests
17 */
18 struct Platform{
19 	Arch arch;
20 	Mode mode;
21 	ubyte[] code;
22 	string comment;
23 	Syntax syntax;
24 	string skipdataMnemonic;
25 	Callback callback;
26 }
27 
28 /// Pretty printing of bytes as in original regression tests
29 auto bytesToHex(in ubyte[] code, bool xPrefix = true, bool upperCase = false, bool spaced = true){
30 	auto fmt = (xPrefix ? "0x" : "") 		   // Hex prefix
31 			 ~ "%02" ~ (upperCase ? "X" : "x") // Hex formatting (with chosen case)
32 			 ~ (spaced ? " " : ""); 		   // Terminal space needed to match original
33 	return code.map!(i => fmt.format(i)).join;
34 }
35 ///
36 unittest{
37 	const CODE = cast(ubyte[])"\xDE\xAD\xBE\xEF";
38 	assert(CODE.bytesToHex == "0xde 0xad 0xbe 0xef ");
39 }
40 
41 /// Pretty printing of mismatches in regression tests
42 string expectationMismatch(in string expected, in string actual)
43 in{
44 	assert(expected != actual);
45 }
46 body{
47 	auto expectedLines = expected.splitLines();
48 	auto actualLines = actual.splitLines();
49 
50 	foreach(i, e, a; zip(expectedLines, actualLines).enumerate(1)){
51 		if(e != a)
52 			return "Mismatch in line %d\nExpected: %s\n  Actual: %s".format(i,e,a);
53 	}
54 
55 	return "Expected %d lines (got %d)".format(expectedLines.length, actualLines.length);
56 }
57 ///
58 unittest{
59 	string expected = "line1\nline2\nline3";
60 	{// Mismatch in line 2 }
61 		string actual = "line1\nlin2\nline3";
62 		auto res = expectationMismatch(expected, actual);
63 		assert("Mismatch in line 2\nExpected: line2\n  Actual: lin2" == res);
64 	}
65 	{// Too short
66 		string actual = "line1";
67 		auto res = expectationMismatch(expected, actual);
68 		assert("Expected 3 lines (got 1)" == res);
69 	}
70 	{// Too long
71 		string actual = "line1\nline2\nline3\nline4";
72 		auto res = expectationMismatch(expected, actual);
73 		assert("Expected 3 lines (got 4)" == res);
74 	}
75 }
76 
77 string accessToString(AccessFlags access) {
78     string[] accessStrs;
79     foreach(accessType; EnumMembers!AccessType[1..$]) // Skip AccessType.invalid
80         if(access & accessType)
81             accessStrs ~= accessType.to!string.toUpper;
82     return accessStrs.join(" | ");
83 }
84 ///
85 unittest{
86 	AccessFlags inv;
87 	assert("", inv.accessToString);
88 	auto rw = AccessFlags(AccessType.read | AccessType.write);
89 	assert("READ | WRITE", rw.accessToString);
90 }