1 package com.fasterxml.jackson.databind; 2 3 import java.io.*; 4 import java.util.Arrays; 5 6 import junit.framework.TestCase; 7 8 import com.fasterxml.jackson.core.*; 9 10 //import static org.junit.Assert.*; 11 12 public abstract class BaseTest 13 extends TestCase 14 { 15 /* 16 /********************************************************** 17 /* Some sample documents: 18 /********************************************************** 19 */ 20 21 protected final static int SAMPLE_SPEC_VALUE_WIDTH = 800; 22 protected final static int SAMPLE_SPEC_VALUE_HEIGHT = 600; 23 protected final static String SAMPLE_SPEC_VALUE_TITLE = "View from 15th Floor"; 24 protected final static String SAMPLE_SPEC_VALUE_TN_URL = "http://www.example.com/image/481989943"; 25 protected final static int SAMPLE_SPEC_VALUE_TN_HEIGHT = 125; 26 protected final static String SAMPLE_SPEC_VALUE_TN_WIDTH = "100"; 27 protected final static int SAMPLE_SPEC_VALUE_TN_ID1 = 116; 28 protected final static int SAMPLE_SPEC_VALUE_TN_ID2 = 943; 29 protected final static int SAMPLE_SPEC_VALUE_TN_ID3 = 234; 30 protected final static int SAMPLE_SPEC_VALUE_TN_ID4 = 38793; 31 32 protected final static String SAMPLE_DOC_JSON_SPEC = 33 "{\n" 34 +" \"Image\" : {\n" 35 +" \"Width\" : "+SAMPLE_SPEC_VALUE_WIDTH+",\n" 36 +" \"Height\" : "+SAMPLE_SPEC_VALUE_HEIGHT+"," 37 +"\"Title\" : \""+SAMPLE_SPEC_VALUE_TITLE+"\",\n" 38 +" \"Thumbnail\" : {\n" 39 +" \"Url\" : \""+SAMPLE_SPEC_VALUE_TN_URL+"\",\n" 40 +"\"Height\" : "+SAMPLE_SPEC_VALUE_TN_HEIGHT+",\n" 41 +" \"Width\" : \""+SAMPLE_SPEC_VALUE_TN_WIDTH+"\"\n" 42 +" },\n" 43 +" \"IDs\" : ["+SAMPLE_SPEC_VALUE_TN_ID1+","+SAMPLE_SPEC_VALUE_TN_ID2+","+SAMPLE_SPEC_VALUE_TN_ID3+","+SAMPLE_SPEC_VALUE_TN_ID4+"]\n" 44 +" }" 45 +"}" 46 ; 47 48 /* 49 /********************************************************** 50 /* Helper classes (beans) 51 /********************************************************** 52 */ 53 54 /** 55 * Sample class from Jackson tutorial ("JacksonInFiveMinutes") 56 */ 57 protected static class FiveMinuteUser { 58 public enum Gender { MALE, FEMALE }; 59 60 public static class Name 61 { 62 private String _first, _last; 63 Name()64 public Name() { } Name(String f, String l)65 public Name(String f, String l) { 66 _first = f; 67 _last = l; 68 } 69 getFirst()70 public String getFirst() { return _first; } getLast()71 public String getLast() { return _last; } 72 setFirst(String s)73 public void setFirst(String s) { _first = s; } setLast(String s)74 public void setLast(String s) { _last = s; } 75 76 @Override equals(Object o)77 public boolean equals(Object o) 78 { 79 if (o == this) return true; 80 if (o == null || o.getClass() != getClass()) return false; 81 Name other = (Name) o; 82 return _first.equals(other._first) && _last.equals(other._last); 83 } 84 } 85 86 private Gender _gender; 87 private Name _name; 88 private boolean _isVerified; 89 private byte[] _userImage; 90 FiveMinuteUser()91 public FiveMinuteUser() { } 92 FiveMinuteUser(String first, String last, boolean verified, Gender g, byte[] data)93 public FiveMinuteUser(String first, String last, boolean verified, Gender g, byte[] data) 94 { 95 _name = new Name(first, last); 96 _isVerified = verified; 97 _gender = g; 98 _userImage = data; 99 } 100 getName()101 public Name getName() { return _name; } isVerified()102 public boolean isVerified() { return _isVerified; } getGender()103 public Gender getGender() { return _gender; } getUserImage()104 public byte[] getUserImage() { return _userImage; } 105 setName(Name n)106 public void setName(Name n) { _name = n; } setVerified(boolean b)107 public void setVerified(boolean b) { _isVerified = b; } setGender(Gender g)108 public void setGender(Gender g) { _gender = g; } setUserImage(byte[] b)109 public void setUserImage(byte[] b) { _userImage = b; } 110 111 @Override equals(Object o)112 public boolean equals(Object o) 113 { 114 if (o == this) return true; 115 if (o == null || o.getClass() != getClass()) return false; 116 FiveMinuteUser other = (FiveMinuteUser) o; 117 if (_isVerified != other._isVerified) return false; 118 if (_gender != other._gender) return false; 119 if (!_name.equals(other._name)) return false; 120 byte[] otherImage = other._userImage; 121 if (otherImage.length != _userImage.length) return false; 122 for (int i = 0, len = _userImage.length; i < len; ++i) { 123 if (_userImage[i] != otherImage[i]) { 124 return false; 125 } 126 } 127 return true; 128 } 129 } 130 131 /* 132 /********************************************************** 133 /* High-level helpers 134 /********************************************************** 135 */ 136 verifyJsonSpecSampleDoc(JsonParser jp, boolean verifyContents)137 protected void verifyJsonSpecSampleDoc(JsonParser jp, boolean verifyContents) 138 throws IOException 139 { 140 verifyJsonSpecSampleDoc(jp, verifyContents, true); 141 } 142 verifyJsonSpecSampleDoc(JsonParser jp, boolean verifyContents, boolean requireNumbers)143 protected void verifyJsonSpecSampleDoc(JsonParser jp, boolean verifyContents, 144 boolean requireNumbers) 145 throws IOException 146 { 147 if (!jp.hasCurrentToken()) { 148 jp.nextToken(); 149 } 150 assertToken(JsonToken.START_OBJECT, jp.currentToken()); // main object 151 152 assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Image' 153 if (verifyContents) { 154 verifyFieldName(jp, "Image"); 155 } 156 157 assertToken(JsonToken.START_OBJECT, jp.nextToken()); // 'image' object 158 159 assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Width' 160 if (verifyContents) { 161 verifyFieldName(jp, "Width"); 162 } 163 164 verifyIntToken(jp.nextToken(), requireNumbers); 165 if (verifyContents) { 166 verifyIntValue(jp, SAMPLE_SPEC_VALUE_WIDTH); 167 } 168 169 assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Height' 170 if (verifyContents) { 171 verifyFieldName(jp, "Height"); 172 } 173 174 verifyIntToken(jp.nextToken(), requireNumbers); 175 if (verifyContents) { 176 verifyIntValue(jp, SAMPLE_SPEC_VALUE_HEIGHT); 177 } 178 assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Title' 179 if (verifyContents) { 180 verifyFieldName(jp, "Title"); 181 } 182 assertToken(JsonToken.VALUE_STRING, jp.nextToken()); 183 assertEquals(SAMPLE_SPEC_VALUE_TITLE, getAndVerifyText(jp)); 184 assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Thumbnail' 185 if (verifyContents) { 186 verifyFieldName(jp, "Thumbnail"); 187 } 188 189 assertToken(JsonToken.START_OBJECT, jp.nextToken()); // 'thumbnail' object 190 assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Url' 191 if (verifyContents) { 192 verifyFieldName(jp, "Url"); 193 } 194 assertToken(JsonToken.VALUE_STRING, jp.nextToken()); 195 if (verifyContents) { 196 assertEquals(SAMPLE_SPEC_VALUE_TN_URL, getAndVerifyText(jp)); 197 } 198 assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Height' 199 if (verifyContents) { 200 verifyFieldName(jp, "Height"); 201 } 202 verifyIntToken(jp.nextToken(), requireNumbers); 203 if (verifyContents) { 204 verifyIntValue(jp, SAMPLE_SPEC_VALUE_TN_HEIGHT); 205 } 206 assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Width' 207 if (verifyContents) { 208 verifyFieldName(jp, "Width"); 209 } 210 // Width value is actually a String in the example 211 assertToken(JsonToken.VALUE_STRING, jp.nextToken()); 212 if (verifyContents) { 213 assertEquals(SAMPLE_SPEC_VALUE_TN_WIDTH, getAndVerifyText(jp)); 214 } 215 216 assertToken(JsonToken.END_OBJECT, jp.nextToken()); // 'thumbnail' object 217 assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'IDs' 218 assertToken(JsonToken.START_ARRAY, jp.nextToken()); // 'ids' array 219 verifyIntToken(jp.nextToken(), requireNumbers); // ids[0] 220 if (verifyContents) { 221 verifyIntValue(jp, SAMPLE_SPEC_VALUE_TN_ID1); 222 } 223 verifyIntToken(jp.nextToken(), requireNumbers); // ids[1] 224 if (verifyContents) { 225 verifyIntValue(jp, SAMPLE_SPEC_VALUE_TN_ID2); 226 } 227 verifyIntToken(jp.nextToken(), requireNumbers); // ids[2] 228 if (verifyContents) { 229 verifyIntValue(jp, SAMPLE_SPEC_VALUE_TN_ID3); 230 } 231 verifyIntToken(jp.nextToken(), requireNumbers); // ids[3] 232 if (verifyContents) { 233 verifyIntValue(jp, SAMPLE_SPEC_VALUE_TN_ID4); 234 } 235 assertToken(JsonToken.END_ARRAY, jp.nextToken()); // 'ids' array 236 237 assertToken(JsonToken.END_OBJECT, jp.nextToken()); // 'image' object 238 239 assertToken(JsonToken.END_OBJECT, jp.nextToken()); // main object 240 } 241 verifyIntToken(JsonToken t, boolean requireNumbers)242 private void verifyIntToken(JsonToken t, boolean requireNumbers) 243 { 244 if (t == JsonToken.VALUE_NUMBER_INT) { 245 return; 246 } 247 if (requireNumbers) { // to get error 248 assertToken(JsonToken.VALUE_NUMBER_INT, t); 249 } 250 // if not number, must be String 251 if (t != JsonToken.VALUE_STRING) { 252 fail("Expected INT or STRING value, got "+t); 253 } 254 } 255 verifyFieldName(JsonParser p, String expName)256 protected void verifyFieldName(JsonParser p, String expName) 257 throws IOException 258 { 259 assertEquals(expName, p.getText()); 260 assertEquals(expName, p.currentName()); 261 } 262 verifyIntValue(JsonParser p, long expValue)263 protected void verifyIntValue(JsonParser p, long expValue) 264 throws IOException 265 { 266 // First, via textual 267 assertEquals(String.valueOf(expValue), p.getText()); 268 } 269 270 /* 271 /********************************************************** 272 /* Parser/generator construction 273 /********************************************************** 274 */ 275 createParserUsingReader(String input)276 protected JsonParser createParserUsingReader(String input) 277 throws IOException, JsonParseException 278 { 279 return createParserUsingReader(new JsonFactory(), input); 280 } 281 createParserUsingReader(JsonFactory f, String input)282 protected JsonParser createParserUsingReader(JsonFactory f, String input) 283 throws IOException 284 { 285 return f.createParser(new StringReader(input)); 286 } 287 createParserUsingStream(String input, String encoding)288 protected JsonParser createParserUsingStream(String input, String encoding) 289 throws IOException 290 { 291 return createParserUsingStream(new JsonFactory(), input, encoding); 292 } 293 createParserUsingStream(JsonFactory f, String input, String encoding)294 protected JsonParser createParserUsingStream(JsonFactory f, 295 String input, String encoding) 296 throws IOException 297 { 298 /* 23-Apr-2008, tatus: UTF-32 is not supported by JDK, have to 299 * use our own codec too (which is not optimal since there's 300 * a chance both encoder and decoder might have bugs, but ones 301 * that cancel each other out or such) 302 */ 303 byte[] data; 304 if (encoding.equalsIgnoreCase("UTF-32")) { 305 data = encodeInUTF32BE(input); 306 } else { 307 data = input.getBytes(encoding); 308 } 309 InputStream is = new ByteArrayInputStream(data); 310 return f.createParser(is); 311 } 312 313 /* 314 /********************************************************** 315 /* Additional assertion methods 316 /********************************************************** 317 */ 318 assertToken(JsonToken expToken, JsonToken actToken)319 protected void assertToken(JsonToken expToken, JsonToken actToken) 320 { 321 if (actToken != expToken) { 322 fail("Expected token "+expToken+", current token "+actToken); 323 } 324 } 325 assertToken(JsonToken expToken, JsonParser jp)326 protected void assertToken(JsonToken expToken, JsonParser jp) 327 { 328 assertToken(expToken, jp.currentToken()); 329 } 330 assertType(Object ob, Class<?> expType)331 protected void assertType(Object ob, Class<?> expType) 332 { 333 if (ob == null) { 334 fail("Expected an object of type "+expType.getName()+", got null"); 335 } 336 Class<?> cls = ob.getClass(); 337 if (!expType.isAssignableFrom(cls)) { 338 fail("Expected type "+expType.getName()+", got "+cls.getName()); 339 } 340 } 341 assertValidLocation(JsonLocation location)342 protected void assertValidLocation(JsonLocation location) { 343 assertNotNull("Should have non-null location", location); 344 assertTrue("Should have positive line number", location.getLineNr() > 0); 345 } 346 verifyException(Throwable e, String... matches)347 public static void verifyException(Throwable e, String... matches) 348 { 349 String msg = e.getMessage(); 350 String lmsg = (msg == null) ? "" : msg.toLowerCase(); 351 for (String match : matches) { 352 String lmatch = match.toLowerCase(); 353 if (lmsg.indexOf(lmatch) >= 0) { 354 return; 355 } 356 } 357 fail("Expected an exception with one of substrings (" 358 +Arrays.asList(matches)+"): got one (of type "+e.getClass().getName() 359 +") with message \""+msg+"\""); 360 } 361 362 /** 363 * Method that gets textual contents of the current token using 364 * available methods, and ensures results are consistent, before 365 * returning them 366 */ getAndVerifyText(JsonParser jp)367 protected String getAndVerifyText(JsonParser jp) 368 throws IOException, JsonParseException 369 { 370 // Ok, let's verify other accessors 371 int actLen = jp.getTextLength(); 372 char[] ch = jp.getTextCharacters(); 373 String str2 = new String(ch, jp.getTextOffset(), actLen); 374 String str = jp.getText(); 375 376 if (str.length() != actLen) { 377 fail("Internal problem (jp.token == "+jp.currentToken()+"): jp.getText().length() ['"+str+"'] == "+str.length()+"; jp.getTextLength() == "+actLen); 378 } 379 assertEquals("String access via getText(), getTextXxx() must be the same", str, str2); 380 381 return str; 382 } 383 384 /* 385 /********************************************************** 386 /* And other helpers 387 /********************************************************** 388 */ 389 encodeInUTF32BE(String input)390 protected byte[] encodeInUTF32BE(String input) 391 { 392 int len = input.length(); 393 byte[] result = new byte[len * 4]; 394 int ptr = 0; 395 for (int i = 0; i < len; ++i, ptr += 4) { 396 char c = input.charAt(i); 397 result[ptr] = result[ptr+1] = (byte) 0; 398 result[ptr+2] = (byte) (c >> 8); 399 result[ptr+3] = (byte) c; 400 } 401 return result; 402 } 403 quote(String str)404 public String quote(String str) { 405 return '"'+str+'"'; 406 } 407 } 408