1 module tests.support.Util; 2 3 bool fileExists(string path) 4 { 5 import std.file : exists, isFile; 6 return exists(path) && isFile(path); 7 } 8 9 string mismatchRegion( 10 string expected, 11 string actual, 12 size_t margin, 13 bool strict, 14 string prefix = "<<<<<<< expected", 15 string interfix = "=======", 16 string suffix = ">>>>>>> actual") 17 { 18 import std.algorithm.iteration : splitter; 19 import std.range : empty; 20 import std..string : lineSplitter, stripRight, strip; 21 import std.algorithm.comparison : min; 22 23 if (!strict) 24 { 25 expected = stripRight(expected); 26 actual = stripRight(actual); 27 } 28 29 string[] Q; 30 size_t q = 0; 31 size_t p = 0; 32 Q.length = margin; 33 34 size_t line = 0; 35 36 auto aItr = lineSplitter(expected); 37 auto bItr = lineSplitter(actual); 38 39 while (!aItr.empty && !bItr.empty) 40 { 41 if (aItr.front != bItr.front) 42 break; 43 44 Q[p] = aItr.front; 45 46 q = min(q + 1, margin); 47 p = (p + 1) % margin; 48 49 aItr.popFront(); 50 bItr.popFront(); 51 52 ++line; 53 } 54 55 if (strict && expected.length != actual.length && aItr.empty && bItr.empty) 56 { 57 if (expected.length < actual.length) 58 bItr = lineSplitter("\n"); 59 else 60 aItr = lineSplitter("\n"); 61 } 62 63 margin = expected.strip.empty 64 || actual.strip.empty 65 ? size_t.max : margin; 66 67 if (!aItr.empty || !bItr.empty) 68 { 69 import std.array : Appender; 70 import std.conv : to; 71 72 auto result = Appender!string(); 73 74 auto l = line - q; 75 76 result.put(prefix); 77 result.put("\n"); 78 79 foreach (i; 0 .. q) 80 { 81 result.put(to!string(l + i)); 82 result.put(": "); 83 result.put(Q[(p + i) % q]); 84 result.put("\n"); 85 } 86 87 for (size_t i = 0; i <= margin && !aItr.empty; ++i) 88 { 89 result.put(to!string(line + i)); 90 result.put("> "); 91 result.put(aItr.front); 92 result.put("\n"); 93 aItr.popFront(); 94 } 95 96 result.put(interfix); 97 result.put("\n"); 98 99 foreach (i; 0 .. q) 100 { 101 result.put(to!string(l + i)); 102 result.put(": "); 103 result.put(Q[(p + i) % q]); 104 result.put("\n"); 105 } 106 107 for (size_t i = 0; i <= margin && !bItr.empty; ++i) 108 { 109 result.put(to!string(line + i)); 110 result.put("> "); 111 result.put(bItr.front); 112 result.put("\n"); 113 bItr.popFront(); 114 } 115 116 result.put(suffix); 117 result.put("\n"); 118 119 return result.data; 120 } 121 122 return null; 123 } 124 125 string mismatchRegionTranslated( 126 string translated, 127 string expected, 128 size_t margin, 129 bool strict) 130 { 131 return mismatchRegion( 132 translated, 133 expected, 134 margin, 135 strict, 136 "Translated code doesn't match expected.\n<<<<<<< translated", 137 "=======", 138 ">>>>>>> expected"); 139 } 140 141 unittest 142 { 143 import core.exception : AssertError; 144 145 void assertMismatchRegion( 146 string expected, 147 string a, 148 string b, 149 bool strict = false, 150 size_t margin = 2, 151 string file = __FILE__, 152 size_t line = __LINE__) 153 { 154 import std.format; 155 156 auto actual = mismatchRegion(a, b, margin, strict); 157 158 if (expected != actual) 159 { 160 auto templ = "\nExpected:\n%s\nActual:\n%s\n"; 161 162 string message = format(templ, expected, actual); 163 164 throw new AssertError(message, file, line); 165 } 166 } 167 168 assertMismatchRegion(null, "", ""); 169 170 assertMismatchRegion(null, "foo", "foo"); 171 172 assertMismatchRegion(q"X 173 <<<<<<< expected 174 0: foo 175 1> bar 176 ======= 177 0: foo 178 1> baz 179 >>>>>>> actual 180 X", "foo\nbar", "foo\nbaz"); 181 182 assertMismatchRegion(q"X 183 <<<<<<< expected 184 0: foo 185 ======= 186 0: foo 187 1> baz 188 >>>>>>> actual 189 X", "foo", "foo\nbaz"); 190 191 assertMismatchRegion(q"X 192 <<<<<<< expected 193 1: bar 194 2: baz 195 3> quuux 196 4> yada 197 5> yada 198 ======= 199 1: bar 200 2: baz 201 3> quux 202 4> yada 203 5> yada 204 >>>>>>> actual 205 X", "foo\nbar\nbaz\nquuux\nyada\nyada\nyada\nlast", "foo\nbar\nbaz\nquux\nyada\nyada\nyada\nlast"); 206 207 assertMismatchRegion(q"X 208 <<<<<<< expected 209 1: bar 210 2: baz 211 3> quuux 212 4> yada 213 5> yada 214 ======= 215 1: bar 216 2: baz 217 3> quuuux 218 4> yada 219 5> yada 220 >>>>>>> actual 221 X", "foo\nbar\nbaz\nquuux\nyada\nyada\nyada\nlast", "foo\nbar\nbaz\nquuuux\nyada\nyada\nyada\nlast"); 222 223 assertMismatchRegion( 224 "<<<<<<< expected\n0: foo\n1> \n=======\n0: foo\n>>>>>>> actual\n", 225 "foo\n", 226 "foo", 227 true); 228 }