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