Home | History | Annotate | Download | only in cts
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.util.proto.cts;
     18 
     19 import android.util.proto.ProtoOutputStream;
     20 import android.util.proto.cts.nano.Test;
     21 
     22 import com.google.protobuf.nano.MessageNano;
     23 import junit.framework.TestCase;
     24 import org.junit.Assert;
     25 
     26 /**
     27  * Test the object methods on the ProtoOutputStream class.
     28  */
     29 public class ProtoOutputStreamObjectTest extends TestCase {
     30 
     31     // ----------------------------------------------------------------------
     32     //  Tokens
     33     // ----------------------------------------------------------------------
     34 
     35     /**
     36      * Test making the tokens for startObject.
     37      */
     38     public void testMakeToken() throws Exception {
     39         assertEquals(0xe000000000000000L, ProtoOutputStream.makeToken(0xffffffff, false, 0, 0, 0));
     40         assertEquals(0x1000000000000000L, ProtoOutputStream.makeToken(0, true, 0, 0, 0));
     41         assertEquals(0x0ff8000000000000L, ProtoOutputStream.makeToken(0, false, 0xffffffff, 0, 0));
     42         assertEquals(0x0007ffff00000000L, ProtoOutputStream.makeToken(0, false, 0, 0xffffffff, 0));
     43         assertEquals(0x00000000ffffffffL, ProtoOutputStream.makeToken(0, false, 0, 0, 0xffffffff));
     44     }
     45 
     46     /**
     47      * Test decoding the tokens.
     48      */
     49     public void testDecodeToken() throws Exception {
     50         assertEquals(0x07, ProtoOutputStream.getTagSizeFromToken(0xffffffffffffffffL));
     51         assertEquals(0, ProtoOutputStream.getTagSizeFromToken(0x1fffffffffffffffL));
     52 
     53         assertEquals(true, ProtoOutputStream.getRepeatedFromToken(0xffffffffffffffffL));
     54         assertEquals(false, ProtoOutputStream.getRepeatedFromToken(0xefffffffffffffffL));
     55 
     56         assertEquals(0x01ff, ProtoOutputStream.getDepthFromToken(0xffffffffffffffffL));
     57         assertEquals(0, ProtoOutputStream.getDepthFromToken(0xf005ffffffffffffL));
     58 
     59         assertEquals(0x07ffff, ProtoOutputStream.getObjectIdFromToken(0xffffffffffffffffL));
     60         assertEquals(0, ProtoOutputStream.getObjectIdFromToken(0xfff80000ffffffffL));
     61 
     62         assertEquals(0xffffffff, ProtoOutputStream.getSizePosFromToken(0xffffffffffffffffL));
     63         assertEquals(0, ProtoOutputStream.getSizePosFromToken(0xffffffff00000000L));
     64     }
     65 
     66     /**
     67      * Test writing an object with one char in it.
     68      */
     69     public void testObjectOneChar() {
     70         testObjectOneChar(0);
     71         testObjectOneChar(1);
     72         testObjectOneChar(5);
     73     }
     74 
     75     /**
     76      * Implementation of testObjectOneChar for a given chunkSize.
     77      */
     78     public void testObjectOneChar(int chunkSize) {
     79         final ProtoOutputStream po = new ProtoOutputStream(chunkSize);
     80 
     81         long token = po.startObject(ProtoOutputStream.makeFieldId(1,
     82                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
     83         po.writeUInt32(ProtoOutputStream.makeFieldId(2,
     84                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
     85                 'b');
     86         po.endObject(token);
     87 
     88         Assert.assertArrayEquals(new byte[] {
     89                 (byte)0x0a, (byte)0x02, (byte)0x10, (byte)0x62,
     90             }, po.getBytes());
     91     }
     92 
     93     /**
     94      * Test writing an object with one multibyte unicode char in it.
     95      */
     96     public void testObjectOneLargeChar() {
     97         testObjectOneLargeChar(0);
     98         testObjectOneLargeChar(1);
     99         testObjectOneLargeChar(5);
    100     }
    101 
    102     /**
    103      * Implementation of testObjectOneLargeChar for a given chunkSize.
    104      */
    105     public void testObjectOneLargeChar(int chunkSize) {
    106         final ProtoOutputStream po = new ProtoOutputStream(chunkSize);
    107 
    108         long token = po.startObject(ProtoOutputStream.makeFieldId(1,
    109                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
    110         po.writeUInt32(ProtoOutputStream.makeFieldId(5000,
    111                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
    112                 '\u3110');
    113         po.endObject(token);
    114 
    115         Assert.assertArrayEquals(new byte[] {
    116                 (byte)0x0a, (byte)0x05, (byte)0xc0, (byte)0xb8,
    117                 (byte)0x02, (byte)0x90, (byte)0x62,
    118             }, po.getBytes());
    119     }
    120 
    121     /**
    122      * Test writing a char, then an object, then a char.
    123      */
    124     public void testObjectAndTwoChars() {
    125         testObjectAndTwoChars(0);
    126         testObjectAndTwoChars(1);
    127         testObjectAndTwoChars(5);
    128     }
    129 
    130     /**
    131      * Implementation of testObjectAndTwoChars for a given chunkSize.
    132      */
    133     public void testObjectAndTwoChars(int chunkSize) {
    134         final ProtoOutputStream po = new ProtoOutputStream(chunkSize);
    135 
    136         po.writeUInt32(ProtoOutputStream.makeFieldId(1,
    137                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
    138                 'a');
    139 
    140         long token = po.startObject(ProtoOutputStream.makeFieldId(2,
    141                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
    142         po.writeUInt32(ProtoOutputStream.makeFieldId(3,
    143                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
    144                 'b');
    145         po.endObject(token);
    146 
    147         po.writeUInt32(ProtoOutputStream.makeFieldId(4,
    148                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
    149                 'c');
    150 
    151         Assert.assertArrayEquals(new byte[] {
    152                 // 1 -> 'a'
    153                 (byte)0x08, (byte)0x61,
    154                 // begin object 1
    155                 (byte)0x12, (byte)0x02,
    156                     // 3 -> 'b'
    157                     (byte)0x18, (byte)0x62,
    158                 // 4 -> 'c'
    159                 (byte)0x20, (byte)0x63,
    160             }, po.getBytes());
    161     }
    162 
    163     /**
    164      * Test writing an object with nothing in it.
    165      */
    166     public void testEmptyObject() {
    167         testEmptyObject(0);
    168         testEmptyObject(1);
    169         testEmptyObject(5);
    170     }
    171 
    172     /**
    173      * Implementation of testEmptyObject for a given chunkSize.
    174      * Nothing should be written.
    175      */
    176     public void testEmptyObject(int chunkSize) {
    177         final ProtoOutputStream po = new ProtoOutputStream(chunkSize);
    178 
    179         long token = po.startObject(ProtoOutputStream.makeFieldId(1,
    180                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
    181         po.endObject(token);
    182 
    183         Assert.assertArrayEquals(new byte[0], po.getBytes());
    184     }
    185 
    186     /**
    187      * Test writing 3 levels deep of objects with nothing in them.
    188      */
    189     public void testDeepEmptyObjects() {
    190         testDeepEmptyObjects(0);
    191         testDeepEmptyObjects(1);
    192         testDeepEmptyObjects(5);
    193     }
    194 
    195     /**
    196      * Implementation of testDeepEmptyObjects for a given chunkSize.
    197      */
    198     public void testDeepEmptyObjects(int chunkSize) {
    199         final ProtoOutputStream po = new ProtoOutputStream(chunkSize);
    200 
    201         long token1 = po.startObject(ProtoOutputStream.makeFieldId(1,
    202                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
    203         long token2 = po.startObject(ProtoOutputStream.makeFieldId(2,
    204                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
    205         long token3 = po.startObject(ProtoOutputStream.makeFieldId(3,
    206                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
    207         po.endObject(token3);
    208         po.endObject(token2);
    209         po.endObject(token1);
    210 
    211         Assert.assertArrayEquals(new byte[0], po.getBytes());
    212     }
    213 
    214     /**
    215      * Test writing a char, then an object with nothing in it, then a char.
    216      */
    217     public void testEmptyObjectAndTwoChars() {
    218         testEmptyObjectAndTwoChars(0);
    219         testEmptyObjectAndTwoChars(1);
    220         testEmptyObjectAndTwoChars(5);
    221     }
    222 
    223     /**
    224      * Implementation of testEmptyObjectAndTwoChars for a given chunkSize.
    225      */
    226     public void testEmptyObjectAndTwoChars(int chunkSize) {
    227         final ProtoOutputStream po = new ProtoOutputStream(chunkSize);
    228 
    229         po.writeUInt32(ProtoOutputStream.makeFieldId(1,
    230                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
    231                 'a');
    232 
    233         long token = po.startObject(ProtoOutputStream.makeFieldId(2,
    234                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
    235         po.endObject(token);
    236 
    237         po.writeUInt32(ProtoOutputStream.makeFieldId(4,
    238                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
    239                 'c');
    240 
    241         Assert.assertArrayEquals(new byte[] {
    242                 // 1 -> 'a'
    243                 (byte)0x08, (byte)0x61,
    244                 // 4 -> 'c'
    245                 (byte)0x20, (byte)0x63,
    246             }, po.getBytes());
    247     }
    248 
    249     /**
    250      * Test empty repeated objects.  For repeated objects, we write an empty header.
    251      */
    252     public void testEmptyRepeatedObject() {
    253         testEmptyRepeatedObject(0);
    254         testEmptyRepeatedObject(1);
    255         testEmptyRepeatedObject(5);
    256     }
    257 
    258     /**
    259      * Implementation of testEmptyRepeatedObject for a given chunkSize.
    260      */
    261     public void testEmptyRepeatedObject(int chunkSize) {
    262         final ProtoOutputStream po = new ProtoOutputStream(chunkSize);
    263         long token;
    264 
    265         token = po.startRepeatedObject(ProtoOutputStream.makeFieldId(1,
    266                     ProtoOutputStream.FIELD_COUNT_REPEATED | ProtoOutputStream.FIELD_TYPE_MESSAGE));
    267         po.endRepeatedObject(token);
    268 
    269         token = po.startRepeatedObject(ProtoOutputStream.makeFieldId(1,
    270                     ProtoOutputStream.FIELD_COUNT_REPEATED | ProtoOutputStream.FIELD_TYPE_MESSAGE));
    271         po.endRepeatedObject(token);
    272 
    273         Assert.assertArrayEquals(new byte[] {
    274                 // 1 -> empty (tag, size)
    275                 (byte)0x0a, (byte)0x00,
    276                 // 1 -> empty (tag, size)
    277                 (byte)0x0a, (byte)0x00,
    278             }, po.getBytes());
    279     }
    280 
    281 
    282     /**
    283      * Test writing a char, then an object with an int and a string in it, then a char.
    284      */
    285     public void testComplexObject() {
    286         testComplexObject(0);
    287         testComplexObject(1);
    288         testComplexObject(5);
    289     }
    290 
    291     /**
    292      * Implementation of testComplexObject for a given chunkSize.
    293      */
    294     public void testComplexObject(int chunkSize) {
    295         final ProtoOutputStream po = new ProtoOutputStream(chunkSize);
    296 
    297         po.writeUInt32(ProtoOutputStream.makeFieldId(1,
    298                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
    299                 'x');
    300 
    301         long token = po.startObject(ProtoOutputStream.makeFieldId(2,
    302                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
    303         po.writeUInt32(ProtoOutputStream.makeFieldId(3,
    304                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
    305                 'y');
    306         po.writeString(ProtoOutputStream.makeFieldId(4,
    307                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_STRING),
    308                 "abcdefghijkl");
    309 
    310         long tokenEmpty = po.startObject(ProtoOutputStream.makeFieldId(500,
    311                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
    312         po.endObject(tokenEmpty);
    313 
    314         po.endObject(token);
    315 
    316         po.writeUInt32(ProtoOutputStream.makeFieldId(5,
    317                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
    318                 'z');
    319 
    320         Assert.assertArrayEquals(new byte[] {
    321                 // 1 -> 'x'
    322                 (byte)0x08, (byte)0x78,
    323                 // begin object 1
    324                 (byte)0x12, (byte)0x10,
    325                     // 3 -> 'y'
    326                     (byte)0x18, (byte)0x79,
    327                     // 4 -> "abcdefghijkl"
    328                     (byte)0x22, (byte)0x0c,
    329                         (byte)0x61, (byte)0x62, (byte)0x63, (byte)0x64, (byte)0x65, (byte)0x66,
    330                         (byte)0x67, (byte)0x68, (byte)0x69, (byte)0x6a, (byte)0x6b, (byte)0x6c,
    331                 // 4 -> 'z'
    332                 (byte)0x28, (byte)0x7a,
    333             }, po.getBytes());
    334     }
    335 
    336     /**
    337      * Test writing 3 levels deep of objects.
    338      */
    339     public void testDeepObjects() {
    340         testDeepObjects(0);
    341         testDeepObjects(1);
    342         testDeepObjects(5);
    343     }
    344 
    345     /**
    346      * Implementation of testDeepObjects for a given chunkSize.
    347      */
    348     public void testDeepObjects(int chunkSize) {
    349         final ProtoOutputStream po = new ProtoOutputStream(chunkSize);
    350 
    351         long token1 = po.startObject(ProtoOutputStream.makeFieldId(1,
    352                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
    353         po.writeUInt32(ProtoOutputStream.makeFieldId(2,
    354                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
    355                 'a');
    356 
    357         long token2 = po.startObject(ProtoOutputStream.makeFieldId(3,
    358                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
    359         po.writeUInt32(ProtoOutputStream.makeFieldId(4,
    360                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
    361                 'b');
    362 
    363         long token3 = po.startObject(ProtoOutputStream.makeFieldId(5,
    364                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
    365         po.writeUInt32(ProtoOutputStream.makeFieldId(6,
    366                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
    367                 'c');
    368 
    369         po.endObject(token3);
    370         po.endObject(token2);
    371         po.endObject(token1);
    372 
    373         Assert.assertArrayEquals(new byte[] {
    374                 // begin object 1
    375                 (byte)0x0a, (byte)0x0a,
    376                     // 2 -> 'a'
    377                     (byte)0x10, (byte)0x61,
    378                     // begin object 3
    379                     (byte)0x1a, (byte)0x06,
    380                         // 4 -> 'b'
    381                         (byte)0x20, (byte)0x62,
    382                         // begin object 5
    383                         (byte)0x2a, (byte)0x02,
    384                             // 6 -> 'c'
    385                             (byte)0x30, (byte)0x63,
    386             }, po.getBytes());
    387     }
    388 
    389     /**
    390      * Test mismatched startObject / endObject calls: too many endObject
    391      * with objects that have data.
    392      */
    393     public void testTooManyEndObjectsWithData() throws Exception {
    394         final ProtoOutputStream po = new ProtoOutputStream();
    395 
    396         long token1 = po.startObject(ProtoOutputStream.makeFieldId(1,
    397                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
    398         po.writeUInt32(ProtoOutputStream.makeFieldId(2,
    399                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
    400                 'a');
    401 
    402         long token2 = po.startObject(ProtoOutputStream.makeFieldId(3,
    403                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
    404         po.writeUInt32(ProtoOutputStream.makeFieldId(4,
    405                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
    406                 'b');
    407 
    408         po.endObject(token2);
    409         try {
    410             po.endObject(token2);
    411             throw new Exception("endObject didn't throw");
    412         } catch (RuntimeException ex) {
    413             // Good
    414         }
    415     }
    416 
    417     /**
    418      * Test mismatched startObject / endObject calls: too many endObject
    419      * with empty objects
    420      */
    421     public void testTooManyEndObjectsWithoutData() throws Exception {
    422         final ProtoOutputStream po = new ProtoOutputStream();
    423 
    424         long token1 = po.startObject(ProtoOutputStream.makeFieldId(1,
    425                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
    426 
    427         long token2 = po.startObject(ProtoOutputStream.makeFieldId(3,
    428                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
    429 
    430         po.endObject(token2);
    431         try {
    432             po.endObject(token2);
    433             throw new Exception("endObject didn't throw");
    434         } catch (RuntimeException ex) {
    435             // Good
    436         }
    437     }
    438 
    439     /**
    440      * Test mismatched startObject / endObject calls: Trailing startObject
    441      * with objects that have data.
    442      */
    443     public void testTrailingStartObjectWithData() throws Exception {
    444         final ProtoOutputStream po = new ProtoOutputStream();
    445 
    446         long token1 = po.startObject(ProtoOutputStream.makeFieldId(1,
    447                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
    448         po.writeUInt32(ProtoOutputStream.makeFieldId(2,
    449                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
    450                 'a');
    451 
    452         long token2 = po.startObject(ProtoOutputStream.makeFieldId(3,
    453                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
    454         po.writeUInt32(ProtoOutputStream.makeFieldId(4,
    455                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
    456                 'b');
    457 
    458         po.endObject(token2);
    459         try {
    460             po.getBytes();
    461             throw new Exception("getBytes didn't throw");
    462         } catch (RuntimeException ex) {
    463             // Good
    464         }
    465     }
    466 
    467     /**
    468      * Test mismatched startObject / endObject calls: Trailing startObject
    469      * with empty objects
    470      */
    471     public void testTrailingStartObjectWithoutData() throws Exception {
    472         final ProtoOutputStream po = new ProtoOutputStream();
    473 
    474         long token1 = po.startObject(ProtoOutputStream.makeFieldId(1,
    475                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
    476 
    477         long token2 = po.startObject(ProtoOutputStream.makeFieldId(3,
    478                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
    479 
    480         po.endObject(token2);
    481         try {
    482             po.getBytes();
    483             throw new Exception("getBytes didn't throw");
    484         } catch (RuntimeException ex) {
    485             // Good
    486         }
    487     }
    488 
    489     /**
    490      * Test mismatched startObject / endObject calls: Extra startObject in the middle.
    491      * with objects that have data.
    492      */
    493     public void testExtraStartObjectInMiddleWithData() throws Exception {
    494         final ProtoOutputStream po = new ProtoOutputStream();
    495 
    496         long token1 = po.startObject(ProtoOutputStream.makeFieldId(1,
    497                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
    498         po.writeUInt32(ProtoOutputStream.makeFieldId(2,
    499                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
    500                 'a');
    501 
    502         long token2 = po.startObject(ProtoOutputStream.makeFieldId(3,
    503                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
    504         po.writeUInt32(ProtoOutputStream.makeFieldId(4,
    505                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
    506                 'b');
    507 
    508         try {
    509             po.endObject(token1);
    510             throw new Exception("endObject didn't throw");
    511         } catch (RuntimeException ex) {
    512             // Good
    513         }
    514     }
    515 
    516     /**
    517      * Test mismatched startObject / endObject calls: Extra startObject in the middle.
    518      * with empty objects
    519      */
    520     public void testExtraStartObjectInMiddleWithoutData() throws Exception {
    521         final ProtoOutputStream po = new ProtoOutputStream();
    522 
    523         long token1 = po.startObject(ProtoOutputStream.makeFieldId(1,
    524                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
    525 
    526         long token2 = po.startObject(ProtoOutputStream.makeFieldId(3,
    527                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
    528 
    529         try {
    530             po.endObject(token1);
    531             throw new Exception("endObject didn't throw");
    532         } catch (RuntimeException ex) {
    533             // Good
    534         }
    535     }
    536 
    537     /**
    538      * Test mismatched startObject / endObject calls: Two deep with swapped endObject.
    539      * with objects that have data.
    540      */
    541     public void testSwappedEndObjectWithData() throws Exception {
    542         final ProtoOutputStream po = new ProtoOutputStream();
    543 
    544         long token1 = po.startObject(ProtoOutputStream.makeFieldId(1,
    545                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
    546         po.writeUInt32(ProtoOutputStream.makeFieldId(2,
    547                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
    548                 'a');
    549 
    550         long token2 = po.startObject(ProtoOutputStream.makeFieldId(3,
    551                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
    552         po.writeUInt32(ProtoOutputStream.makeFieldId(4,
    553                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
    554                 'b');
    555         po.endObject(token2);
    556 
    557         long token3 = po.startObject(ProtoOutputStream.makeFieldId(5,
    558                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
    559         po.writeUInt32(ProtoOutputStream.makeFieldId(4,
    560                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
    561                 'b');
    562 
    563         try {
    564             po.endObject(token2);
    565             throw new Exception("endObject didn't throw");
    566         } catch (RuntimeException ex) {
    567             // Good
    568         }
    569     }
    570 
    571     /**
    572      * Test mismatched startObject / endObject calls: Two deep with swapped endObject.
    573      * with empty objects
    574      */
    575     public void testSwappedEndObjectWithoutData() throws Exception {
    576         final ProtoOutputStream po = new ProtoOutputStream();
    577 
    578         long token1 = po.startObject(ProtoOutputStream.makeFieldId(1,
    579                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
    580 
    581         long token2 = po.startObject(ProtoOutputStream.makeFieldId(3,
    582                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
    583         po.endObject(token2);
    584 
    585         long token3 = po.startObject(ProtoOutputStream.makeFieldId(5,
    586                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
    587 
    588         try {
    589             po.endObject(token2);
    590             throw new Exception("endObject didn't throw");
    591         } catch (RuntimeException ex) {
    592             // Good
    593         }
    594     }
    595 
    596     /**
    597      * Test mismatched startObject / endObject calls: Two deep with swapped endObject.
    598      * with empty objects
    599      */
    600     public void testEndObjectMismatchError() throws Exception {
    601         final ProtoOutputStream po = new ProtoOutputStream();
    602 
    603         long token1 = po.startObject(ProtoOutputStream.makeFieldId(1,
    604                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
    605 
    606         long token2 = po.startObject(ProtoOutputStream.makeFieldId(3,
    607                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
    608         po.endObject(token2);
    609 
    610         long token3 = po.startObject(ProtoOutputStream.makeFieldId(5,
    611                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
    612 
    613         try {
    614             po.endObject(token2);
    615             throw new Exception("endObject didn't throw");
    616         } catch (RuntimeException ex) {
    617             // Check this, because it's really useful, and if we lose the message it'll be
    618             // harder to debug typos.
    619             assertEquals("Mismatched startObject/endObject calls. Current depth 2"
    620                     + " token=Token(val=0x2017fffd0000000a depth=2 object=2 tagSize=1 sizePos=10)"
    621                     + " expectedToken=Token(val=0x2017fffc0000000a depth=2 object=3 tagSize=1"
    622                     + " sizePos=10)", ex.getMessage());
    623         }
    624     }
    625 
    626     /**
    627      * Test compatibility of nested objects.
    628      */
    629     public void testNestedCompat() throws Exception {
    630         final Test.All all = new Test.All();
    631         final ProtoOutputStream po = new ProtoOutputStream(0);
    632 
    633         all.nestedField = new Test.Nested();
    634         all.nestedField.data = 1;
    635         all.nestedField.nested = new Test.Nested();
    636         all.nestedField.nested.data = 2;
    637         all.nestedField.nested.nested = new Test.Nested();
    638         all.nestedField.nested.nested.data = 3;
    639 
    640         final long token1 = po.startObject(ProtoOutputStream.makeFieldId(170,
    641                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
    642         po.writeInt32(ProtoOutputStream.makeFieldId(10001,
    643                         ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_INT32),
    644                 1);
    645         final long token2 = po.startObject(ProtoOutputStream.makeFieldId(10002,
    646                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
    647         po.writeInt32(ProtoOutputStream.makeFieldId(10001,
    648                         ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_INT32),
    649                 2);
    650         final long token3 = po.startObject(ProtoOutputStream.makeFieldId(10002,
    651                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
    652         po.writeInt32(ProtoOutputStream.makeFieldId(10001,
    653                         ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_INT32),
    654                 3);
    655         po.endObject(token3);
    656         po.endObject(token2);
    657         po.endObject(token1);
    658 
    659         final byte[] result = po.getBytes();
    660         final byte[] expected = MessageNano.toByteArray(all);
    661 
    662         Assert.assertArrayEquals(expected, result);
    663     }
    664 
    665     /**
    666      * Test compatibility of repeated nested objects.
    667      */
    668     public void testRepeatedNestedCompat() throws Exception {
    669         final Test.All all = new Test.All();
    670         final ProtoOutputStream po = new ProtoOutputStream(0);
    671 
    672         final int N = 3;
    673         all.nestedFieldRepeated = new Test.Nested[N];
    674         for (int i=0; i<N; i++) {
    675             all.nestedFieldRepeated[i] = new Test.Nested();
    676             all.nestedFieldRepeated[i].data = 1;
    677             all.nestedFieldRepeated[i].nested = new Test.Nested();
    678             all.nestedFieldRepeated[i].nested.data = 2;
    679             all.nestedFieldRepeated[i].nested.nested = new Test.Nested();
    680             all.nestedFieldRepeated[i].nested.nested.data = 3;
    681 
    682             final long token1 = po.startObject(ProtoOutputStream.makeFieldId(171,
    683                         ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
    684             po.writeInt32(ProtoOutputStream.makeFieldId(10001,
    685                         ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_INT32),
    686                     1);
    687             final long token2 = po.startObject(ProtoOutputStream.makeFieldId(10002,
    688                         ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
    689             po.writeInt32(ProtoOutputStream.makeFieldId(10001,
    690                         ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_INT32),
    691                     2);
    692             final long token3 = po.startObject(ProtoOutputStream.makeFieldId(10002,
    693                         ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
    694             po.writeInt32(ProtoOutputStream.makeFieldId(10001,
    695                         ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_INT32),
    696                     3);
    697             po.endObject(token3);
    698             po.endObject(token2);
    699             po.endObject(token1);
    700         }
    701 
    702         final byte[] result = po.getBytes();
    703         final byte[] expected = MessageNano.toByteArray(all);
    704 
    705         Assert.assertArrayEquals(expected, result);
    706     }
    707 
    708     /**
    709      * Test that if you pass in the wrong type of fieldId, it throws.
    710      */
    711     public void testBadFieldIds() {
    712         // Single
    713 
    714         // Good Count / Bad Type
    715         try {
    716             final ProtoOutputStream po = new ProtoOutputStream();
    717             po.startObject(ProtoOutputStream.makeFieldId(1,
    718                         ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_DOUBLE));
    719         } catch (IllegalArgumentException ex) {
    720             // good
    721         }
    722 
    723         // Bad Count / Good Type
    724         try {
    725             final ProtoOutputStream po = new ProtoOutputStream();
    726             po.startObject(ProtoOutputStream.makeFieldId(1,
    727                         ProtoOutputStream.FIELD_COUNT_PACKED | ProtoOutputStream.FIELD_TYPE_MESSAGE));
    728         } catch (IllegalArgumentException ex) {
    729             // good
    730         }
    731 
    732         // Repeated
    733 
    734         // Good Count / Bad Type
    735         try {
    736             final ProtoOutputStream po = new ProtoOutputStream();
    737             po.startRepeatedObject(ProtoOutputStream.makeFieldId(1,
    738                         ProtoOutputStream.FIELD_COUNT_REPEATED | ProtoOutputStream.FIELD_TYPE_DOUBLE));
    739         } catch (IllegalArgumentException ex) {
    740             // good
    741         }
    742 
    743         // Bad Count / Good Type
    744         try {
    745             final ProtoOutputStream po = new ProtoOutputStream();
    746             po.startRepeatedObject(ProtoOutputStream.makeFieldId(1,
    747                         ProtoOutputStream.FIELD_COUNT_PACKED | ProtoOutputStream.FIELD_TYPE_MESSAGE));
    748         } catch (IllegalArgumentException ex) {
    749             // good
    750         }
    751     }
    752 
    753     /**
    754      * Test that if endRepeatedObject is called with a token from startObject that it fails.
    755      */
    756     public void testMismatchedEndObject() {
    757         final ProtoOutputStream po = new ProtoOutputStream();
    758         final long token = po.startObject(ProtoOutputStream.makeFieldId(1,
    759                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
    760 
    761         try {
    762             po.endRepeatedObject(token);
    763         } catch (IllegalArgumentException ex) {
    764             // good
    765         }
    766     }
    767 
    768     /**
    769      * Test that if endRepeatedObject is called with a token from startObject that it fails.
    770      */
    771     public void testMismatchedEndRepeatedObject() {
    772         final ProtoOutputStream po = new ProtoOutputStream();
    773         final long token = po.startRepeatedObject(ProtoOutputStream.makeFieldId(1,
    774                     ProtoOutputStream.FIELD_COUNT_REPEATED | ProtoOutputStream.FIELD_TYPE_MESSAGE));
    775 
    776         try {
    777             po.endObject(token);
    778         } catch (IllegalArgumentException ex) {
    779             // good
    780         }
    781     }
    782 
    783     /**
    784      * Test writeObject, which takes a pre-encoded and compacted protobuf object and writes it into
    785      * a field.
    786      */
    787     public void testWriteObject() {
    788         byte[] innerRaw = new byte[] {
    789             // varint 1 -> 42
    790             (byte)0x08,
    791             (byte)0xd0, (byte)0x02,
    792             // string 2 -> "ab"
    793             (byte)0x12,
    794             (byte)0x02,
    795             (byte)0x62, (byte)0x63,
    796             // object 3 -> ...
    797             (byte)0x1a,
    798             (byte)0x4,
    799                 // varint 4 -> 0
    800                 (byte)0x20,
    801                 (byte)0x00,
    802                 // varint 4 --> 1
    803                 (byte)0x20,
    804                 (byte)0x01,
    805         };
    806 
    807         final ProtoOutputStream po = new ProtoOutputStream();
    808         po.writeObject(ProtoOutputStream.makeFieldId(10,
    809                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE),
    810                 innerRaw);
    811 
    812         final byte[] result = po.getBytes();
    813         final byte[] expected = new byte[2 + innerRaw.length];
    814         expected[0] = (byte)0x52;
    815         expected[1] = (byte)0x0d;
    816         System.arraycopy(innerRaw, 0, expected, 2, innerRaw.length);
    817 
    818         Assert.assertArrayEquals(expected, result);
    819     }
    820 
    821     /**
    822      * Test writeObject, which takes a pre-encoded and compacted protobuf object and writes it into
    823      * a field.
    824      */
    825     public void testWriteObjectEmpty() {
    826         byte[] innerRaw = new byte[0];
    827 
    828         final ProtoOutputStream po = new ProtoOutputStream();
    829         po.writeObject(ProtoOutputStream.makeFieldId(10,
    830                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE),
    831                 innerRaw);
    832 
    833         final byte[] result = po.getBytes();
    834 
    835         Assert.assertEquals(0, result.length);
    836     }
    837 
    838     /**
    839      * Test writeObject, which takes a pre-encoded and compacted protobuf object and writes it into
    840      * a field.
    841      */
    842     public void testWriteObjectRepeated() {
    843         byte[] innerRaw = new byte[] {
    844             // varint 1 -> 42
    845             (byte)0x08,
    846             (byte)0xd0, (byte)0x02,
    847             // string 2 -> "ab"
    848             (byte)0x12,
    849             (byte)0x02,
    850             (byte)0x62, (byte)0x63,
    851             // object 3 -> ...
    852             (byte)0x1a,
    853             (byte)0x4,
    854                 // varint 4 -> 0
    855                 (byte)0x20,
    856                 (byte)0x00,
    857                 // varint 4 --> 1
    858                 (byte)0x20,
    859                 (byte)0x01,
    860         };
    861 
    862         final ProtoOutputStream po = new ProtoOutputStream();
    863         po.writeRepeatedObject(ProtoOutputStream.makeFieldId(10,
    864                     ProtoOutputStream.FIELD_COUNT_REPEATED | ProtoOutputStream.FIELD_TYPE_MESSAGE),
    865                 innerRaw);
    866 
    867         final byte[] result = po.getBytes();
    868         final byte[] expected = new byte[2 + innerRaw.length];
    869         expected[0] = (byte)0x52;
    870         expected[1] = (byte)0x0d;
    871         System.arraycopy(innerRaw, 0, expected, 2, innerRaw.length);
    872 
    873         Assert.assertArrayEquals(expected, result);
    874     }
    875 
    876     /**
    877      * Test writeObject, which takes a pre-encoded and compacted protobuf object and writes it into
    878      * a field.
    879      */
    880     public void testWriteObjectRepeatedEmpty() {
    881         byte[] innerRaw = new byte[0];
    882 
    883         final ProtoOutputStream po = new ProtoOutputStream();
    884         po.writeRepeatedObject(ProtoOutputStream.makeFieldId(10,
    885                     ProtoOutputStream.FIELD_COUNT_REPEATED | ProtoOutputStream.FIELD_TYPE_MESSAGE),
    886                 innerRaw);
    887 
    888         Assert.assertArrayEquals(new byte[] {
    889             (byte)0x52,
    890             (byte)0x00
    891         }, po.getBytes());
    892     }
    893 }
    894