2 * ========================LICENSE_START=================================
5 * Copyright (C) 2023 Nordix Foundation
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ========================LICENSE_END===================================
21 package org.oran.pmproducer.filter;
23 import static org.assertj.core.api.Assertions.assertThat;
25 import com.google.gson.Gson;
26 import com.google.gson.GsonBuilder;
27 import com.google.protobuf.AbstractMessage.Builder;
28 import com.google.protobuf.Message;
29 import com.google.protobuf.MessageOrBuilder;
30 import com.google.protobuf.util.JsonFormat;
32 import java.io.IOException;
33 import java.lang.invoke.MethodHandles;
34 import java.lang.reflect.InvocationTargetException;
35 import java.nio.charset.Charset;
36 import java.nio.file.Files;
37 import java.nio.file.Path;
38 import java.time.Instant;
40 import org.junit.jupiter.api.Test;
41 import org.oran.pmproducer.tasks.TopicListener;
42 import org.oran.pmproducer.tasks.TopicListener.DataFromTopic;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
46 class PmReportFilterTest {
48 public static class ProtoJsonUtil {
51 * Makes a Json from a given message or builder
53 * @param messageOrBuilder is the instance
54 * @return The string representation
55 * @throws IOException if any error occurs
57 public static String toJson(MessageOrBuilder messageOrBuilder) throws IOException {
58 return JsonFormat.printer().print(messageOrBuilder);
62 * Makes a new instance of message based on the json and the class
64 * @param <T> is the class type
65 * @param json is the json instance
66 * @param clazz is the class instance
67 * @return An instance of T based on the json values
68 * @throws IOException if any error occurs
70 @SuppressWarnings({"unchecked", "rawtypes"})
71 public static <T extends Message> T fromJson(String json, Class<T> clazz) throws IOException {
72 // https://stackoverflow.com/questions/27642021/calling-parsefrom-method-for-generic-protobuffer-class-in-java/33701202#33701202
73 Builder builder = null;
75 // Since we are dealing with a Message type, we can call newBuilder()
76 builder = (Builder) clazz.getMethod("newBuilder").invoke(null);
78 } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException
79 | NoSuchMethodException | SecurityException e) {
83 // The instance is placed into the builder values
84 JsonFormat.parser().ignoringUnknownFields().merge(json, builder);
86 // the instance will be from the build
87 return (T) builder.build();
91 private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
93 private static Gson gson = new GsonBuilder() //
94 .disableHtmlEscaping() //
97 private String filterReport(PmReportFilter filter) throws Exception {
99 TopicListener.DataFromTopic data =
100 new TopicListener.DataFromTopic("typeId", null, null, loadReport().getBytes());
101 FilteredData filtered = filter.filter(data);
103 String reportAfterFilter = gson.toJson(data.getCachedPmReport());
104 String reportBeforeFilter = gson.toJson(gson.fromJson(loadReport(), PmReport.class));
106 assertThat(reportAfterFilter).isEqualTo(reportBeforeFilter);
108 return filtered.getValueAString();
112 void testPmFilterMeasTypes() throws Exception {
114 PmReportFilter.FilterData filterData = new PmReportFilter.FilterData();
115 filterData.addMeasTypes("UtranCell", "succImmediateAssignProcs");
117 PmReportFilter filter = new PmReportFilter(filterData);
118 String filtered = filterReport(filter);
120 assertThat(filtered).contains("succImmediateAssignProcs").doesNotContain("\"p\":2").contains("\"p\":1")
121 .contains("Gbg-997");
123 // Test that no report is returned if not meas types were found
124 filterData = new PmReportFilter.FilterData();
125 filterData.addMeasTypes("junk", "succImmediateAssignProcs");
127 filter = new PmReportFilter(filterData);
128 filtered = filterReport(filter);
129 assertThat(filtered).isEmpty();
133 void testMeasObjInstIds() throws Exception {
134 PmReportFilter.FilterData filterData = new PmReportFilter.FilterData();
135 filterData.measObjInstIds.add("junk");
136 PmReportFilter filter = new PmReportFilter(filterData);
137 String filtered = filterReport(filter);
138 assertThat(filtered).isEmpty();
140 filterData = new PmReportFilter.FilterData();
141 filterData.measObjInstIds.add("UtranCell=Gbg-997");
142 filter = new PmReportFilter(filterData);
143 filtered = filterReport(filter);
144 assertThat(filtered).contains("Gbg-997").doesNotContain("Gbg-998");
148 void testMeasObjClass() throws Exception {
150 PmReportFilter.FilterData filterData = new PmReportFilter.FilterData();
151 filterData.addMeasTypes("junk");
152 PmReportFilter filter = new PmReportFilter(filterData);
153 String filtered = filterReport(filter);
154 assertThat(filtered).isEmpty();
158 TopicListener.DataFromTopic data =
159 new TopicListener.DataFromTopic("typeId", null, null, loadReport().getBytes());
161 PmReportFilter.FilterData utranCellFilter = new PmReportFilter.FilterData();
162 utranCellFilter.addMeasTypes("UtranCell");
163 FilteredData filtered = new PmReportFilter(utranCellFilter).filter(data);
164 assertThat(filtered.getValueAString()).contains("UtranCell").doesNotContain("ENodeBFunction");
166 PmReportFilter.FilterData eNodeBFilter = new PmReportFilter.FilterData();
167 eNodeBFilter.addMeasTypes("ENodeBFunction");
168 filtered = new PmReportFilter(eNodeBFilter).filter(data);
169 assertThat(filtered.getValueAString()).contains("ENodeBFunction").doesNotContain("UtranCell");
174 void testSourceNames() throws Exception {
175 PmReportFilter.FilterData filterData = new PmReportFilter.FilterData();
176 filterData.sourceNames.add("junk");
177 PmReportFilter filter = new PmReportFilter(filterData);
178 String filtered = filterReport(filter);
179 assertThat(filtered).isEmpty();
181 filterData = new PmReportFilter.FilterData();
182 filterData.sourceNames.add("O-DU-1122");
183 filter = new PmReportFilter(filterData);
184 filtered = filterReport(filter);
185 assertThat(filtered).contains("O-DU-1122");
189 void testSomeCharacteristics() throws Exception {
190 String path = "./src/test/resources/A20000626.2315+0200-2330+0200_HTTPS-6-73.json";
192 String pmReportJson = Files.readString(Path.of(path), Charset.defaultCharset());
197 path = "./src/test/resources/A20000626.2315+0200-2330+0200_HTTPS-6-73.json.gz";
198 byte[] pmReportZipped = Files.readAllBytes(Path.of(path));
200 Instant startTime = Instant.now();
201 for (int i = 0; i < TIMES; ++i) {
202 TopicListener.unzip(pmReportZipped);
205 printDuration("Unzip", startTime, TIMES);
209 PmReportFilter.FilterData filterData = new PmReportFilter.FilterData();
210 filterData.addMeasTypes("NRCellCU", "pmCounterNumber0");
211 PmReportFilter filter = new PmReportFilter(filterData);
212 DataFromTopic topicData = new DataFromTopic("typeId", null, null, pmReportJson.getBytes());
214 Instant startTime = Instant.now();
215 for (int i = 0; i < TIMES; ++i) {
216 filter.filter(topicData);
218 printDuration("PM Filter", startTime, TIMES);
221 Instant startTime = Instant.now();
222 for (int i = 0; i < TIMES; ++i) {
223 gson.fromJson(pmReportJson, PmReport.class);
225 printDuration("Json parsing", startTime, TIMES);
230 void printDuration(String str, Instant startTime, int noOfIterations) {
231 final long durationMs = Instant.now().toEpochMilli() - startTime.toEpochMilli();
232 logger.info("*** Duration (ms) " + str + " :" + durationMs + ", objects/second: "
233 + (noOfIterations * 1000) / durationMs);
237 void testMeasuredEntityDns() throws Exception {
238 PmReportFilter.FilterData filterData = new PmReportFilter.FilterData();
239 filterData.measuredEntityDns.add("junk");
240 PmReportFilter filter = new PmReportFilter(filterData);
241 String filtered = filterReport(filter);
242 assertThat(filtered).isEmpty();
244 filterData = new PmReportFilter.FilterData();
245 filterData.measuredEntityDns.add("ManagedElement=RNC-Gbg-1");
246 filter = new PmReportFilter(filterData);
247 filtered = filterReport(filter);
248 assertThat(filtered).contains("ManagedElement=RNC-Gbg-1");
252 void testCrapInput() {
253 PmReportFilter.FilterData filterData = new PmReportFilter.FilterData();
254 PmReportFilter filter = new PmReportFilter(filterData);
256 FilteredData filtered = filter.filter(new TopicListener.DataFromTopic("typeId", null, null, "junk".getBytes()));
257 assertThat(filtered.isEmpty()).isTrue();
260 .filter(new TopicListener.DataFromTopic("typeId", null, null, reQuote("{'msg': 'test'}").getBytes()));
261 assertThat(filtered.isEmpty()).isTrue();
265 private String reQuote(String str) {
266 return str.replaceAll("'", "\\\"");
270 void testParse() throws Exception {
271 com.google.gson.Gson gson = new com.google.gson.GsonBuilder().disableHtmlEscaping().create();
272 PmReport report = gson.fromJson(loadReport(), PmReport.class);
274 String dn = report.event.getPerf3gppFields().getMeasDataCollection().getMeasuredEntityDn();
275 String json = gson.toJson(report);
276 report = gson.fromJson(json, PmReport.class);
278 // '=' is escaped to unicode by gson. but converted back
279 assertThat(report.event.getPerf3gppFields().getMeasDataCollection().getMeasuredEntityDn()).isEqualTo(dn);
282 private String loadReport() throws Exception {
283 String path = "./src/test/resources/pm_report.json";
284 return Files.readString(Path.of(path), Charset.defaultCharset());