1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19 package org.apache.thrift; 20 21 import static org.junit.jupiter.api.Assertions.assertArrayEquals; 22 import static org.junit.jupiter.api.Assertions.assertEquals; 23 import static org.junit.jupiter.api.Assertions.assertFalse; 24 import static org.junit.jupiter.api.Assertions.assertNotNull; 25 import static org.junit.jupiter.api.Assertions.assertNull; 26 import static org.junit.jupiter.api.Assertions.assertThrows; 27 import static org.junit.jupiter.api.Assertions.assertTrue; 28 29 import java.io.ByteArrayInputStream; 30 import java.io.ByteArrayOutputStream; 31 import java.io.ObjectInputStream; 32 import java.io.ObjectOutputStream; 33 import java.nio.ByteBuffer; 34 import java.util.HashMap; 35 import java.util.Map; 36 import org.apache.thrift.meta_data.FieldMetaData; 37 import org.apache.thrift.meta_data.ListMetaData; 38 import org.apache.thrift.meta_data.MapMetaData; 39 import org.apache.thrift.meta_data.SetMetaData; 40 import org.apache.thrift.meta_data.StructMetaData; 41 import org.apache.thrift.protocol.TBinaryProtocol; 42 import org.apache.thrift.protocol.TType; 43 import org.junit.jupiter.api.Test; 44 import thrift.test.Bonk; 45 import thrift.test.CrazyNesting; 46 import thrift.test.HolyMoley; 47 import thrift.test.Insanity; 48 import thrift.test.JavaTestHelper; 49 import thrift.test.Nesting; 50 import thrift.test.Numberz; 51 import thrift.test.OneOfEach; 52 import thrift.test.StructA; 53 import thrift.test.StructB; 54 import thrift.test.Xtruct; 55 56 public class TestStruct { 57 @Test testIdentity()58 public void testIdentity() throws Exception { 59 TSerializer binarySerializer = new TSerializer(new TBinaryProtocol.Factory()); 60 TDeserializer binaryDeserializer = new TDeserializer(new TBinaryProtocol.Factory()); 61 62 OneOfEach ooe = Fixtures.getOneOfEach(); 63 64 Nesting n = new Nesting(); 65 n.my_ooe = ooe; 66 n.my_ooe.integer16 = 16; 67 n.my_ooe.integer32 = 32; 68 n.my_ooe.integer64 = 64; 69 n.my_ooe.double_precision = (Math.sqrt(5) + 1) / 2; 70 n.my_ooe.some_characters = ":R (me going \"rrrr\")"; 71 n.my_ooe.zomg_unicode = 72 "\u04c0\u216e\u039d\u0020\u041d\u03bf\u217f" 73 + "\u043e\u0261\u0433\u0430\u03c1\u210e\u0020" 74 + "\u0391\u0074\u0074\u03b1\u217d\u03ba\u01c3" 75 + "\u203c"; 76 n.my_bonk = Fixtures.getNesting().my_bonk; 77 78 HolyMoley hm = Fixtures.getHolyMoley(); 79 80 OneOfEach ooe2 = new OneOfEach(); 81 binaryDeserializer.deserialize(ooe2, binarySerializer.serialize(ooe)); 82 83 assertEquals(ooe, ooe2); 84 assertEquals(ooe.hashCode(), ooe2.hashCode()); 85 86 Nesting n2 = new Nesting(); 87 binaryDeserializer.deserialize(n2, binarySerializer.serialize(n)); 88 89 assertEquals(n, n2); 90 assertEquals(n.hashCode(), n2.hashCode()); 91 92 HolyMoley hm2 = new HolyMoley(); 93 binaryDeserializer.deserialize(hm2, binarySerializer.serialize(hm)); 94 95 assertEquals(hm, hm2); 96 assertEquals(hm.hashCode(), hm2.hashCode()); 97 } 98 99 @Test testDeepCopy()100 public void testDeepCopy() throws Exception { 101 TSerializer binarySerializer = new TSerializer(new TBinaryProtocol.Factory()); 102 TDeserializer binaryDeserializer = new TDeserializer(new TBinaryProtocol.Factory()); 103 104 HolyMoley hm = Fixtures.getHolyMoley(); 105 106 byte[] binaryCopy = binarySerializer.serialize(hm); 107 HolyMoley hmCopy = new HolyMoley(); 108 binaryDeserializer.deserialize(hmCopy, binaryCopy); 109 HolyMoley hmCopy2 = new HolyMoley(hm); 110 111 assertEquals(hm, hmCopy); 112 assertEquals(hmCopy, hmCopy2); 113 114 // change binary value in original object 115 hm.big.get(0).base64.array()[0]++; 116 // make sure the change didn't propagate to the copied object 117 assertFalse(hm.equals(hmCopy2)); 118 hm.big.get(0).base64.array()[0]--; // undo change 119 120 hmCopy2.bonks.get("two").get(1).message = "What else?"; 121 122 assertFalse(hm.equals(hmCopy2)); 123 } 124 125 @Test testCompareTo()126 public void testCompareTo() throws Exception { 127 Bonk bonk1 = new Bonk(); 128 Bonk bonk2 = new Bonk(); 129 130 // Compare empty thrift objects. 131 assertEquals(0, bonk1.compareTo(bonk2)); 132 133 bonk1.setMessage("m"); 134 135 // Compare one thrift object with a filled in field and another without it. 136 assertTrue(bonk1.compareTo(bonk2) > 0); 137 assertTrue(bonk2.compareTo(bonk1) < 0); 138 139 // Compare both have filled-in fields. 140 bonk2.setMessage("z"); 141 assertTrue(bonk1.compareTo(bonk2) < 0); 142 assertTrue(bonk2.compareTo(bonk1) > 0); 143 144 // Compare bonk1 has a field filled in that bonk2 doesn't. 145 bonk1.setType(123); 146 assertTrue(bonk1.compareTo(bonk2) > 0); 147 assertTrue(bonk2.compareTo(bonk1) < 0); 148 149 // Compare bonk1 and bonk2 equal. 150 bonk2.setType(123); 151 bonk2.setMessage("m"); 152 assertEquals(0, bonk1.compareTo(bonk2)); 153 } 154 155 @Test 156 public void testCompareToWithDataStructures() { 157 Insanity insanity1 = new Insanity(); 158 Insanity insanity2 = new Insanity(); 159 160 // Both empty. 161 expectEquals(insanity1, insanity2); 162 163 insanity1.setUserMap(new HashMap<Numberz, Long>()); 164 // insanity1.map = {}, insanity2.map = null 165 expectGreaterThan(insanity1, insanity2); 166 167 // insanity1.map = {2:1}, insanity2.map = null 168 insanity1.getUserMap().put(Numberz.TWO, 1L); 169 expectGreaterThan(insanity1, insanity2); 170 171 // insanity1.map = {2:1}, insanity2.map = {} 172 insanity2.setUserMap(new HashMap<Numberz, Long>()); 173 expectGreaterThan(insanity1, insanity2); 174 175 // insanity1.map = {2:1}, insanity2.map = {2:2} 176 insanity2.getUserMap().put(Numberz.TWO, 2L); 177 expectLessThan(insanity1, insanity2); 178 179 // insanity1.map = {2:1, 3:5}, insanity2.map = {2:2} 180 insanity1.getUserMap().put(Numberz.THREE, 5L); 181 expectGreaterThan(insanity1, insanity2); 182 183 // insanity1.map = {2:1, 3:5}, insanity2.map = {2:1, 4:5} 184 insanity2.getUserMap().put(Numberz.TWO, 1L); 185 insanity2.getUserMap().put(Numberz.FIVE, 5L); 186 expectLessThan(insanity1, insanity2); 187 } 188 189 private void expectLessThan(Insanity insanity1, Insanity insanity2) { 190 int compareTo = insanity1.compareTo(insanity2); 191 assertTrue( 192 compareTo < 0, insanity1 + " should be less than " + insanity2 + ", but is: " + compareTo); 193 } 194 195 private void expectGreaterThan(Insanity insanity1, Insanity insanity2) { 196 int compareTo = insanity1.compareTo(insanity2); 197 assertTrue( 198 compareTo > 0, 199 insanity1 + " should be greater than " + insanity2 + ", but is: " + compareTo); 200 } 201 202 private void expectEquals(Insanity insanity1, Insanity insanity2) { 203 int compareTo = insanity1.compareTo(insanity2); 204 assertEquals( 205 0, compareTo, insanity1 + " should be equal to " + insanity2 + ", but is: " + compareTo); 206 } 207 208 @Test 209 public void testMetaData() throws Exception { 210 Map<CrazyNesting._Fields, FieldMetaData> mdMap = CrazyNesting.metaDataMap; 211 212 // Check for struct fields existence 213 assertEquals(5, mdMap.size()); 214 assertTrue(mdMap.containsKey(CrazyNesting._Fields.SET_FIELD)); 215 assertTrue(mdMap.containsKey(CrazyNesting._Fields.LIST_FIELD)); 216 assertTrue(mdMap.containsKey(CrazyNesting._Fields.STRING_FIELD)); 217 assertTrue(mdMap.containsKey(CrazyNesting._Fields.BINARY_FIELD)); 218 assertTrue(mdMap.containsKey(CrazyNesting._Fields.UUID_FIELD)); 219 220 // Check for struct fields contents 221 assertEquals("string_field", mdMap.get(CrazyNesting._Fields.STRING_FIELD).fieldName); 222 assertEquals("list_field", mdMap.get(CrazyNesting._Fields.LIST_FIELD).fieldName); 223 assertEquals("set_field", mdMap.get(CrazyNesting._Fields.SET_FIELD).fieldName); 224 assertEquals("binary_field", mdMap.get(CrazyNesting._Fields.BINARY_FIELD).fieldName); 225 assertEquals("uuid_field", mdMap.get(CrazyNesting._Fields.UUID_FIELD).fieldName); 226 227 assertEquals( 228 TFieldRequirementType.DEFAULT, 229 mdMap.get(CrazyNesting._Fields.STRING_FIELD).requirementType); 230 assertEquals( 231 TFieldRequirementType.REQUIRED, mdMap.get(CrazyNesting._Fields.LIST_FIELD).requirementType); 232 assertEquals( 233 TFieldRequirementType.OPTIONAL, mdMap.get(CrazyNesting._Fields.SET_FIELD).requirementType); 234 235 assertEquals(TType.STRING, mdMap.get(CrazyNesting._Fields.STRING_FIELD).valueMetaData.type); 236 assertFalse(mdMap.get(CrazyNesting._Fields.STRING_FIELD).valueMetaData.isBinary()); 237 assertEquals(TType.LIST, mdMap.get(CrazyNesting._Fields.LIST_FIELD).valueMetaData.type); 238 assertEquals(TType.SET, mdMap.get(CrazyNesting._Fields.SET_FIELD).valueMetaData.type); 239 assertEquals(TType.STRING, mdMap.get(CrazyNesting._Fields.BINARY_FIELD).valueMetaData.type); 240 assertEquals(TType.UUID, mdMap.get(CrazyNesting._Fields.UUID_FIELD).valueMetaData.type); 241 assertTrue(mdMap.get(CrazyNesting._Fields.BINARY_FIELD).valueMetaData.isBinary()); 242 243 // Check nested structures 244 assertTrue(mdMap.get(CrazyNesting._Fields.LIST_FIELD).valueMetaData.isContainer()); 245 246 assertFalse(mdMap.get(CrazyNesting._Fields.LIST_FIELD).valueMetaData.isStruct()); 247 248 assertEquals( 249 TType.STRUCT, 250 ((MapMetaData) 251 ((ListMetaData) 252 ((SetMetaData) 253 ((MapMetaData) 254 ((MapMetaData) 255 ((ListMetaData) 256 mdMap.get(CrazyNesting._Fields.LIST_FIELD) 257 .valueMetaData) 258 .elemMetaData) 259 .valueMetaData) 260 .valueMetaData) 261 .elemMetaData) 262 .elemMetaData) 263 .keyMetaData 264 .type); 265 266 assertEquals( 267 Insanity.class, 268 ((StructMetaData) 269 ((MapMetaData) 270 ((ListMetaData) 271 ((SetMetaData) 272 ((MapMetaData) 273 ((MapMetaData) 274 ((ListMetaData) 275 mdMap.get( 276 CrazyNesting._Fields 277 .LIST_FIELD) 278 .valueMetaData) 279 .elemMetaData) 280 .valueMetaData) 281 .valueMetaData) 282 .elemMetaData) 283 .elemMetaData) 284 .keyMetaData) 285 .structClass); 286 287 // Check that FieldMetaData contains a map with metadata for all generated struct classes 288 assertNotNull(FieldMetaData.getStructMetaDataMap(CrazyNesting.class)); 289 assertNotNull(FieldMetaData.getStructMetaDataMap(Insanity.class)); 290 assertNotNull(FieldMetaData.getStructMetaDataMap(Xtruct.class)); 291 292 assertEquals(CrazyNesting.metaDataMap, FieldMetaData.getStructMetaDataMap(CrazyNesting.class)); 293 assertEquals(Insanity.metaDataMap, FieldMetaData.getStructMetaDataMap(Insanity.class)); 294 295 for (Map.Entry<? extends TFieldIdEnum, FieldMetaData> mdEntry : mdMap.entrySet()) { 296 assertEquals(mdEntry.getKey(), CrazyNesting._Fields.findByName(mdEntry.getValue().fieldName)); 297 } 298 299 MapMetaData vmd = 300 (MapMetaData) Insanity.metaDataMap.get(Insanity._Fields.USER_MAP).valueMetaData; 301 assertTrue(vmd.valueMetaData.isTypedef()); 302 assertFalse(vmd.keyMetaData.isTypedef()); 303 } 304 305 @Test 306 public void testToString() throws Exception { 307 JavaTestHelper object = new JavaTestHelper(); 308 object.req_int = 0; 309 object.req_obj = ""; 310 311 object.req_bin = 312 ByteBuffer.wrap( 313 new byte[] { 314 0, -1, 2, -3, 4, -5, 6, -7, 8, -9, 10, -11, 12, -13, 14, -15, 16, -17, 18, -19, 20, 315 -21, 22, -23, 24, -25, 26, -27, 28, -29, 30, -31, 32, -33, 34, -35, 36, -37, 38, -39, 316 40, -41, 42, -43, 44, -45, 46, -47, 48, -49, 50, -51, 52, -53, 54, -55, 56, -57, 58, 317 -59, 60, -61, 62, -63, 64, -65, 66, -67, 68, -69, 70, -71, 72, -73, 74, -75, 76, -77, 318 78, -79, 80, -81, 82, -83, 84, -85, 86, -87, 88, -89, 90, -91, 92, -93, 94, -95, 96, 319 -97, 98, -99, 100, -101, 102, -103, 104, -105, 106, -107, 108, -109, 110, -111, 112, 320 -113, 114, -115, 116, -117, 118, -119, 120, -121, 122, -123, 124, -125, 126, -127, 321 }); 322 323 assertEquals( 324 "JavaTestHelper(req_int:0, req_obj:, req_bin:" 325 + "00 FF 02 FD 04 FB 06 F9 08 F7 0A F5 0C F3 0E F1 10 EF 12 ED 14 " 326 + "EB 16 E9 18 E7 1A E5 1C E3 1E E1 20 DF 22 DD 24 DB 26 D9 28 D7 " 327 + "2A D5 2C D3 2E D1 30 CF 32 CD 34 CB 36 C9 38 C7 3A C5 3C C3 3E " 328 + "C1 40 BF 42 BD 44 BB 46 B9 48 B7 4A B5 4C B3 4E B1 50 AF 52 AD " 329 + "54 AB 56 A9 58 A7 5A A5 5C A3 5E A1 60 9F 62 9D 64 9B 66 99 68 " 330 + "97 6A 95 6C 93 6E 91 70 8F 72 8D 74 8B 76 89 78 87 7A 85 7C 83 " 331 + "7E 81)", 332 object.toString()); 333 334 object.req_bin = 335 ByteBuffer.wrap( 336 new byte[] { 337 0, -1, 2, -3, 4, -5, 6, -7, 8, -9, 10, -11, 12, -13, 14, -15, 16, -17, 18, -19, 20, 338 -21, 22, -23, 24, -25, 26, -27, 28, -29, 30, -31, 32, -33, 34, -35, 36, -37, 38, -39, 339 40, -41, 42, -43, 44, -45, 46, -47, 48, -49, 50, -51, 52, -53, 54, -55, 56, -57, 58, 340 -59, 60, -61, 62, -63, 64, -65, 66, -67, 68, -69, 70, -71, 72, -73, 74, -75, 76, -77, 341 78, -79, 80, -81, 82, -83, 84, -85, 86, -87, 88, -89, 90, -91, 92, -93, 94, -95, 96, 342 -97, 98, -99, 100, -101, 102, -103, 104, -105, 106, -107, 108, -109, 110, -111, 112, 343 -113, 114, -115, 116, -117, 118, -119, 120, -121, 122, -123, 124, -125, 126, -127, 0, 344 }); 345 346 assertEquals( 347 "JavaTestHelper(req_int:0, req_obj:, req_bin:" 348 + "00 FF 02 FD 04 FB 06 F9 08 F7 0A F5 0C F3 0E F1 10 EF 12 ED 14 " 349 + "EB 16 E9 18 E7 1A E5 1C E3 1E E1 20 DF 22 DD 24 DB 26 D9 28 D7 " 350 + "2A D5 2C D3 2E D1 30 CF 32 CD 34 CB 36 C9 38 C7 3A C5 3C C3 3E " 351 + "C1 40 BF 42 BD 44 BB 46 B9 48 B7 4A B5 4C B3 4E B1 50 AF 52 AD " 352 + "54 AB 56 A9 58 A7 5A A5 5C A3 5E A1 60 9F 62 9D 64 9B 66 99 68 " 353 + "97 6A 95 6C 93 6E 91 70 8F 72 8D 74 8B 76 89 78 87 7A 85 7C 83 " 354 + "7E 81...)", 355 object.toString()); 356 357 object.req_bin = ByteBuffer.wrap(new byte[] {}); 358 object.setOpt_binIsSet(true); 359 360 assertEquals("JavaTestHelper(req_int:0, req_obj:, req_bin:)", object.toString()); 361 } 362 363 @Test 364 public void testBytesBufferFeatures() throws Exception { 365 366 final String testString = "testBytesBufferFeatures"; 367 final JavaTestHelper o = new JavaTestHelper(); 368 369 o.setReq_bin((ByteBuffer) null); 370 assertNull(o.getReq_bin()); 371 372 o.setReq_bin(ByteBuffer.wrap(testString.getBytes())); 373 assertArrayEquals(testString.getBytes(), o.getReq_bin()); 374 375 o.setReq_bin((byte[]) null); 376 assertNull(o.getReq_bin()); 377 378 o.setReq_bin(testString.getBytes()); 379 assertArrayEquals(testString.getBytes(), o.getReq_bin()); 380 381 o.setFieldValue(JavaTestHelper._Fields.REQ_BIN, null); 382 assertNull(o.getReq_bin()); 383 384 o.setFieldValue(JavaTestHelper._Fields.REQ_BIN, testString.getBytes()); 385 assertArrayEquals(testString.getBytes(), o.getReq_bin()); 386 387 o.setFieldValue(JavaTestHelper._Fields.REQ_BIN, null); 388 assertNull(o.getReq_bin()); 389 390 o.setFieldValue(JavaTestHelper._Fields.REQ_BIN, ByteBuffer.wrap(testString.getBytes())); 391 assertArrayEquals(testString.getBytes(), o.getReq_bin()); 392 } 393 394 @Test 395 public void testJavaSerializable() throws Exception { 396 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 397 ObjectOutputStream oos = new ObjectOutputStream(baos); 398 399 OneOfEach ooe = Fixtures.getOneOfEach(); 400 401 // Serialize ooe the Java way... 402 oos.writeObject(ooe); 403 byte[] serialized = baos.toByteArray(); 404 405 // Attempt to deserialize it 406 ByteArrayInputStream bais = new ByteArrayInputStream(serialized); 407 ObjectInputStream ois = new ObjectInputStream(bais); 408 OneOfEach ooe2 = (OneOfEach) ois.readObject(); 409 410 assertEquals(ooe, ooe2); 411 } 412 413 @Test 414 public void testSubStructValidation() throws Exception { 415 StructA valid = new StructA("valid"); 416 StructA invalid = new StructA(); 417 418 StructB b = new StructB(); 419 assertThrows(TException.class, b::validate); 420 421 b = new StructB().setAb(valid); 422 b.validate(); 423 424 b = new StructB().setAb(invalid); 425 assertThrows(TException.class, b::validate); 426 427 b = new StructB().setAb(valid).setAa(invalid); 428 assertThrows(TException.class, b::validate); 429 } 430 } 431