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