1# Writing an MPC Test 2 3Using 4[this CL](https://android-review.googlesource.com/c/platform/cts/+/3185540) as a 5guide focusing on requirement 6[5.1/H-1-2](https://source.android.com/docs/compatibility/15/android-15-cdd#2271_media): 7 8- R: MUST support 6 instances of hardware video decoder sessions (AVC or HEVC) 9 in any codec combination running concurrently at 720p resolution@30 fps. 10- S: MUST support 6 instances of hardware video decoder sessions (AVC, HEVC, 11 VP9* or later) in any codec combination running concurrently at 720p 12 resolution@30 fps. *Only 2 instances are required if VP9 codec is present. 13- Tiramisu: MUST support 6 instances of hardware video decoder sessions (AVC, 14 HEVC, VP9, AV1 or later) in any codec combination running concurrently at 15 1080p resolution@30 fps. 16- Upside-Down Cake: MUST support 6 instances of 8-bit (SDR) hardware video 17 decoder sessions (AVC, HEVC, VP9, AV1 or later) in any codec combination 18 running concurrently with 3 sessions at 1080p resolution@30 fps and 3 19 sessions at 4k resolution@30fps, unless AV1. AV1 codecs are only required to 20 support 1080p resolution, but are still required to support 6 instances at 21 1080p30fps. 22- Vanilla Ice Cream: MUST support 6 instances of 8-bit (SDR) hardware video 23 decoder sessions (AVC, HEVC, VP9, AV1, or later) in any codec combination 24 running concurrently with 3 sessions at 1080p resolution@30 fps and 3 25 sessions at 4k resolution@30fps, unless AV1. For all sessions, there MUST 26 NOT be more than 1 frame dropped per second. AV1 codecs are only required to 27 support 1080p resolution, but are still required to support 6 instances at 28 1080p30fps. 29 30## Define Requirements 31 32Each requirement needs to be defined in 33[requirements.txtpb](https://cs.android.com/android/platform/superproject/main/+/main:cts/tests/mediapc/requirements/requirements.txtpb). 34The information in this file is then used to generate code to be used in tests 35for said requirement. 36 37### Give the Requirement a Name 38 39Each requirement needs to be given a human-readable name describing what the 40requirement is testing for. Additionally, each requirement name needs to be 41unique. This name is then used for the class name when generating code. 42 43For example, we gave 44[5.1/H-1-2](https://source.android.com/docs/compatibility/15/android-15-cdd#2271_media) 45the name `"Concurrent Video Decoder Sessions"`. This will then generate the 46following class: 47 48``` 49android.mediapc.cts.common.Requirements.ConcurrentVideoDecoderSessionsRequirement 50``` 51 52### Define Test Configs 53 54A test config describes different set ups for a given requirement that change 55which performance classes are being tested for said requirement. 56 57For example: requirement, 58[5.1/H-1-2](https://source.android.com/docs/compatibility/15/android-15-cdd#2271_media), 59there's 3 test configs: - 720p: which describes tests ran at 720p (makes sense), 60these tests only check for performance classes R and S. - 1080p: these tests 61only check for performance class Tiramisu. - 4k: these tests only check for 62performance classes Upside-down Cake and Vanilla Ice Cream 63 64Additionally each test config needs to be given a proto field number. This 65number must be unique for *all* test configs within 66[requirements.txtpb](https://cs.android.com/android/platform/superproject/main/+/main:cts/tests/mediapc/requirements/requirements.txtpb). 67 68``` 69test_configs: { 70 key: "720p" 71 value: { 72 description: "Tests running at 720p" 73 proto_field_number: 4 74 } 75} 76test_configs: { 77 key: "1080p" 78 value: { 79 description: "Tests running at 1080p" 80 proto_field_number: 5 81 } 82} 83test_configs: { 84 key: "4k" 85 value: { 86 description: "Tests running at 4k" 87 proto_field_number: 6 88 } 89} 90``` 91 92NOTE: The changelist for 93[5.1/H-1-2](https://source.android.com/docs/compatibility/15/android-15-cdd#2271_media) 94describes an `id` field. This field has since been deprecated and removed, so 95please ignore it. 96 97NOTE: The changelist for 98[5.1/H-1-2](https://source.android.com/docs/compatibility/15/android-15-cdd#2271_media) 99does not describe proto field numbers. These were yet to be implemented when the 100change was created. They are implemented now. 101 102TIP: Most requirements only use one test configuration to describe a singular 103test which tests for all performance classes. For these requirements, you only 104need to specify a blank default test config. Example: 105[cl/666945690](https://android-review.googlesource.com/c/platform/cts/+/3237331) 106 107``` 108test_configs: { 109 key: "" 110 value: { 111 description: "Default test config" 112 proto_field_number: 47 113 } 114} 115``` 116 117### Define Variants 118 119In addition to test configs, any variants for a given requirement must also be 120defined. 121 122A variant describes a different set of thresholds a requirement must meet 123depending on the test setup. The main difference between a variant and a test 124config is variants do NOT affect which performance classes are being tested. 125 126Variants do NOT need proto field numbers. 127 128For our requirement, 129[5.1/H-1-2](https://source.android.com/docs/compatibility/15/android-15-cdd#2271_media), 130when testing with a VP9 codec at 720p, 2 instances are required for S and none 131for R, and when testing with an AV1 or other codec, there is no requirement for 132R. Therefore, we have created 2 variants: 133 134``` 135variants: { 136 key: "VP9" 137 value: { 138 description: "When one of the codecs is VP9, variant used in 720p tests" 139 } 140} 141variants: { 142 key: "AV1" 143 value: { 144 description: "When one of the codecs is AV1, variant used in 720p tests" 145 } 146} 147``` 148 149NOTE: Sometimes, it can be confusing whether to make a variant or a test config 150when defining requirements. If unsure, the recommendation is to make another 151test config rather than another variant. 152 153NOTE: A variant does not need to be used in every test config. 154 155### Define Measurements 156 157A set of measurements for the requirement must also be defined. Each measurement 158needs a name, a measurement_type, a comparison, and a proto field number. 159 160The measurement name is described in the `key` field for each measurement. It 161needs to be able to be used as a field for a proto, so lowercase, underscores, 162no spaces, etc. 163 164The `measurement_type` describes the data type of the measurement. It can be one 165of the following types: 166 167- MEASUREMENT_TYPE_BOOL 168- MEASUREMENT_TYPE_DOUBLE 169- MEASUREMENT_TYPE_INT 170- MEASUREMENT_TYPE_STRING 171- MEASUREMENT_TYPE_LONG 172- MEASUREMENT_TYPE_FLOAT 173 174The `comparison` describes how the measurement will be tested when evaluating 175the performance class. It can be one of the following types: 176 177- COMPARISON_EQUAL 178- COMPARISON_LESS_THAN 179- COMPARISON_LESS_THAN_OR_EQUAL 180- COMPARISON_GREATER_THAN 181- COMPARISON_GREATER_THAN_OR_EQUAL 182- COMPARISON_INFO_ONLY 183- COMPARISON_CONFIG 184 185NOTE: Config comparison types are measurements that describe how the test was 186set up. These values are automatically set, and the thresholds defined later 187must all be the same per test config. 188 189Finally, like with test configs, each measurement needs a `proto_field_number`. 190For measurements, the number must be greater than or equal to 3, and only needs 191to be unique among the other measurements for a given requirement. 192 193For our requirement, 194[5.1/H-1-2](https://source.android.com/docs/compatibility/15/android-15-cdd#2271_media), 195we've defined the following: 196 197``` 198measurements: { 199 key: "concurrent_fps" 200 value: { 201 description: "The number of frames per second that can be decoded concurrently" 202 measurement_type: MEASUREMENT_TYPE_DOUBLE 203 comparison: COMPARISON_GREATER_THAN_OR_EQUAL 204 proto_field_number: 3 205 } 206} 207measurements: { 208 key: "frame_drops_per_sec" 209 value: { 210 description: "The number of frames dropped per second" 211 measurement_type: MEASUREMENT_TYPE_DOUBLE 212 comparison: COMPARISON_LESS_THAN_OR_EQUAL 213 proto_field_number: 4 214 } 215} 216measurements: { 217 key: "resolution" 218 value: { 219 description: "The resolution the test was run at" 220 measurement_type: MEASUREMENT_TYPE_INT 221 comparison: COMPARISON_CONFIG 222 proto_field_number: 5 223 } 224} 225``` 226 227### Define Specs 228 229Lastly, the specs for the requirement need to be defined. A spec describes the 230required thresholds for a given performance class. It has the following fields: 231`mpc`, `specification`, `test_config_id`, and `required_values`. Additionally it 232is stored as a map within 233[requirements.txtpb](https://cs.android.com/android/platform/superproject/main/+/main:cts/tests/mediapc/requirements/requirements.txtpb), 234so it needs a `key` field which corresponds to performance class the spec 235describes. 236 237The field `mpc` is an enum that also describes the performance class associated 238with the spec. As such, it should correspond to `key` field. - 239MEDIA_PERFORMANCE_CLASS_11 corresponds to 30 - MEDIA_PERFORMANCE_CLASS_12 240corresponds to 31 - MEDIA_PERFORMANCE_CLASS_13 corresponds to 33 - 241MEDIA_PERFORMANCE_CLASS_14 corresponds to 34 - MEDIA_PERFORMANCE_CLASS_15 242corresponds to 35 243 244The field `specification` describes in text what the requirement is and the 245threshold that must be met. This text is copied directly from the 246[Android CDD](https://source.android.com/docs/compatibility/15/android-15-cdd#2271_media) 247for a given requirement/performance class. 248 249The field `test_config_id` describes the associated `test_config_id` which was 250defined previously. If it is the default blank `test_config_id`, the field does 251not have to be set. 252 253#### Define Required Values 254 255Finally, `required_values` must be defined for all measurements associated with 256the specified performance class. These values are stored as a map where the 257`key` corresponds to the measurement name, and the value corresponds to the 258required threshold. 259 260NOTE: if a measurement is not needed for a given performance class level it does 261not have to be specified 262 263NOTE: config measurements must have the same threshold for all performance class 264levels for a given test config 265 266##### Define Variant Required Values 267 268Additionally, `required_values` must be set for variants. A described variant 269does not have to correspond to every test config, but if described for given 270test config, it must be described for all specs with the same test config. 271 272### Example Generated Class for [5.1/H-1-2](https://source.android.com/docs/compatibility/15/android-15-cdd#2271_media) 273 274``` 275/** 276 * Add a new ConcurrentVideoDecoderSessionsRequirement for requirement 5.1/H-1-2 to a 277 * {@code PerformanceClassEvaluator} instance. 278 * 279 * Concurrent video decoder sessions 280 */ 281public static ConcurrentVideoDecoderSessionsRequirement.With addR5_1__H_1_2() { 282 return new ConcurrentVideoDecoderSessionsRequirement.With(); 283} 284 285/** 286 * 5.1/H-1-2 Concurrent Video Decoder Sessions 287 * 288 * Concurrent video decoder sessions 289 */ 290public static final class ConcurrentVideoDecoderSessionsRequirement extends Requirement { 291 292 public static final class With { 293 private With() {} 294 public static final class Config1080P { 295 private Config1080P() {} 296 public ConcurrentVideoDecoderSessionsRequirement to(PerformanceClassEvaluator pce) { 297 return pce.addRequirement(ConcurrentVideoDecoderSessionsRequirement.create1080P()); 298 } 299 } 300 public static final class Config4K { 301 private Config4K() {} 302 public ConcurrentVideoDecoderSessionsRequirement to(PerformanceClassEvaluator pce) { 303 return pce.addRequirement(ConcurrentVideoDecoderSessionsRequirement.create4K()); 304 } 305 } 306 public static final class Config720P { 307 private Config720P() {} 308 public ConcurrentVideoDecoderSessionsRequirement to(PerformanceClassEvaluator pce) { 309 return pce.addRequirement(ConcurrentVideoDecoderSessionsRequirement.create720P()); 310 } 311 public Config720PAndVariantAV1 withVariantAV1() { 312 return new Config720PAndVariantAV1(); 313 } 314 public Config720PAndVariantVP9 withVariantVP9() { 315 return new Config720PAndVariantVP9(); 316 } 317 } 318 public static final class VariantAV1 { 319 private VariantAV1() {} 320 public Config720PAndVariantAV1 withConfig720P() { 321 return new Config720PAndVariantAV1(); 322 } 323 } 324 public static final class VariantVP9 { 325 private VariantVP9() {} 326 public Config720PAndVariantVP9 withConfig720P() { 327 return new Config720PAndVariantVP9(); 328 } 329 } 330 public static final class Config720PAndVariantAV1 { 331 private Config720PAndVariantAV1() {} 332 public ConcurrentVideoDecoderSessionsRequirement to(PerformanceClassEvaluator pce) { 333 return pce.addRequirement(ConcurrentVideoDecoderSessionsRequirement.create720PAV1()); 334 } 335 } 336 public static final class Config720PAndVariantVP9 { 337 private Config720PAndVariantVP9() {} 338 public ConcurrentVideoDecoderSessionsRequirement to(PerformanceClassEvaluator pce) { 339 return pce.addRequirement(ConcurrentVideoDecoderSessionsRequirement.create720PVP9()); 340 } 341 } 342 public Config1080P withConfig1080P() { 343 return new Config1080P(); 344 } 345 public Config4K withConfig4K() { 346 return new Config4K(); 347 } 348 public Config720P withConfig720P() { 349 return new Config720P(); 350 } 351 public VariantAV1 withVariantAV1() { 352 return new VariantAV1(); 353 } 354 public VariantVP9 withVariantVP9() { 355 return new VariantVP9(); 356 } 357 } 358 359 /** 360 * 5.1/H-1-2 Concurrent Video Decoder Sessions 361 * 362 * Concurrent video decoder sessions 363 */ 364 private static ConcurrentVideoDecoderSessionsRequirement create1080P() { 365 var concurrentFps = 366 RequiredMeasurement.<Double>builder() 367 .setId("concurrent_fps") 368 .setPredicate(RequirementConstants.DOUBLE_GTE) 369 .addRequiredValue(VERSION_CODES.TIRAMISU, 171.000000) 370 .build(); 371 var frameDropsPerSec = 372 RequiredMeasurement.<Double>builder() 373 .setId("frame_drops_per_sec") 374 .setPredicate(RequirementConstants.DOUBLE_LTE) 375 .build(); 376 var resolution = 377 RequiredMeasurement.<Integer>builder() 378 .setId("resolution") 379 .setPredicate(RequirementConstants.INTEGER_INFO) 380 .addRequiredValue(VERSION_CODES.TIRAMISU, 1080) 381 .build(); 382 383 ConcurrentVideoDecoderSessionsRequirement req = 384 new ConcurrentVideoDecoderSessionsRequirement( 385 "r5_1__h_1_2__1080_p", 386 concurrentFps, 387 frameDropsPerSec, 388 resolution); 389 req.setMeasuredValue("resolution",1080); 390 return req; 391 } 392 393 /** 394 * 5.1/H-1-2 Concurrent Video Decoder Sessions 395 * 396 * Concurrent video decoder sessions 397 */ 398 private static ConcurrentVideoDecoderSessionsRequirement create4K() { 399 var concurrentFps = 400 RequiredMeasurement.<Double>builder() 401 .setId("concurrent_fps") 402 .setPredicate(RequirementConstants.DOUBLE_GTE) 403 .addRequiredValue(VERSION_CODES.UPSIDE_DOWN_CAKE, 171.000000) 404 .addRequiredValue(VERSION_CODES.VANILLA_ICE_CREAM, 171.000000) 405 .build(); 406 var frameDropsPerSec = 407 RequiredMeasurement.<Double>builder() 408 .setId("frame_drops_per_sec") 409 .setPredicate(RequirementConstants.DOUBLE_LTE) 410 .addRequiredValue(VERSION_CODES.VANILLA_ICE_CREAM, 1.000000) 411 .build(); 412 var resolution = 413 RequiredMeasurement.<Integer>builder() 414 .setId("resolution") 415 .setPredicate(RequirementConstants.INTEGER_INFO) 416 .addRequiredValue(VERSION_CODES.UPSIDE_DOWN_CAKE, 2160) 417 .addRequiredValue(VERSION_CODES.VANILLA_ICE_CREAM, 2160) 418 .build(); 419 420 ConcurrentVideoDecoderSessionsRequirement req = 421 new ConcurrentVideoDecoderSessionsRequirement( 422 "r5_1__h_1_2__4_k", 423 concurrentFps, 424 frameDropsPerSec, 425 resolution); 426 req.setMeasuredValue("resolution",2160); 427 return req; 428 } 429 430 /** 431 * 5.1/H-1-2 Concurrent Video Decoder Sessions 432 * 433 * Concurrent video decoder sessions 434 */ 435 private static ConcurrentVideoDecoderSessionsRequirement create720P() { 436 var concurrentFps = 437 RequiredMeasurement.<Double>builder() 438 .setId("concurrent_fps") 439 .setPredicate(RequirementConstants.DOUBLE_GTE) 440 .addRequiredValue(VERSION_CODES.R, 171.000000) 441 .addRequiredValue(VERSION_CODES.S, 171.000000) 442 .build(); 443 var frameDropsPerSec = 444 RequiredMeasurement.<Double>builder() 445 .setId("frame_drops_per_sec") 446 .setPredicate(RequirementConstants.DOUBLE_LTE) 447 .build(); 448 var resolution = 449 RequiredMeasurement.<Integer>builder() 450 .setId("resolution") 451 .setPredicate(RequirementConstants.INTEGER_INFO) 452 .addRequiredValue(VERSION_CODES.R, 720) 453 .addRequiredValue(VERSION_CODES.S, 720) 454 .build(); 455 456 ConcurrentVideoDecoderSessionsRequirement req = 457 new ConcurrentVideoDecoderSessionsRequirement( 458 "r5_1__h_1_2__720_p", 459 concurrentFps, 460 frameDropsPerSec, 461 resolution); 462 req.setMeasuredValue("resolution",720); 463 return req; 464 } 465 466 /** 467 * 5.1/H-1-2 Concurrent Video Decoder Sessions When one of the codecs is AV1, variant used in 720p tests 468 * 469 * Concurrent video decoder sessions 470 */ 471 private static ConcurrentVideoDecoderSessionsRequirement create720PAV1() { 472 var concurrentFps = RequiredMeasurement 473 .<Double>builder() 474 .setId("concurrent_fps") 475 .setPredicate(RequirementConstants.DOUBLE_GTE) 476 .addRequiredValue(VERSION_CODES.S, 171.000000) 477 .build(); 478 var frameDropsPerSec = RequiredMeasurement 479 .<Double>builder() 480 .setId("frame_drops_per_sec") 481 .setPredicate(RequirementConstants.DOUBLE_LTE) 482 .build(); 483 var resolution = RequiredMeasurement 484 .<Integer>builder() 485 .setId("resolution") 486 .setPredicate(RequirementConstants.INTEGER_INFO) 487 .addRequiredValue(VERSION_CODES.R, 720) 488 .addRequiredValue(VERSION_CODES.S, 720) 489 .build(); 490 ConcurrentVideoDecoderSessionsRequirement req = 491 new ConcurrentVideoDecoderSessionsRequirement( 492 "r5_1__h_1_2", 493 concurrentFps, 494 frameDropsPerSec, 495 resolution); 496 req.setMeasuredValue("resolution",720); 497 return req; 498 } 499 500 /** 501 * 5.1/H-1-2 Concurrent Video Decoder Sessions When one of the codecs is VP9, variant used in 720p tests 502 * 503 * Concurrent video decoder sessions 504 */ 505 private static ConcurrentVideoDecoderSessionsRequirement create720PVP9() { 506 var concurrentFps = RequiredMeasurement 507 .<Double>builder() 508 .setId("concurrent_fps") 509 .setPredicate(RequirementConstants.DOUBLE_GTE) 510 .addRequiredValue(VERSION_CODES.S, 57.000000) 511 .build(); 512 var frameDropsPerSec = RequiredMeasurement 513 .<Double>builder() 514 .setId("frame_drops_per_sec") 515 .setPredicate(RequirementConstants.DOUBLE_LTE) 516 .build(); 517 var resolution = RequiredMeasurement 518 .<Integer>builder() 519 .setId("resolution") 520 .setPredicate(RequirementConstants.INTEGER_INFO) 521 .addRequiredValue(VERSION_CODES.R, 720) 522 .addRequiredValue(VERSION_CODES.S, 720) 523 .build(); 524 ConcurrentVideoDecoderSessionsRequirement req = 525 new ConcurrentVideoDecoderSessionsRequirement( 526 "r5_1__h_1_2", 527 concurrentFps, 528 frameDropsPerSec, 529 resolution); 530 req.setMeasuredValue("resolution",720); 531 return req; 532 } 533 534 /** The number of frames per second that can be decoded concurrently */ 535 public void setConcurrentFps(double v) { 536 this.setMeasuredValue("concurrent_fps", v); 537 } 538 539 /** The number of frames dropped per second */ 540 public void setFrameDropsPerSec(double v) { 541 this.setMeasuredValue("frame_drops_per_sec", v); 542 } 543 544 /** The resolution the test was run at */ 545 public int getResolution() { 546 return this.getMeasuredValue("resolution", Integer.class); 547 } 548 549 private ConcurrentVideoDecoderSessionsRequirement(String id, RequiredMeasurement<?>... reqs) { 550 super(id, reqs); 551 } 552} 553``` 554 555## Update Test to Report Data Using PerformanceClassEvaluator 556 557Now that we have a requirement defined we just need to update our test to use 558PerformanceClassEvaluator. 559 560First we need to add the following to our test class: @Rule public final 561TestName mTestName = new TestName(); 562 563### Initializing the Requirement Objects 564 565Next we will create the evaluator and add our newly defined requirement. This 566can be done at any point during the test, but typically test writers choose to 567do this at the end of the test: 568 569``` 570PerformanceClassEvaluator pce = new PerformanceClassEvaluator(this.mTestName); 571ConcurrentVideoDecoderSessionsRequirement r5_1__h_1_2 = 572 Requirements.addR5_1__H_1_2().withConfigX().withVariantY().to(pce); 573``` 574 575NOTE: `withConfigX` should be replaced with the proper config, ex: 576`withConfig1080P`. If using the default blank config, this should be left out. 577 578NOTE: `withVariantY` should be replaced with the proper variant, ex: 579`withVariantVP9`. If the test is not associated with a variant, this should also 580be left out. 581 582NOTE: the order configs and variants are specified does not matter, i.e. 583`withVariantY().withConfigX()` is also valid 584 585### Setting the Measured Values 586 587The generated class for the given requirement also generates with set methods 588for each measurement. 589 590After the test, once our required measurement(s) have been calculated, we use 591the set measurement method(s) generated to report them: 592 593``` 594r5_1__H_1_2.setConcurrentFps(achievedFrameRate); 595r5_1__H_1_2.setFrameDropsPerSec(frameDropsPerSec); 596``` 597 598NOTE: if a measurement is not associated with the specified test config, it does 599not have to be set. 600 601NOTE: config measurement generate get methods instead of set, and do not need to 602be set 603 604### Submitting the Test Results 605 606Finally, we just need to submit our results. The submit method should be called 607only once at the very end of the test. If we are writing our test CTS, we should 608use `submitAndCheck`; if we are writing our test under CTS-Verifier or ITS, we 609should use `submitAndVerify`. Ex: 610 611``` 612pce.submitAndCheck(); 613``` 614 615The test results are then processed and reported creating a file 616media_performance_class_test_cases.reportlog.json which will eventually have its 617data uploaded and processed. 618 619You can view the file with 620 621```shell 622adb root 623adb shell cat /storage/emulated/0/report-log-files/MediaPerformanceClassTestCases.reportlog.json 624``` 625