1 // Copyright Mario Kröplin 2017. 2 // Distributed under the Boost Software License, Version 1.0. 3 // (See accompanying file LICENSE_1_0.txt or copy at 4 // http://www.boost.org/LICENSE_1_0.txt) 5 6 module dunit.diff; 7 8 import std.algorithm; 9 import std.range; 10 import std.typecons; 11 12 /** 13 * Returns a description of the difference between the strings. 14 */ 15 string description(string expected, string actual) @safe pure 16 { 17 const MAX_LENGTH = 20; 18 const result = diff(expected, actual); 19 const oneLiner = max(result[0].length, result[1].length) <= MAX_LENGTH 20 && !result[0].canFind("\n", "\r") 21 && !result[1].canFind("\n", "\r"); 22 23 if (oneLiner) 24 return "expected: <" ~ result[0] ~ "> but was: <" ~ result[1] ~ ">"; 25 else 26 return "expected:\n" ~ result[0] ~ "\nbut was:\n" ~ result[1]; 27 } 28 29 /// 30 @safe pure unittest 31 { 32 assert(description("ab", "Ab") == "expected: <<a>b> but was: <<A>b>"); 33 assert(description("a\nb", "A\nb") == "expected:\n<a>\nb\nbut was:\n<A>\nb"); 34 } 35 36 /** 37 * Returns a pair of strings that highlight the difference between lhs and rhs. 38 */ 39 Tuple!(string, string) diff(string)(string lhs, string rhs) 40 { 41 const MAX_LENGTH = 20; 42 43 if (lhs == rhs) 44 return tuple(lhs, rhs); 45 46 auto rest = mismatch(lhs, rhs); 47 auto retroDiff = mismatch(retro(rest[0]), retro(rest[1])); 48 auto diff = tuple(retro(retroDiff[0]), retro(retroDiff[1])); 49 string prefix = lhs[0 .. $ - rest[0].length]; 50 string suffix = lhs[prefix.length + diff[0].length .. $]; 51 52 if (prefix.length > MAX_LENGTH) 53 prefix = "..." ~ prefix[$ - MAX_LENGTH .. $]; 54 if (suffix.length > MAX_LENGTH) 55 suffix = suffix[0 .. MAX_LENGTH] ~ "..."; 56 57 return tuple( 58 prefix ~ '<' ~ diff[0] ~ '>' ~ suffix, 59 prefix ~ '<' ~ diff[1] ~ '>' ~ suffix); 60 } 61 62 /// 63 @safe pure unittest 64 { 65 assert(diff("abc", "abc") == tuple("abc", "abc")); 66 // highlight difference 67 assert(diff("abc", "Abc") == tuple("<a>bc", "<A>bc")); 68 assert(diff("abc", "aBc") == tuple("a<b>c", "a<B>c")); 69 assert(diff("abc", "abC") == tuple("ab<c>", "ab<C>")); 70 assert(diff("abc", "") == tuple("<abc>", "<>")); 71 assert(diff("abc", "abbc") == tuple("ab<>c", "ab<b>c")); 72 // abbreviate long prefix or suffix 73 assert(diff("_12345678901234567890a", "_12345678901234567890A") 74 == tuple("...12345678901234567890<a>", "...12345678901234567890<A>")); 75 assert(diff("a12345678901234567890_", "A12345678901234567890_") 76 == tuple("<a>12345678901234567890...", "<A>12345678901234567890...")); 77 }