README.md
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