1# Writing an MPC Test
2
3Using
4[this CL](https://android-review.googlesource.com/c/platform/cts/+/2128521) as a
5guide focusing on requirement
6[8.2/H-1-1](https://source.android.com/docs/compatibility/13/android-13-cdd#2274_performance):
7
8-   R: MUST ensure a sequential write performance of at least 100MB/s.
9-   S and Tiramisu: MUST ensure a sequential write performance of at least
10    125MB/s.
11
12## Define Requirements
13
14### Define Constants Under RequirementConstants
15
16For our requirement,
17[8.2/H-1-1](https://source.android.com/docs/compatibility/13/android-13-cdd#2274_performance),
18we have one required measurement, so we will create one constant,
19`FILESYSTEM_IO_RATE`, to track that. This constant eventually will make its way
20to the internal cts_media_performance_class_test_metrics.proto, so the
21string-value we choose for it needs to structured like a proto field name and
22should include units at the end:
23
24```
25public static final String FILESYSTEM_IO_RATE = "filesystem_io_rate_mbps";
26```
27
28Additionally, we may need to create a new BiPredicate for our requirement. The
29BiPredicate defines the operator to test measurements for our requirement with.
30For our case, we want to test if an I/O rate is at or above a certain value, so
31we will use a GTE operator. We will additionally be storing I/O rates as
32doubles, leading us to define the BiPredicate DOUBLE_GTE:
33
34```
35public static final BiPredicate<Double, Double> DOUBLE_GTE = RequirementConstants.gte();
36```
37
38### Define Requirement Class Under PerformanceClassEvaluator
39
40In PerformanceClassEvaluator.java, we will define a new requirement class. This
41class should be defined as nested class under PerformanceClassEvaluator:
42
43```
44// used for requirements [8.2/H-1-1], [8.2/H-1-2], [8.2/H-1-3], [8.2/H-1-4]
45public static class FileSystemRequirement extends Requirement {
46
47  private static final String TAG = FileSystemRequirement.class.getSimpleName();
48
49  ...
50
51}
52```
53
54#### Define Constructor
55
56The constructors for requirement classes are very standardized. They are always
57private; they always take in two inputs: `String id, RequiredMeasurement<?> ...
58reqs`; and they always contain one line: `super(id, reqs)`:
59
60```
61private FileSystemRequirement(String id, RequiredMeasurement<?> ... reqs) {
62  super(id, reqs);
63}
64```
65
66#### Define Set Measured Value Method(s)
67
68Requirement classes need to define a method for each required measurement. These
69methods always contain one line: a function call to Requirement’s
70`setMeausredValue` method. For
71[8.2/H-1-1](https://source.android.com/docs/compatibility/13/android-13-cdd#2274_performance),
72we only have one required measurement so only need to make one method for it:
73
74```
75/**
76 * Set the Filesystem I/O Rate in MB/s.
77 */
78public void setFilesystemIoRate(double filesystemIoRate) {
79  this.setMeasuredValue(RequirementConstants.FILESYSTEM_IO_RATE, filesystemIoRate);
80}
81```
82
83#### Define Create Method
84
85The last thing we need to make for our requirement class is a create method.
86This method defines each of the required measurements. Each
87RequiredMeasurement.java is created through a builder and defining the
88following:
89
90*   The datatype of the measurement. This is during the call to the
91    RequiredMeasurement’s builder function, ex: `<Double>builder()`
92*   The ID for the measurement. This is the String constant we defined earlier.
93*   The predicate for the measurement. This is the BiPredicate we defined
94    earlier.
95*   A required value for each achievable performance class. For example, using
96    the GTE a BiPredicate:
97    *   `addRequiredValue(Build.VERSION_CODES.R, 100.0)` says that if a
98        requirement measurement is greater than or equal to to 100, the device
99        makes performance class R
100    *   `addRequiredValue(Build.VERSION_CODES.TIRAMISU, 125.0)` says that if a
101        requirement measurement is greater than or equal to to 125, the device
102        makes performance class Tiramisu
103
104Note: if a device meets multiple performance classes for a requirement, the
105system automatically chooses to record the higher classification
106
107For requirement
108[8.2/H-1-1](https://source.android.com/docs/compatibility/13/android-13-cdd#2274_performance)
109we define the following create method:
110
111```
112/**
113 * [8.2/H-1-1] MUST ensure a sequential write performance of
114 * at least 100(R) / 125(S & T) MB/s.
115 */
116public static FileSystemRequirement createR8_2__H_1_1() {
117  RequiredMeasurement<Double> filesystem_io_rate =
118      RequiredMeasurement.<Double>builder()
119      .setId(RequirementConstants.FILESYSTEM_IO_RATE)
120      .setPredicate(RequirementConstants.DOUBLE_GTE)
121      .addRequiredValue(Build.VERSION_CODES.R, 100.0)
122      .addRequiredValue(Build.VERSION_CODES.TIRAMISU, 125.0)
123      .build();
124
125  return new FileSystemRequirement(RequirementConstants.R8_2__H_1_1,
126      filesystem_io_rate);
127}
128```
129
130Note: a requirement class can be and often is used for multiple requirements. If
131so, a create method must be defined for each.
132
133#### Define Add Method at the Bottom of PerformanceClassEvaluator
134
135After all of that we just need to define an add method at the bottom of
136PerformacneClassEvaluator for our requirement. All it does is call
137PerformanceClassEvaluator’s `addRequirement` method using the create method we
138defined earlier.
139
140```
141public FileSystemRequirement addR8_2__H_1_1() {
142  return this.addRequirement(FileSystemRequirement.createR8_2__H_1_1());
143}
144```
145
146## Update Test to Report Data Using PerformanceClassEvaluator
147
148Now that we have a requirement defined we just need to update our test to use
149PerformanceClassEvaluator.
150
151First we need to add the following to our test class: @Rule public final
152TestName mTestName = new TestName();
153
154Next we will create the evaluator and add our newly defined requirement. This
155can be done at any point during the test, but typically test writers choose to
156do this at the end of the test:
157
158```
159PerformanceClassEvaluator pce = new PerformanceClassEvaluator(this.mTestName);
160PerformanceClassEvaluator.FileSystemRequirement r8_2__H_1_1 = pce.addR8_2__H_1_1();
161```
162
163After the test, once our required measurement(s) have been calculated, we use
164the set measurement method(s) we defined to report them:
165
166```
167r8_2__H_1_1.setFilesystemIoRate(stat.mAverage);
168```
169
170Finally, we just need to submit our results. The submit method should be called
171only once at the very end of the test. If we are writing our test CTS, we should
172use `submitAndCheck`; if we are writing our test under CTS-Verifier or ITS, we
173should use `submitAndVerify`. Ex:
174
175```
176pce.submitAndCheck();
177```
178
179The test results are then processed and reported creating a file
180cts_media_performance_class_test_cases.reportlog.json which will eventually have
181its data upload and processed.
182
183You can view the file with
184
185```shell
186adb root
187adb shell cat /storage/emulated/0/report-log-files/CtsMediaPerformanceClassTestCases.reportlog.json
188```
189