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.protocol;
20 
21 import java.nio.ByteBuffer;
22 import java.util.Arrays;
23 import java.util.List;
24 
25 import junit.framework.TestCase;
26 
27 import org.apache.thrift.Fixtures;
28 import org.apache.thrift.TBase;
29 import org.apache.thrift.TDeserializer;
30 import org.apache.thrift.TException;
31 import org.apache.thrift.TSerializer;
32 import org.apache.thrift.transport.TMemoryBuffer;
33 
34 import thrift.test.CompactProtoTestStruct;
35 import thrift.test.HolyMoley;
36 import thrift.test.Nesting;
37 import thrift.test.OneOfEach;
38 import thrift.test.Srv;
39 
40 public abstract class ProtocolTestBase extends TestCase {
41 
42   /** Does it make sense to call methods like writeI32 directly on your protocol? */
canBeUsedNaked()43   protected abstract boolean canBeUsedNaked();
44 
45   /** The protocol factory for the protocol being tested. */
getFactory()46   protected abstract TProtocolFactory getFactory();
47 
testDouble()48   public void testDouble() throws Exception {
49     if (canBeUsedNaked()) {
50       TMemoryBuffer buf = new TMemoryBuffer(1000);
51       TProtocol proto = getFactory().getProtocol(buf);
52       proto.writeDouble(123.456);
53       assertEquals(123.456, proto.readDouble());
54     }
55 
56     internalTestStructField(new StructFieldTestCase(TType.DOUBLE, (short)15) {
57       @Override
58       public void readMethod(TProtocol proto) throws TException {
59         assertEquals(123.456, proto.readDouble());
60       }
61 
62       @Override
63       public void writeMethod(TProtocol proto) throws TException {
64         proto.writeDouble(123.456);
65       }
66     });
67   }
68 
testSerialization()69   public void testSerialization() throws Exception {
70     internalTestSerialization(OneOfEach.class, Fixtures.oneOfEach);
71     internalTestSerialization(Nesting.class, Fixtures.nesting);
72     internalTestSerialization(HolyMoley.class, Fixtures.holyMoley);
73     internalTestSerialization(CompactProtoTestStruct.class, Fixtures.compactProtoTestStruct);
74   }
75 
testBinary()76   public void testBinary() throws Exception {
77     for (byte[] b : Arrays.asList(new byte[0],
78                                   new byte[]{0,1,2,3,4,5,6,7,8,9,10},
79                                   new byte[]{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14},
80                                   new byte[]{0x5D},
81                                   new byte[]{(byte)0xD5,(byte)0x5D},
82                                   new byte[]{(byte)0xFF,(byte)0xD5,(byte)0x5D},
83                                   new byte[128])) {
84       if (canBeUsedNaked()) {
85         internalTestNakedBinary(b);
86       }
87       internalTestBinaryField(b);
88     }
89 
90     if (canBeUsedNaked()) {
91       byte[] data = {1, 2, 3, 4, 5, 6};
92 
93       TMemoryBuffer buf = new TMemoryBuffer(0);
94       TProtocol proto = getFactory().getProtocol(buf);
95       ByteBuffer bb = ByteBuffer.wrap(data);
96       bb.get();
97       proto.writeBinary(bb.slice());
98       assertEquals(ByteBuffer.wrap(data, 1, 5), proto.readBinary());
99     }
100   }
101 
testString()102   public void testString() throws Exception {
103     for (String s : Arrays.asList("", "short", "borderlinetiny", "a bit longer than the smallest possible")) {
104       if (canBeUsedNaked()) {
105         internalTestNakedString(s);
106       }
107       internalTestStringField(s);
108     }
109   }
110 
testLong()111   public void testLong() throws Exception {
112     if (canBeUsedNaked()) {
113       internalTestNakedI64(0);
114     }
115     internalTestI64Field(0);
116     for (int i = 0; i < 62; i++) {
117       if (canBeUsedNaked()) {
118         internalTestNakedI64(1L << i);
119         internalTestNakedI64(-(1L << i));
120       }
121       internalTestI64Field(1L << i);
122       internalTestI64Field(-(1L << i));
123     }
124   }
125 
testInt()126   public void testInt() throws Exception {
127     for (int i : Arrays.asList(0, 1, 7, 150, 15000, 31337, 0xffff, 0xffffff, -1, -7, -150, -15000, -0xffff, -0xffffff)) {
128       if (canBeUsedNaked()) {
129         internalTestNakedI32(i);
130       }
131       internalTestI32Field(i);
132     }
133   }
134 
testShort()135   public void testShort() throws Exception {
136     for (int s : Arrays.asList(0, 1, 7, 150, 15000, 0x7fff, -1, -7, -150, -15000, -0x7fff)) {
137       if (canBeUsedNaked()) {
138         internalTestNakedI16((short)s);
139       }
140       internalTestI16Field((short)s);
141     }
142   }
143 
testByte()144   public void testByte() throws Exception {
145     if (canBeUsedNaked()) {
146       internalTestNakedByte();
147     }
148     for (int i = 0; i < 128; i++) {
149       internalTestByteField((byte)i);
150       internalTestByteField((byte)-i);
151     }
152   }
153 
internalTestNakedByte()154   private void internalTestNakedByte() throws Exception {
155     TMemoryBuffer buf = new TMemoryBuffer(1000);
156     TProtocol proto = getFactory().getProtocol(buf);
157     proto.writeByte((byte)123);
158     assertEquals((byte) 123, proto.readByte());
159   }
160 
internalTestByteField(final byte b)161   private void internalTestByteField(final byte b) throws Exception {
162     internalTestStructField(new StructFieldTestCase(TType.BYTE, (short)15) {
163       public void writeMethod(TProtocol proto) throws TException {
164         proto.writeByte(b);
165       }
166 
167       public void readMethod(TProtocol proto) throws TException {
168         assertEquals((byte)b, proto.readByte());
169       }
170     });
171   }
172 
internalTestNakedI16(short n)173   private void internalTestNakedI16(short n) throws Exception {
174     TMemoryBuffer buf = new TMemoryBuffer(0);
175     TProtocol proto = getFactory().getProtocol(buf);
176     proto.writeI16(n);
177     assertEquals(n, proto.readI16());
178   }
179 
internalTestI16Field(final short n)180   private void internalTestI16Field(final short n) throws Exception {
181     internalTestStructField(new StructFieldTestCase(TType.I16, (short)15) {
182       public void writeMethod(TProtocol proto) throws TException {
183         proto.writeI16(n);
184       }
185 
186       public void readMethod(TProtocol proto) throws TException {
187         assertEquals(n, proto.readI16());
188       }
189     });
190   }
191 
internalTestNakedI32(int n)192   private void internalTestNakedI32(int n) throws Exception {
193     TMemoryBuffer buf = new TMemoryBuffer(0);
194     TProtocol proto = getFactory().getProtocol(buf);
195     proto.writeI32(n);
196     assertEquals(n, proto.readI32());
197   }
198 
internalTestI32Field(final int n)199   private void internalTestI32Field(final int n) throws Exception {
200     internalTestStructField(new StructFieldTestCase(TType.I32, (short)15) {
201       public void writeMethod(TProtocol proto) throws TException {
202         proto.writeI32(n);
203       }
204 
205       public void readMethod(TProtocol proto) throws TException {
206         assertEquals(n, proto.readI32());
207       }
208     });
209   }
210 
internalTestNakedI64(long n)211   private void internalTestNakedI64(long n) throws Exception {
212     TMemoryBuffer buf = new TMemoryBuffer(0);
213     TProtocol proto = getFactory().getProtocol(buf);
214     proto.writeI64(n);
215     assertEquals(n, proto.readI64());
216   }
217 
internalTestI64Field(final long n)218   private void internalTestI64Field(final long n) throws Exception {
219     internalTestStructField(new StructFieldTestCase(TType.I64, (short)15) {
220       public void writeMethod(TProtocol proto) throws TException {
221         proto.writeI64(n);
222       }
223 
224       public void readMethod(TProtocol proto) throws TException {
225         assertEquals(n, proto.readI64());
226       }
227     });
228   }
229 
internalTestNakedString(String str)230   private void internalTestNakedString(String str) throws Exception {
231     TMemoryBuffer buf = new TMemoryBuffer(0);
232     TProtocol proto = getFactory().getProtocol(buf);
233     proto.writeString(str);
234     assertEquals(str, proto.readString());
235   }
236 
internalTestStringField(final String str)237   private void internalTestStringField(final String str) throws Exception {
238     internalTestStructField(new StructFieldTestCase(TType.STRING, (short)15) {
239       public void writeMethod(TProtocol proto) throws TException {
240         proto.writeString(str);
241       }
242 
243       public void readMethod(TProtocol proto) throws TException {
244         assertEquals(str, proto.readString());
245       }
246     });
247   }
248 
internalTestNakedBinary(byte[] data)249   private void internalTestNakedBinary(byte[] data) throws Exception {
250     TMemoryBuffer buf = new TMemoryBuffer(0);
251     TProtocol proto = getFactory().getProtocol(buf);
252     proto.writeBinary(ByteBuffer.wrap(data));
253     assertEquals(ByteBuffer.wrap(data), proto.readBinary());
254   }
255 
internalTestBinaryField(final byte[] data)256   private void internalTestBinaryField(final byte[] data) throws Exception {
257     internalTestStructField(new StructFieldTestCase(TType.STRING, (short)15) {
258       public void writeMethod(TProtocol proto) throws TException {
259         proto.writeBinary(ByteBuffer.wrap(data));
260       }
261 
262       public void readMethod(TProtocol proto) throws TException {
263         assertEquals(ByteBuffer.wrap(data), proto.readBinary());
264       }
265     });
266   }
267 
internalTestSerialization(Class<T> klass, T expected)268   private <T extends TBase> void internalTestSerialization(Class<T> klass, T expected) throws Exception {
269     TMemoryBuffer buf = new TMemoryBuffer(0);
270     TBinaryProtocol binproto = new TBinaryProtocol(buf);
271 
272     expected.write(binproto);
273 
274     buf = new TMemoryBuffer(0);
275     TProtocol proto = getFactory().getProtocol(buf);
276 
277     expected.write(proto);
278     System.out.println("Size in " +  proto.getClass().getSimpleName() + ": " + buf.length());
279 
280     T actual = klass.newInstance();
281     actual.read(proto);
282     assertEquals(expected, actual);
283   }
284 
testMessage()285   public void testMessage() throws Exception {
286     List<TMessage> msgs = Arrays.asList(new TMessage[]{
287       new TMessage("short message name", TMessageType.CALL, 0),
288       new TMessage("1", TMessageType.REPLY, 12345),
289       new TMessage("loooooooooooooooooooooooooooooooooong", TMessageType.EXCEPTION, 1 << 16),
290       new TMessage("Janky", TMessageType.CALL, 0),
291     });
292 
293     for (TMessage msg : msgs) {
294       TMemoryBuffer buf = new TMemoryBuffer(0);
295       TProtocol proto = getFactory().getProtocol(buf);
296       TMessage output = null;
297 
298       proto.writeMessageBegin(msg);
299       proto.writeMessageEnd();
300 
301       output = proto.readMessageBegin();
302 
303       assertEquals(msg, output);
304     }
305   }
306 
testServerRequest()307   public void testServerRequest() throws Exception {
308     Srv.Iface handler = new Srv.Iface() {
309       public int Janky(int i32arg) throws TException {
310         return i32arg * 2;
311       }
312 
313       public int primitiveMethod() throws TException {
314         return 0;
315       }
316 
317       public CompactProtoTestStruct structMethod() throws TException {
318         return null;
319       }
320 
321       public void voidMethod() throws TException {
322       }
323 
324       public void methodWithDefaultArgs(int something) throws TException {
325       }
326 
327       @Override
328       public void onewayMethod() throws TException {
329       }
330 
331       @Override
332       public boolean declaredExceptionMethod(boolean shouldThrow) throws TException {
333         return shouldThrow;
334       }
335     };
336 
337     Srv.Processor testProcessor = new Srv.Processor(handler);
338 
339     TMemoryBuffer clientOutTrans = new TMemoryBuffer(0);
340     TProtocol clientOutProto = getFactory().getProtocol(clientOutTrans);
341     TMemoryBuffer clientInTrans = new TMemoryBuffer(0);
342     TProtocol clientInProto = getFactory().getProtocol(clientInTrans);
343 
344     Srv.Client testClient = new Srv.Client(clientInProto, clientOutProto);
345 
346     testClient.send_Janky(1);
347     // System.out.println(clientOutTrans.inspect());
348     testProcessor.process(clientOutProto, clientInProto);
349     // System.out.println(clientInTrans.inspect());
350     assertEquals(2, testClient.recv_Janky());
351   }
352 
testTDeserializer()353   public void testTDeserializer() throws TException {
354     TSerializer ser = new TSerializer(getFactory());
355     byte[] bytes = ser.serialize(Fixtures.compactProtoTestStruct);
356 
357     TDeserializer deser = new TDeserializer(getFactory());
358     CompactProtoTestStruct cpts = new CompactProtoTestStruct();
359     deser.deserialize(cpts, bytes);
360 
361     assertEquals(Fixtures.compactProtoTestStruct, cpts);
362   }
363 
364   //
365   // Helper methods
366   //
367 
internalTestStructField(StructFieldTestCase testCase)368   private void internalTestStructField(StructFieldTestCase testCase) throws Exception {
369     TMemoryBuffer buf = new TMemoryBuffer(0);
370     TProtocol proto = getFactory().getProtocol(buf);
371 
372     TField field = new TField("test_field", testCase.type_, testCase.id_);
373     proto.writeStructBegin(new TStruct("test_struct"));
374     proto.writeFieldBegin(field);
375     testCase.writeMethod(proto);
376     proto.writeFieldEnd();
377     proto.writeStructEnd();
378 
379     proto.readStructBegin();
380     TField readField = proto.readFieldBegin();
381     assertEquals(testCase.id_, readField.id);
382     assertEquals(testCase.type_, readField.type);
383     testCase.readMethod(proto);
384     proto.readStructEnd();
385   }
386 
387   private static abstract class StructFieldTestCase {
388     byte type_;
389     short id_;
StructFieldTestCase(byte type, short id)390     public StructFieldTestCase(byte type, short id) {
391       type_ = type;
392       id_ = id;
393     }
394 
writeMethod(TProtocol proto)395     public abstract void writeMethod(TProtocol proto) throws TException;
readMethod(TProtocol proto)396     public abstract void readMethod(TProtocol proto) throws TException;
397   }
398 
399   private static final int NUM_TRIALS = 5;
400   private static final int NUM_REPS = 10000;
401 
benchmark()402   protected void benchmark() throws Exception {
403     for (int trial = 0; trial < NUM_TRIALS; trial++) {
404       TSerializer ser = new TSerializer(getFactory());
405       byte[] serialized = null;
406       long serStart = System.currentTimeMillis();
407       for (int rep = 0; rep < NUM_REPS; rep++) {
408         serialized = ser.serialize(Fixtures.holyMoley);
409       }
410       long serEnd = System.currentTimeMillis();
411       long serElapsed = serEnd - serStart;
412       System.out.println("Ser:\t" + serElapsed + "ms\t"
413           + ((double)serElapsed / NUM_REPS) + "ms per serialization");
414 
415       HolyMoley cpts = new HolyMoley();
416       TDeserializer deser = new TDeserializer(getFactory());
417       long deserStart = System.currentTimeMillis();
418       for (int rep = 0; rep < NUM_REPS; rep++) {
419         deser.deserialize(cpts, serialized);
420       }
421       long deserEnd = System.currentTimeMillis();
422       long deserElapsed = deserEnd - deserStart;
423       System.out.println("Des:\t" + deserElapsed + "ms\t"
424           + ((double)deserElapsed / NUM_REPS) + "ms per deserialization");
425     }
426   }
427 }
428