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