1 | // Copyright (C) 2007 Google Inc. |
2 | // |
3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | // you may not use this file except in compliance with the License. |
5 | // You may obtain a copy of the License at |
6 | // |
7 | // http://www.apache.org/licenses/LICENSE-2.0 |
8 | // |
9 | // Unless required by applicable law or agreed to in writing, software |
10 | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | // See the License for the specific language governing permissions and |
13 | // limitations under the License. |
14 | |
15 | package com.google.caja.util; |
16 | |
17 | import java.util.Formatter; |
18 | import java.util.List; |
19 | import java.util.ListIterator; |
20 | |
21 | import junit.framework.ComparisonFailure; |
22 | |
23 | /** |
24 | * Extensions to junit.framework.Asserts that can be statically imported as by |
25 | * {@code import static com.google.caja.util.MoreAsserts.*;}. |
26 | * |
27 | * @author mikesamuel@gmail.com |
28 | */ |
29 | public final class MoreAsserts { |
30 | |
31 | /** |
32 | * Fails iff the contents of the two lists differ according to |
33 | * {@code Object.equals}. |
34 | * Tries to present the differences as nice diffs. |
35 | * <p> |
36 | * TODO(mikesamuel): maybe actually diff using |
37 | * http://www.incava.org/projects/java/java-diff/ |
38 | */ |
39 | public static <T> void assertListsEqual( |
40 | List<? extends T> expected, List<? extends T> actual) { |
41 | assertListsEqual(expected, actual, 2); |
42 | } |
43 | |
44 | /** |
45 | * Fails iff the contents of the two lists differ according to |
46 | * {@code Object.equals}. |
47 | * Tries to present the differences as nice diffs. |
48 | * <p> |
49 | * TODO(mikesamuel): maybe actually diff using |
50 | * http://www.incava.org/projects/java/java-diff/ |
51 | * |
52 | * @param diffContext the number of extra lines to show if there are errors. |
53 | */ |
54 | public static <T> void assertListsEqual( |
55 | List<? extends T> expected, List<? extends T> actual, int diffContext) { |
56 | int m = expected.size(); |
57 | int n = actual.size(); |
58 | |
59 | int commonPrefix = 0; |
60 | { |
61 | ListIterator<? extends T> i = expected.listIterator(); |
62 | ListIterator<? extends T> j = actual.listIterator(); |
63 | |
64 | while (i.hasNext() && j.hasNext() && areEqual(i.next(), j.next())) { |
65 | ++commonPrefix; |
66 | } |
67 | } |
68 | |
69 | if (commonPrefix == Math.max(m, n)) { |
70 | // All are equal |
71 | return; |
72 | } |
73 | |
74 | int commonSuffix = 0; |
75 | if (commonPrefix != Math.min(m, n)) { |
76 | ListIterator<? extends T> i = expected.listIterator(m); |
77 | ListIterator<? extends T> j = actual.listIterator(n); |
78 | |
79 | int max = Math.min(m, n) - commonPrefix; |
80 | while (commonSuffix < max && i.hasPrevious() && j.hasPrevious() |
81 | && areEqual(i.previous(), j.previous())) { |
82 | ++commonSuffix; |
83 | } |
84 | } |
85 | |
86 | throw new ComparisonFailure( |
87 | "Expected: {{{\n" |
88 | + snippet(expected, |
89 | Math.max(commonPrefix - diffContext, 0), |
90 | Math.min(m, m - commonSuffix + diffContext), 84) |
91 | + "\n}}} != {{{\n" |
92 | + snippet(actual, |
93 | Math.max(commonPrefix - diffContext, 0), |
94 | Math.min(n, n - commonSuffix + diffContext), 84) |
95 | + "\n}}}", |
96 | snippet(expected, 0, expected.size(), Integer.MAX_VALUE), |
97 | snippet(actual, 0, actual.size(), Integer.MAX_VALUE) |
98 | ); |
99 | } |
100 | |
101 | private static String snippet(List<?> a, int start, int end, int maxlen) { |
102 | StringBuilder sb = new StringBuilder(); |
103 | if (start != 0) { |
104 | sb.append("\t..."); |
105 | } |
106 | |
107 | Formatter f = new Formatter(sb); |
108 | int index = start; |
109 | for (Object item : a.subList(start, end)) { |
110 | if (sb.length() != 0) { sb.append('\n'); } |
111 | if (item != null) { |
112 | String type = item.getClass().getSimpleName(); |
113 | f.format("\t%3d %s: %s", Integer.valueOf(index), |
114 | abbreviatedString("" + item, maxlen - type.length()), type); |
115 | } else { |
116 | f.format("\t%3d <null>", Integer.valueOf(index)); |
117 | } |
118 | ++index; |
119 | } |
120 | if (end < a.size()) { |
121 | if (sb.length() != 0) { sb.append('\n'); } |
122 | sb.append("\t..."); |
123 | } |
124 | return sb.toString(); |
125 | } |
126 | |
127 | private static String abbreviatedString(String s, int maxLen) { |
128 | s = s.replace("\\", "\\\\") |
129 | .replace("\n", "\\n") |
130 | .replace("\r", "\\r"); |
131 | if (s.length() > maxLen) { |
132 | System.err.println("<<<" + s + ">>>"); |
133 | int headLen = (maxLen - 3) / 2; |
134 | int tailLen = maxLen - 3 - headLen; |
135 | s = s.substring(0, headLen) + "..." + s.substring(s.length() - tailLen); |
136 | } |
137 | return "`" + s + "`"; |
138 | } |
139 | |
140 | private static boolean areEqual(Object a, Object b) { |
141 | return a != null ? a.equals(b) : b == null; |
142 | } |
143 | |
144 | private MoreAsserts() { /* not instantiable */ } |
145 | } |