1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.juneau;
18
19 import static org.apache.juneau.TestUtils.*;
20 import static org.junit.jupiter.api.Assertions.*;
21
22 import java.lang.reflect.*;
23 import java.util.*;
24 import java.util.function.*;
25
26 import org.apache.juneau.html.*;
27 import org.apache.juneau.json.*;
28 import org.apache.juneau.msgpack.*;
29 import org.apache.juneau.parser.*;
30 import org.apache.juneau.serializer.*;
31 import org.apache.juneau.uon.*;
32 import org.apache.juneau.urlencoding.*;
33 import org.apache.juneau.utils.*;
34 import org.apache.juneau.xml.*;
35
36
37
38
39
40 public class ComboRoundTrip_Tester<T> {
41
42 public static <T> Builder<T> create(int index, String label, Type type, Supplier<T> in) {
43 return new Builder<>(index, label, type, in);
44 }
45
46 public static class Builder<T> {
47 private int index;
48 private String label;
49 private Supplier<T> in;
50 private String exceptionMsg;
51 private Predicate<String> skipTest = x -> false;
52 private Function<T,T> postConvert = x -> x;
53 private List<Function<T,String>> verify = list();
54 private List<Class<?>> swaps = list();
55 private Type type;
56 private Map<String,String> expected = map();
57 private List<Tuple2<Class<?>,Consumer<?>>> applies = list();
58 private Consumer<Serializer.Builder> serializerApply = x -> {};
59 private Consumer<Parser.Builder> parserApply = x -> {};
60
61 public Builder(int index, String label, Type type, T in) {
62 this.index = index;
63 this.label = label;
64 this.type = type;
65 this.in = () -> in;
66 }
67
68 public Builder(int index, String label, Type type, Supplier<T> in) {
69 this.index = index;
70 this.label = label;
71 this.type = type;
72 this.in = in;
73 }
74
75 public Builder<T> beanContext(Consumer<BeanContext.Builder> c) { apply(BeanContext.Builder.class, c); return this; }
76
77 public <T2> Builder<T> apply(Class<T2> t, Consumer<T2> c) { applies.add(Tuple2.of(t, c)); return this; }
78
79 public Builder<T> exceptionMsg(String v) { exceptionMsg = v; return this; }
80
81 public Builder<T> skipTest(Predicate<String> v) { skipTest = v; return this; }
82
83 public Builder<T> postConvert(Function<T,T> v) { postConvert = v; return this; }
84
85 public Builder<T> verify(Function<T,String> v) { verify.add(v); return this; }
86
87 public Builder<T> verify(Predicate<T> p, String msg, Object...args) { verify.add(x -> p.test(x) ? null : f(msg, args)); return this; }
88
89 public Builder<T> swaps(Class<?>...c) { swaps.addAll(list(c)); return this; }
90
91 public Builder<T> serializerApply(Consumer<Serializer.Builder> v) { serializerApply = v; return this; }
92
93 public Builder<T> parserApply(Consumer<Parser.Builder> v) { parserApply = v; return this; }
94
95 public Builder<T> json(String value) { expected.put("json", value); return this; }
96 public Builder<T> jsonT(String value) { expected.put("jsonT", value); return this; }
97 public Builder<T> jsonR(String value) { expected.put("jsonR", value); return this; }
98 public Builder<T> xml(String value) { expected.put("xml", value); return this; }
99 public Builder<T> xmlT(String value) { expected.put("xmlT", value); return this; }
100 public Builder<T> xmlR(String value) { expected.put("xmlR", value); return this; }
101 public Builder<T> xmlNs(String value) { expected.put("xmlNs", value); return this; }
102 public Builder<T> html(String value) { expected.put("html", value); return this; }
103 public Builder<T> htmlT(String value) { expected.put("htmlT", value); return this; }
104 public Builder<T> htmlR(String value) { expected.put("htmlR", value); return this; }
105 public Builder<T> uon(String value) { expected.put("uon", value); return this; }
106 public Builder<T> uonT(String value) { expected.put("uonT", value); return this; }
107 public Builder<T> uonR(String value) { expected.put("uonR", value); return this; }
108 public Builder<T> urlEnc(String value) { expected.put("urlEnc", value); return this; }
109 public Builder<T> urlEncT(String value) { expected.put("urlEncT", value); return this; }
110 public Builder<T> urlEncR(String value) { expected.put("urlEncR", value); return this; }
111 public Builder<T> msgPack(String value) { expected.put("msgPack", value); return this; }
112 public Builder<T> msgPackT(String value) { expected.put("msgPackT", value); return this; }
113 public Builder<T> rdfXml(String value) { expected.put("rdfXml", value); return this; }
114 public Builder<T> rdfXmlT(String value) { expected.put("rdfXmlT", value); return this; }
115 public Builder<T> rdfXmlR(String value) { expected.put("rdfXmlR", value); return this; }
116
117 public ComboRoundTrip_Tester<T> build() {
118 return new ComboRoundTrip_Tester<>(this);
119 }
120 }
121
122 private final String label;
123 private final Supplier<T> in;
124 private final String exceptionMsg;
125 private final Predicate<String> skipTest;
126 private final List<Function<T,String>> verify;
127 private final Type type;
128 private final Map<String,String> expected;
129 private final Map<String,Serializer> serializers = map();
130 private final Map<String,Parser> parsers = map();
131 private final Function<T,T> postConvert;
132
133 private ComboRoundTrip_Tester(Builder<T> b) {
134 label = "[" + b.index + "] " + b.label;
135 type = b.type;
136 in = b.in;
137 expected = b.expected;
138 postConvert = b.postConvert;
139 verify = b.verify;
140 skipTest = b.skipTest;
141 exceptionMsg = b.exceptionMsg;
142
143 serializers.put("json", create(b, Json5Serializer.DEFAULT.copy().addBeanTypes().addRootType()));
144 serializers.put("jsonT", create(b, JsonSerializer.create().json5().typePropertyName("t").addBeanTypes().addRootType()));
145 serializers.put("jsonR", create(b, Json5Serializer.DEFAULT_READABLE.copy().addBeanTypes().addRootType()));
146 serializers.put("xml", create(b, XmlSerializer.DEFAULT_SQ.copy().addBeanTypes().addRootType()));
147 serializers.put("xmlT", create(b, XmlSerializer.create().sq().typePropertyName("t").addBeanTypes().addRootType()));
148 serializers.put("xmlR", create(b, XmlSerializer.DEFAULT_SQ_READABLE.copy().addBeanTypes().addRootType()));
149 serializers.put("xmlNs", create(b, XmlSerializer.DEFAULT_NS_SQ.copy().addBeanTypes().addRootType()));
150 serializers.put("html", create(b, HtmlSerializer.DEFAULT_SQ.copy().addBeanTypes().addRootType()));
151 serializers.put("htmlT", create(b, HtmlSerializer.create().sq().typePropertyName("t").addBeanTypes().addRootType()));
152 serializers.put("htmlR", create(b, HtmlSerializer.DEFAULT_SQ_READABLE.copy().addBeanTypes().addRootType()));
153 serializers.put("uon", create(b, UonSerializer.DEFAULT.copy().addBeanTypes().addRootType()));
154 serializers.put("uonT", create(b, UonSerializer.create().typePropertyName("t").addBeanTypes().addRootType()));
155 serializers.put("uonR", create(b, UonSerializer.DEFAULT_READABLE.copy().addBeanTypes().addRootType()));
156 serializers.put("urlEnc", create(b, UrlEncodingSerializer.DEFAULT.copy().addBeanTypes().addRootType()));
157 serializers.put("urlEncT", create(b, UrlEncodingSerializer.create().typePropertyName("t").addBeanTypes().addRootType()));
158 serializers.put("urlEncR", create(b, UrlEncodingSerializer.DEFAULT_READABLE.copy().addBeanTypes().addRootType()));
159 serializers.put("msgPack", create(b, MsgPackSerializer.create().addBeanTypes().addRootType()));
160 serializers.put("msgPackT", create(b, MsgPackSerializer.create().typePropertyName("t").addBeanTypes().addRootType()));
161
162 parsers.put("json", create(b, JsonParser.DEFAULT.copy()));
163 parsers.put("jsonT", create(b, JsonParser.create().typePropertyName("t")));
164 parsers.put("jsonR", create(b, JsonParser.DEFAULT.copy()));
165 parsers.put("xml", create(b, XmlParser.DEFAULT.copy()));
166 parsers.put("xmlT", create(b, XmlParser.create().typePropertyName("t")));
167 parsers.put("xmlR", create(b, XmlParser.DEFAULT.copy()));
168 parsers.put("xmlNs", create(b, XmlParser.DEFAULT.copy()));
169 parsers.put("html", create(b, HtmlParser.DEFAULT.copy()));
170 parsers.put("htmlT", create(b, HtmlParser.create().typePropertyName("t")));
171 parsers.put("htmlR", create(b, HtmlParser.DEFAULT.copy()));
172 parsers.put("uon", create(b, UonParser.DEFAULT.copy()));
173 parsers.put("uonT", create(b, UonParser.create().typePropertyName("t")));
174 parsers.put("uonR", create(b, UonParser.DEFAULT.copy()));
175 parsers.put("urlEnc", create(b, UrlEncodingParser.DEFAULT.copy()));
176 parsers.put("urlEncT", create(b, UrlEncodingParser.create().typePropertyName("t")));
177 parsers.put("urlEncR", create(b, UrlEncodingParser.DEFAULT.copy()));
178 parsers.put("msgPack", create(b, MsgPackParser.DEFAULT.copy()));
179 parsers.put("msgPackT", create(b, MsgPackParser.create().typePropertyName("t")));
180 }
181
182 @SuppressWarnings("unchecked")
183 private Serializer create(Builder<?> tb, Serializer.Builder sb) {
184 tb.serializerApply.accept(sb);
185 sb.swaps(tb.swaps);
186 tb.applies.forEach(x -> {
187 if (x.getA().equals(BeanContext.Builder.class))
188 sb.beanContext((Consumer<BeanContext.Builder>) x.getB());
189 else if (x.getA().isInstance(sb))
190 sb.apply(Serializer.Builder.class, (Consumer<Serializer.Builder>) x.getB());
191 });
192 return sb.build();
193 }
194
195 @SuppressWarnings("unchecked")
196 private Parser create(Builder<?> tb, Parser.Builder pb) {
197 tb.parserApply.accept(pb);
198 pb.swaps(tb.swaps);
199 tb.applies.forEach(x -> {
200 if (x.getA().equals(BeanContext.Builder.class))
201 pb.beanContext((Consumer<BeanContext.Builder>) x.getB());
202 else if (x.getA().isInstance(pb))
203 pb.apply(Parser.Builder.class, (Consumer<Parser.Builder>) x.getB());
204 });
205 return pb.build();
206 }
207
208 private void verify(T o, String testName) {
209 for (var v : verify) {
210 var s = v.apply(o);
211 if (isNotEmpty(s)) {
212 throw new BasicAssertionError("Verification failed on test {0}/{1}: {2}", label, testName, s);
213 }
214 }
215 }
216
217 private boolean isSkipped(String testName, String expected) {
218 return "SKIP".equals(expected) || skipTest.test(testName);
219 }
220
221 public void testSerialize(String testName) throws Exception {
222 var s = serializers.get(testName);
223 var exp = expected.get(testName);
224 try {
225 if (isSkipped(testName + "-serialize", exp)) return;
226
227 var r = s.serializeToString(in.get());
228
229
230 if (eq(exp, "xxx")) {
231 System.out.println(getClass().getName() + ": " + label + "/" + testName + "=\n" + r.replaceAll("\n", "\\\\n").replaceAll("\t", "\\\\t"));
232 System.out.println(r);
233 }
234
235 assertEquals(exp, r, fms("{0}/{1} serialize-normal failed.", label, testName));
236 } catch (AssertionError e) {
237 if (exceptionMsg == null)
238 throw e;
239 assertContains(exceptionMsg, e.getMessage());
240 } catch (Exception e) {
241 if (exceptionMsg == null)
242 throw new BasicAssertionError(e, "{0}/{1} failed. exception={2}", label, testName, e.getLocalizedMessage());
243 assertContains(exceptionMsg, e.getMessage());
244 }
245 }
246
247 @SuppressWarnings("unchecked")
248 public void testParse(String testName) throws Exception {
249 var s = serializers.get(testName);
250 var exp = expected.get(testName);
251 var p = parsers.get(testName);
252 try {
253 if (isSkipped(testName + "-parse", exp)) return;
254
255 var r = s.serializeToString(in.get());
256 var o = p.parse(r, type);
257 o = postConvert.apply((T)o);
258 r = s.serializeToString(o);
259
260 assertEquals(exp, r, fms("{0}/{1} parse-normal failed", label, testName));
261 } catch (AssertionError e) {
262 if (exceptionMsg == null)
263 throw e;
264 assertContains(exceptionMsg, e.getMessage());
265 } catch (Throwable e) {
266 if (exceptionMsg == null)
267 throw new BasicAssertionError(e, "{0}/{1} failed. exception={2}", label, testName, e.getLocalizedMessage());
268 assertContains(exceptionMsg, e.getMessage());
269 }
270 }
271
272 @SuppressWarnings("unchecked")
273 public void testParseVerify(String testName) throws Exception {
274 var s = serializers.get(testName);
275 var p = parsers.get(testName);
276 try {
277 if (isSkipped(testName + "verify", "")) return;
278
279 var r = s.serializeToString(in.get());
280 var o = p.parse(r, type);
281
282 verify((T)o, testName);
283 } catch (AssertionError e) {
284 if (exceptionMsg == null)
285 throw e;
286 assertContains(exceptionMsg, e.getMessage());
287 } catch (Exception e) {
288 if (exceptionMsg == null)
289 throw new BasicAssertionError(e, "{0}/{1} failed. exception={2}", label, testName, e.getLocalizedMessage());
290 assertContains(exceptionMsg, e.getMessage());
291 }
292 }
293
294 public void testParseJsonEquivalency(String testName) throws Exception {
295 var s = serializers.get(testName);
296 var js = (WriterSerializer)serializers.get("json");
297 var exp = expected.get("json");
298 var p = parsers.get(testName);
299 try {
300 var r = s.serializeToString(in.get());
301 var o = p.parse(r, type);
302 r = js.serialize(o);
303 assertEquals(exp, r, fms("{0}/{1} parse-normal failed on JSON equivalency", label, testName));
304 } catch (AssertionError e) {
305 if (exceptionMsg == null)
306 throw e;
307 assertContains(exceptionMsg, e.getMessage());
308 } catch (Exception e) {
309 if (exceptionMsg == null)
310 throw new BasicAssertionError(e, "{0}/{1} failed. exception={2}", label, testName, e.getLocalizedMessage());
311 assertContains(exceptionMsg, e.getMessage());
312 }
313 }
314
315 @Override
316 public String toString() {
317 return "ComboRoundTripTester: " + label;
318 }
319 }