xref: /aosp_15_r20/external/cronet/components/cronet/android/test/src/org/chromium/net/ReportingCollector.java (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2018 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 package org.chromium.net;
6 
7 import static com.google.common.truth.Truth.assertThat;
8 
9 import com.google.common.truth.Correspondence;
10 import com.google.common.truth.Correspondence.BinaryPredicate;
11 
12 import org.json.JSONArray;
13 import org.json.JSONException;
14 import org.json.JSONObject;
15 
16 import java.util.ArrayList;
17 import java.util.Iterator;
18 import java.util.List;
19 import java.util.concurrent.Semaphore;
20 import java.util.concurrent.TimeUnit;
21 
22 /**
23  * Stores Reporting API reports received by a test collector, providing helper methods for checking
24  * whether expected reports were actually received.
25  */
26 class ReportingCollector {
27     private List<JSONObject> mReceivedReports = new ArrayList<JSONObject>();
28     private Semaphore mReceivedReportsSemaphore = new Semaphore(0);
29 
30     /**
31      * Stores a batch of uploaded reports.
32      * @param payload the POST payload from the upload
33      * @return whether the payload was parsed successfully
34      */
addReports(String payload)35     public boolean addReports(String payload) {
36         try {
37             JSONArray reports = new JSONArray(payload);
38             int elementCount = 0;
39             synchronized (mReceivedReports) {
40                 for (int i = 0; i < reports.length(); i++) {
41                     JSONObject element = reports.optJSONObject(i);
42                     if (element != null) {
43                         mReceivedReports.add(element);
44                         elementCount++;
45                     }
46                 }
47             }
48             mReceivedReportsSemaphore.release(elementCount);
49             return true;
50         } catch (JSONException e) {
51             return false;
52         }
53     }
54 
assertContainsReport(String expectedString)55     public void assertContainsReport(String expectedString) {
56         JSONObject expectedJson;
57 
58         try {
59             expectedJson = new JSONObject(expectedString);
60         } catch (JSONException e) {
61             throw new IllegalArgumentException(e);
62         }
63         synchronized (mReceivedReports) {
64             assertThat(mReceivedReports)
65                     .comparingElementsUsing(
66                             Correspondence.from(
67                                     (BinaryPredicate<JSONObject, JSONObject>)
68                                             (actual, expected) ->
69                                                     isJSONObjectSubset(expected, actual),
70                                     "is a subset of"))
71                     .contains(expectedJson);
72         }
73     }
74 
75     /** Waits until the requested number of reports have been received, with a 5-second timeout. */
waitForReports(int reportCount)76     public void waitForReports(int reportCount) {
77         final int timeoutSeconds = 5;
78         try {
79             mReceivedReportsSemaphore.tryAcquire(reportCount, timeoutSeconds, TimeUnit.SECONDS);
80         } catch (InterruptedException e) {
81         }
82     }
83 
84     /**
85      * Checks whether one {@link JSONObject} is a subset of another.  Any fields that appear in
86      * {@code lhs} must also appear in {@code rhs}, with the same value.  There can be extra fields
87      * in {@code rhs}; if so, they are ignored.
88      */
isJSONObjectSubset(JSONObject lhs, JSONObject rhs)89     private boolean isJSONObjectSubset(JSONObject lhs, JSONObject rhs) {
90         Iterator<String> keys = lhs.keys();
91         while (keys.hasNext()) {
92             String key = keys.next();
93             Object lhsElement = lhs.opt(key);
94             Object rhsElement = rhs.opt(key);
95 
96             if (rhsElement == null) {
97                 // lhs has an element that doesn't appear in rhs
98                 return false;
99             }
100 
101             if (lhsElement instanceof JSONObject) {
102                 if (!(rhsElement instanceof JSONObject)) {
103                     return false;
104                 }
105                 return isJSONObjectSubset((JSONObject) lhsElement, (JSONObject) rhsElement);
106             }
107 
108             if (!lhsElement.equals(rhsElement)) {
109                 return false;
110             }
111         }
112         return true;
113     }
114 }
115 ;
116