1 package com.fasterxml.jackson.core; 2 3 import java.io.*; 4 import java.util.Arrays; 5 6 import com.fasterxml.jackson.core.testsupport.MockDataInput; 7 import com.fasterxml.jackson.core.testsupport.ThrottledInputStream; 8 9 import junit.framework.TestCase; 10 11 @SuppressWarnings("resource") 12 public abstract class BaseTest 13 extends TestCase 14 { 15 protected final static String FIELD_BASENAME = "f"; 16 17 protected final static int MODE_INPUT_STREAM = 0; 18 protected final static int MODE_INPUT_STREAM_THROTTLED = 1; 19 protected final static int MODE_READER = 2; 20 protected final static int MODE_DATA_INPUT = 3; 21 22 protected final static int[] ALL_MODES = new int[] { 23 MODE_INPUT_STREAM, 24 MODE_INPUT_STREAM_THROTTLED, 25 MODE_READER, 26 MODE_DATA_INPUT 27 }; 28 29 protected final static int[] ALL_BINARY_MODES = new int[] { 30 MODE_INPUT_STREAM, 31 MODE_INPUT_STREAM_THROTTLED, 32 MODE_DATA_INPUT 33 }; 34 35 protected final static int[] ALL_TEXT_MODES = new int[] { 36 MODE_READER 37 }; 38 39 // DataInput not streaming 40 protected final static int[] ALL_STREAMING_MODES = new int[] { 41 MODE_INPUT_STREAM, 42 MODE_INPUT_STREAM_THROTTLED, 43 MODE_READER 44 }; 45 46 /* 47 /********************************************************** 48 /* Some sample documents: 49 /********************************************************** 50 */ 51 52 protected final static int SAMPLE_SPEC_VALUE_WIDTH = 800; 53 protected final static int SAMPLE_SPEC_VALUE_HEIGHT = 600; 54 protected final static String SAMPLE_SPEC_VALUE_TITLE = "View from 15th Floor"; 55 protected final static String SAMPLE_SPEC_VALUE_TN_URL = "http://www.example.com/image/481989943"; 56 protected final static int SAMPLE_SPEC_VALUE_TN_HEIGHT = 125; 57 protected final static String SAMPLE_SPEC_VALUE_TN_WIDTH = "100"; 58 protected final static int SAMPLE_SPEC_VALUE_TN_ID1 = 116; 59 protected final static int SAMPLE_SPEC_VALUE_TN_ID2 = 943; 60 protected final static int SAMPLE_SPEC_VALUE_TN_ID3 = 234; 61 protected final static int SAMPLE_SPEC_VALUE_TN_ID4 = 38793; 62 63 protected final static String SAMPLE_DOC_JSON_SPEC = 64 "{\n" 65 +" \"Image\" : {\n" 66 +" \"Width\" : "+SAMPLE_SPEC_VALUE_WIDTH+",\n" 67 +" \"Height\" : "+SAMPLE_SPEC_VALUE_HEIGHT+"," 68 +"\"Title\" : \""+SAMPLE_SPEC_VALUE_TITLE+"\",\n" 69 +" \"Thumbnail\" : {\n" 70 +" \"Url\" : \""+SAMPLE_SPEC_VALUE_TN_URL+"\",\n" 71 +"\"Height\" : "+SAMPLE_SPEC_VALUE_TN_HEIGHT+",\n" 72 +" \"Width\" : \""+SAMPLE_SPEC_VALUE_TN_WIDTH+"\"\n" 73 +" },\n" 74 +" \"IDs\" : ["+SAMPLE_SPEC_VALUE_TN_ID1+","+SAMPLE_SPEC_VALUE_TN_ID2+","+SAMPLE_SPEC_VALUE_TN_ID3+","+SAMPLE_SPEC_VALUE_TN_ID4+"]\n" 75 +" }" 76 +"}" 77 ; 78 79 /* 80 /********************************************************** 81 /* Helper classes (beans) 82 /********************************************************** 83 */ 84 85 /** 86 * Sample class from Jackson tutorial ("JacksonInFiveMinutes") 87 */ 88 protected static class FiveMinuteUser { 89 public enum Gender { MALE, FEMALE }; 90 91 public static class Name 92 { 93 private String _first, _last; 94 Name()95 public Name() { } Name(String f, String l)96 public Name(String f, String l) { 97 _first = f; 98 _last = l; 99 } 100 getFirst()101 public String getFirst() { return _first; } getLast()102 public String getLast() { return _last; } 103 setFirst(String s)104 public void setFirst(String s) { _first = s; } setLast(String s)105 public void setLast(String s) { _last = s; } 106 107 @Override equals(Object o)108 public boolean equals(Object o) 109 { 110 if (o == this) return true; 111 if (o == null || o.getClass() != getClass()) return false; 112 Name other = (Name) o; 113 return _first.equals(other._first) && _last.equals(other._last); 114 } 115 } 116 117 private Gender _gender; 118 private Name _name; 119 private boolean _isVerified; 120 private byte[] _userImage; 121 FiveMinuteUser()122 public FiveMinuteUser() { } 123 FiveMinuteUser(String first, String last, boolean verified, Gender g, byte[] data)124 public FiveMinuteUser(String first, String last, boolean verified, Gender g, byte[] data) 125 { 126 _name = new Name(first, last); 127 _isVerified = verified; 128 _gender = g; 129 _userImage = data; 130 } 131 getName()132 public Name getName() { return _name; } isVerified()133 public boolean isVerified() { return _isVerified; } getGender()134 public Gender getGender() { return _gender; } getUserImage()135 public byte[] getUserImage() { return _userImage; } 136 setName(Name n)137 public void setName(Name n) { _name = n; } setVerified(boolean b)138 public void setVerified(boolean b) { _isVerified = b; } setGender(Gender g)139 public void setGender(Gender g) { _gender = g; } setUserImage(byte[] b)140 public void setUserImage(byte[] b) { _userImage = b; } 141 142 @Override equals(Object o)143 public boolean equals(Object o) 144 { 145 if (o == this) return true; 146 if (o == null || o.getClass() != getClass()) return false; 147 FiveMinuteUser other = (FiveMinuteUser) o; 148 if (_isVerified != other._isVerified) return false; 149 if (_gender != other._gender) return false; 150 if (!_name.equals(other._name)) return false; 151 byte[] otherImage = other._userImage; 152 if (otherImage.length != _userImage.length) return false; 153 for (int i = 0, len = _userImage.length; i < len; ++i) { 154 if (_userImage[i] != otherImage[i]) { 155 return false; 156 } 157 } 158 return true; 159 } 160 } 161 162 protected final JsonFactory JSON_FACTORY = new JsonFactory(); 163 164 /* 165 /********************************************************** 166 /* High-level helpers 167 /********************************************************** 168 */ 169 verifyJsonSpecSampleDoc(JsonParser p, boolean verifyContents)170 protected void verifyJsonSpecSampleDoc(JsonParser p, boolean verifyContents) 171 throws IOException 172 { 173 verifyJsonSpecSampleDoc(p, verifyContents, true); 174 } 175 verifyJsonSpecSampleDoc(JsonParser p, boolean verifyContents, boolean requireNumbers)176 protected void verifyJsonSpecSampleDoc(JsonParser p, boolean verifyContents, 177 boolean requireNumbers) 178 throws IOException 179 { 180 if (!p.hasCurrentToken()) { 181 p.nextToken(); 182 } 183 // first basic check, mostly for test coverage 184 assertNull(p.getTypeId()); 185 assertNull(p.getObjectId()); 186 187 assertToken(JsonToken.START_OBJECT, p.currentToken()); // main object 188 189 assertToken(JsonToken.FIELD_NAME, p.nextToken()); // 'Image' 190 if (verifyContents) { 191 verifyFieldName(p, "Image"); 192 } 193 194 assertToken(JsonToken.START_OBJECT, p.nextToken()); // 'image' object 195 196 assertToken(JsonToken.FIELD_NAME, p.nextToken()); // 'Width' 197 if (verifyContents) { 198 verifyFieldName(p, "Width"); 199 } 200 201 verifyIntToken(p.nextToken(), requireNumbers); 202 if (verifyContents) { 203 verifyIntValue(p, SAMPLE_SPEC_VALUE_WIDTH); 204 } 205 206 assertToken(JsonToken.FIELD_NAME, p.nextToken()); // 'Height' 207 if (verifyContents) { 208 verifyFieldName(p, "Height"); 209 } 210 211 verifyIntToken(p.nextToken(), requireNumbers); 212 if (verifyContents) { 213 verifyIntValue(p, SAMPLE_SPEC_VALUE_HEIGHT); 214 } 215 assertToken(JsonToken.FIELD_NAME, p.nextToken()); // 'Title' 216 if (verifyContents) { 217 verifyFieldName(p, "Title"); 218 } 219 assertToken(JsonToken.VALUE_STRING, p.nextToken()); 220 assertEquals(SAMPLE_SPEC_VALUE_TITLE, getAndVerifyText(p)); 221 assertToken(JsonToken.FIELD_NAME, p.nextToken()); // 'Thumbnail' 222 if (verifyContents) { 223 verifyFieldName(p, "Thumbnail"); 224 } 225 226 assertToken(JsonToken.START_OBJECT, p.nextToken()); // 'thumbnail' object 227 assertToken(JsonToken.FIELD_NAME, p.nextToken()); // 'Url' 228 if (verifyContents) { 229 verifyFieldName(p, "Url"); 230 } 231 assertToken(JsonToken.VALUE_STRING, p.nextToken()); 232 if (verifyContents) { 233 assertEquals(SAMPLE_SPEC_VALUE_TN_URL, getAndVerifyText(p)); 234 } 235 assertToken(JsonToken.FIELD_NAME, p.nextToken()); // 'Height' 236 if (verifyContents) { 237 verifyFieldName(p, "Height"); 238 } 239 verifyIntToken(p.nextToken(), requireNumbers); 240 if (verifyContents) { 241 verifyIntValue(p, SAMPLE_SPEC_VALUE_TN_HEIGHT); 242 } 243 assertToken(JsonToken.FIELD_NAME, p.nextToken()); // 'Width' 244 if (verifyContents) { 245 verifyFieldName(p, "Width"); 246 } 247 // Width value is actually a String in the example 248 assertToken(JsonToken.VALUE_STRING, p.nextToken()); 249 if (verifyContents) { 250 assertEquals(SAMPLE_SPEC_VALUE_TN_WIDTH, getAndVerifyText(p)); 251 } 252 253 assertToken(JsonToken.END_OBJECT, p.nextToken()); // 'thumbnail' object 254 assertToken(JsonToken.FIELD_NAME, p.nextToken()); // 'IDs' 255 assertToken(JsonToken.START_ARRAY, p.nextToken()); // 'ids' array 256 verifyIntToken(p.nextToken(), requireNumbers); // ids[0] 257 if (verifyContents) { 258 verifyIntValue(p, SAMPLE_SPEC_VALUE_TN_ID1); 259 } 260 verifyIntToken(p.nextToken(), requireNumbers); // ids[1] 261 if (verifyContents) { 262 verifyIntValue(p, SAMPLE_SPEC_VALUE_TN_ID2); 263 } 264 verifyIntToken(p.nextToken(), requireNumbers); // ids[2] 265 if (verifyContents) { 266 verifyIntValue(p, SAMPLE_SPEC_VALUE_TN_ID3); 267 } 268 verifyIntToken(p.nextToken(), requireNumbers); // ids[3] 269 if (verifyContents) { 270 verifyIntValue(p, SAMPLE_SPEC_VALUE_TN_ID4); 271 } 272 assertToken(JsonToken.END_ARRAY, p.nextToken()); // 'ids' array 273 274 assertToken(JsonToken.END_OBJECT, p.nextToken()); // 'image' object 275 276 assertToken(JsonToken.END_OBJECT, p.nextToken()); // main object 277 } 278 verifyIntToken(JsonToken t, boolean requireNumbers)279 private void verifyIntToken(JsonToken t, boolean requireNumbers) 280 { 281 if (t == JsonToken.VALUE_NUMBER_INT) { 282 return; 283 } 284 if (requireNumbers) { // to get error 285 assertToken(JsonToken.VALUE_NUMBER_INT, t); 286 } 287 // if not number, must be String 288 if (t != JsonToken.VALUE_STRING) { 289 fail("Expected INT or STRING value, got "+t); 290 } 291 } 292 verifyFieldName(JsonParser p, String expName)293 protected void verifyFieldName(JsonParser p, String expName) 294 throws IOException 295 { 296 assertEquals(expName, p.getText()); 297 assertEquals(expName, p.getCurrentName()); 298 } 299 verifyIntValue(JsonParser p, long expValue)300 protected void verifyIntValue(JsonParser p, long expValue) 301 throws IOException 302 { 303 // First, via textual 304 assertEquals(String.valueOf(expValue), p.getText()); 305 } 306 307 /* 308 /********************************************************** 309 /* Parser construction 310 /********************************************************** 311 */ 312 createParser(int mode, String doc)313 protected JsonParser createParser(int mode, String doc) throws IOException { 314 return createParser(JSON_FACTORY, mode, doc); 315 } 316 createParser(int mode, byte[] doc)317 protected JsonParser createParser(int mode, byte[] doc) throws IOException { 318 return createParser(JSON_FACTORY, mode, doc); 319 } 320 createParser(JsonFactory f, int mode, String doc)321 protected JsonParser createParser(JsonFactory f, int mode, String doc) throws IOException 322 { 323 switch (mode) { 324 case MODE_INPUT_STREAM: 325 return createParserUsingStream(f, doc, "UTF-8"); 326 case MODE_INPUT_STREAM_THROTTLED: 327 { 328 InputStream in = new ThrottledInputStream(doc.getBytes("UTF-8"), 1); 329 return f.createParser(in); 330 } 331 case MODE_READER: 332 return createParserUsingReader(f, doc); 333 case MODE_DATA_INPUT: 334 return createParserForDataInput(f, new MockDataInput(doc)); 335 default: 336 } 337 throw new RuntimeException("internal error"); 338 } 339 createParser(JsonFactory f, int mode, byte[] doc)340 protected JsonParser createParser(JsonFactory f, int mode, byte[] doc) throws IOException 341 { 342 switch (mode) { 343 case MODE_INPUT_STREAM: 344 return f.createParser(new ByteArrayInputStream(doc)); 345 case MODE_INPUT_STREAM_THROTTLED: 346 { 347 InputStream in = new ThrottledInputStream(doc, 1); 348 return f.createParser(in); 349 } 350 case MODE_READER: 351 return f.createParser(new StringReader(new String(doc, "UTF-8"))); 352 case MODE_DATA_INPUT: 353 return createParserForDataInput(f, new MockDataInput(doc)); 354 default: 355 } 356 throw new RuntimeException("internal error"); 357 } 358 createParserUsingReader(String input)359 protected JsonParser createParserUsingReader(String input) throws IOException 360 { 361 return createParserUsingReader(new JsonFactory(), input); 362 } 363 createParserUsingReader(JsonFactory f, String input)364 protected JsonParser createParserUsingReader(JsonFactory f, String input) 365 throws IOException 366 { 367 return f.createParser(new StringReader(input)); 368 } 369 createParserUsingStream(String input, String encoding)370 protected JsonParser createParserUsingStream(String input, String encoding) 371 throws IOException 372 { 373 return createParserUsingStream(new JsonFactory(), input, encoding); 374 } 375 createParserUsingStream(JsonFactory f, String input, String encoding)376 protected JsonParser createParserUsingStream(JsonFactory f, 377 String input, String encoding) 378 throws IOException 379 { 380 381 /* 23-Apr-2008, tatus: UTF-32 is not supported by JDK, have to 382 * use our own codec too (which is not optimal since there's 383 * a chance both encoder and decoder might have bugs, but ones 384 * that cancel each other out or such) 385 */ 386 byte[] data; 387 if (encoding.equalsIgnoreCase("UTF-32")) { 388 data = encodeInUTF32BE(input); 389 } else { 390 data = input.getBytes(encoding); 391 } 392 InputStream is = new ByteArrayInputStream(data); 393 return f.createParser(is); 394 } 395 createParserForDataInput(JsonFactory f, DataInput input)396 protected JsonParser createParserForDataInput(JsonFactory f, 397 DataInput input) 398 throws IOException 399 { 400 return f.createParser(input); 401 } 402 403 /* 404 /********************************************************** 405 /* Generator construction 406 /********************************************************** 407 */ 408 createGenerator(OutputStream out)409 protected JsonGenerator createGenerator(OutputStream out) throws IOException { 410 return createGenerator(JSON_FACTORY, out); 411 } 412 createGenerator(TokenStreamFactory f, OutputStream out)413 protected JsonGenerator createGenerator(TokenStreamFactory f, OutputStream out) throws IOException { 414 return f.createGenerator(out); 415 } 416 createGenerator(Writer w)417 protected JsonGenerator createGenerator(Writer w) throws IOException { 418 return createGenerator(JSON_FACTORY, w); 419 } 420 createGenerator(TokenStreamFactory f, Writer w)421 protected JsonGenerator createGenerator(TokenStreamFactory f, Writer w) throws IOException { 422 return f.createGenerator(w); 423 } 424 425 /* 426 /********************************************************** 427 /* Helper read/write methods 428 /********************************************************** 429 */ 430 writeJsonDoc(JsonFactory f, String doc, Writer w)431 protected void writeJsonDoc(JsonFactory f, String doc, Writer w) throws IOException 432 { 433 writeJsonDoc(f, doc, f.createGenerator(w)); 434 } 435 writeJsonDoc(JsonFactory f, String doc, JsonGenerator g)436 protected void writeJsonDoc(JsonFactory f, String doc, JsonGenerator g) throws IOException 437 { 438 JsonParser p = f.createParser(aposToQuotes(doc)); 439 440 while (p.nextToken() != null) { 441 g.copyCurrentStructure(p); 442 } 443 p.close(); 444 g.close(); 445 } 446 readAndWrite(JsonFactory f, JsonParser p)447 protected String readAndWrite(JsonFactory f, JsonParser p) throws IOException 448 { 449 StringWriter sw = new StringWriter(100); 450 JsonGenerator g = f.createGenerator(sw); 451 g.disable(JsonGenerator.Feature.AUTO_CLOSE_JSON_CONTENT); 452 try { 453 while (p.nextToken() != null) { 454 g.copyCurrentEvent(p); 455 } 456 } catch (IOException e) { 457 g.flush(); 458 fail("Unexpected problem during `readAndWrite`. Output so far: '"+sw+"'; problem: "+e); 459 } 460 p.close(); 461 g.close(); 462 return sw.toString(); 463 } 464 465 /* 466 /********************************************************** 467 /* Additional assertion methods 468 /********************************************************** 469 */ 470 assertToken(JsonToken expToken, JsonToken actToken)471 protected void assertToken(JsonToken expToken, JsonToken actToken) 472 { 473 if (actToken != expToken) { 474 fail("Expected token "+expToken+", current token "+actToken); 475 } 476 } 477 assertToken(JsonToken expToken, JsonParser p)478 protected void assertToken(JsonToken expToken, JsonParser p) 479 { 480 assertToken(expToken, p.currentToken()); 481 } 482 assertType(Object ob, Class<?> expType)483 protected void assertType(Object ob, Class<?> expType) 484 { 485 if (ob == null) { 486 fail("Expected an object of type "+expType.getName()+", got null"); 487 } 488 Class<?> cls = ob.getClass(); 489 if (!expType.isAssignableFrom(cls)) { 490 fail("Expected type "+expType.getName()+", got "+cls.getName()); 491 } 492 } 493 verifyException(Throwable e, String... matches)494 protected void verifyException(Throwable e, String... matches) 495 { 496 String msg = e.getMessage(); 497 String lmsg = (msg == null) ? "" : msg.toLowerCase(); 498 for (String match : matches) { 499 String lmatch = match.toLowerCase(); 500 if (lmsg.indexOf(lmatch) >= 0) { 501 return; 502 } 503 } 504 fail("Expected an exception with one of substrings ("+Arrays.asList(matches)+"): got one with message \""+msg+"\""); 505 } 506 507 /** 508 * Method that gets textual contents of the current token using 509 * available methods, and ensures results are consistent, before 510 * returning them 511 */ getAndVerifyText(JsonParser p)512 protected String getAndVerifyText(JsonParser p) throws IOException 513 { 514 // Ok, let's verify other accessors 515 int actLen = p.getTextLength(); 516 char[] ch = p.getTextCharacters(); 517 String str2 = new String(ch, p.getTextOffset(), actLen); 518 String str = p.getText(); 519 520 if (str.length() != actLen) { 521 fail("Internal problem (p.token == "+p.currentToken()+"): p.getText().length() ['"+str+"'] == "+str.length()+"; p.getTextLength() == "+actLen); 522 } 523 assertEquals("String access via getText(), getTextXxx() must be the same", str, str2); 524 525 return str; 526 } 527 528 /* 529 /********************************************************** 530 /* And other helpers 531 /********************************************************** 532 */ 533 quote(String str)534 protected static String quote(String str) { 535 return '"'+str+'"'; 536 } 537 aposToQuotes(String json)538 protected static String aposToQuotes(String json) { 539 return json.replace("'", "\""); 540 } 541 encodeInUTF32BE(String input)542 protected byte[] encodeInUTF32BE(String input) 543 { 544 int len = input.length(); 545 byte[] result = new byte[len * 4]; 546 int ptr = 0; 547 for (int i = 0; i < len; ++i, ptr += 4) { 548 char c = input.charAt(i); 549 result[ptr] = result[ptr+1] = (byte) 0; 550 result[ptr+2] = (byte) (c >> 8); 551 result[ptr+3] = (byte) c; 552 } 553 return result; 554 } 555 556 // @since 2.9.7 utf8Bytes(String str)557 protected static byte[] utf8Bytes(String str) { 558 try { 559 return str.getBytes("UTF-8"); 560 } catch (IOException e) { 561 throw new RuntimeException(e); 562 } 563 } 564 fieldNameFor(StringBuilder sb, int index)565 protected void fieldNameFor(StringBuilder sb, int index) 566 { 567 /* let's do something like "f1.1" to exercise different 568 * field names (important for byte-based codec) 569 * Other name shuffling done mostly just for fun... :) 570 */ 571 sb.append(FIELD_BASENAME); 572 sb.append(index); 573 if (index > 50) { 574 sb.append('.'); 575 if (index > 200) { 576 sb.append(index); 577 if (index > 4000) { // and some even longer symbols... 578 sb.append(".").append(index); 579 } 580 } else { 581 sb.append(index >> 3); // divide by 8 582 } 583 } 584 } 585 586 // @since 2.9.7 sharedStreamFactory()587 protected JsonFactory sharedStreamFactory() { 588 return JSON_FACTORY; 589 } 590 591 // @since 2.9.7 newStreamFactory()592 protected JsonFactory newStreamFactory() { 593 return new JsonFactory(); 594 } 595 596 // @since 2.9.8 streamFactoryBuilder()597 protected JsonFactoryBuilder streamFactoryBuilder() { 598 return (JsonFactoryBuilder) JsonFactory.builder(); 599 } 600 fieldNameFor(int index)601 protected String fieldNameFor(int index) 602 { 603 StringBuilder sb = new StringBuilder(16); 604 fieldNameFor(sb, index); 605 return sb.toString(); 606 } 607 calcQuads(byte[] wordBytes)608 protected int[] calcQuads(byte[] wordBytes) { 609 int blen = wordBytes.length; 610 int[] result = new int[(blen + 3) / 4]; 611 for (int i = 0; i < blen; ++i) { 612 int x = wordBytes[i] & 0xFF; 613 614 if (++i < blen) { 615 x = (x << 8) | (wordBytes[i] & 0xFF); 616 if (++i < blen) { 617 x = (x << 8) | (wordBytes[i] & 0xFF); 618 if (++i < blen) { 619 x = (x << 8) | (wordBytes[i] & 0xFF); 620 } 621 } 622 } 623 result[i >> 2] = x; 624 } 625 return result; 626 } 627 } 628