1 // Copyright (C) 2017 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "src/guardrail/StatsdStats.h"
16 #include "statslog.h"
17 #include "tests/statsd_test_util.h"
18
19 #include <gtest/gtest.h>
20 #include <vector>
21
22 #ifdef __ANDROID__
23
24 namespace android {
25 namespace os {
26 namespace statsd {
27
28 using std::vector;
29
TEST(StatsdStatsTest,TestValidConfigAdd)30 TEST(StatsdStatsTest, TestValidConfigAdd) {
31 StatsdStats stats;
32 ConfigKey key(0, 12345);
33 const int metricsCount = 10;
34 const int conditionsCount = 20;
35 const int matchersCount = 30;
36 const int alertsCount = 10;
37 stats.noteConfigReceived(key, metricsCount, conditionsCount, matchersCount, alertsCount, {},
38 true /*valid config*/);
39 vector<uint8_t> output;
40 stats.dumpStats(&output, false /*reset stats*/);
41
42 StatsdStatsReport report;
43 bool good = report.ParseFromArray(&output[0], output.size());
44 EXPECT_TRUE(good);
45 EXPECT_EQ(1, report.config_stats_size());
46 const auto& configReport = report.config_stats(0);
47 EXPECT_EQ(0, configReport.uid());
48 EXPECT_EQ(12345, configReport.id());
49 EXPECT_EQ(metricsCount, configReport.metric_count());
50 EXPECT_EQ(conditionsCount, configReport.condition_count());
51 EXPECT_EQ(matchersCount, configReport.matcher_count());
52 EXPECT_EQ(alertsCount, configReport.alert_count());
53 EXPECT_EQ(true, configReport.is_valid());
54 EXPECT_FALSE(configReport.has_deletion_time_sec());
55 }
56
TEST(StatsdStatsTest,TestInvalidConfigAdd)57 TEST(StatsdStatsTest, TestInvalidConfigAdd) {
58 StatsdStats stats;
59 ConfigKey key(0, 12345);
60 const int metricsCount = 10;
61 const int conditionsCount = 20;
62 const int matchersCount = 30;
63 const int alertsCount = 10;
64 stats.noteConfigReceived(key, metricsCount, conditionsCount, matchersCount, alertsCount, {},
65 false /*bad config*/);
66 vector<uint8_t> output;
67 stats.dumpStats(&output, false);
68
69 StatsdStatsReport report;
70 bool good = report.ParseFromArray(&output[0], output.size());
71 EXPECT_TRUE(good);
72 EXPECT_EQ(1, report.config_stats_size());
73 const auto& configReport = report.config_stats(0);
74 // The invalid config should be put into icebox with a deletion time.
75 EXPECT_TRUE(configReport.has_deletion_time_sec());
76 }
77
TEST(StatsdStatsTest,TestConfigRemove)78 TEST(StatsdStatsTest, TestConfigRemove) {
79 StatsdStats stats;
80 ConfigKey key(0, 12345);
81 const int metricsCount = 10;
82 const int conditionsCount = 20;
83 const int matchersCount = 30;
84 const int alertsCount = 10;
85 stats.noteConfigReceived(key, metricsCount, conditionsCount, matchersCount, alertsCount, {},
86 true);
87 vector<uint8_t> output;
88 stats.dumpStats(&output, false);
89 StatsdStatsReport report;
90 bool good = report.ParseFromArray(&output[0], output.size());
91 EXPECT_TRUE(good);
92 EXPECT_EQ(1, report.config_stats_size());
93 const auto& configReport = report.config_stats(0);
94 EXPECT_FALSE(configReport.has_deletion_time_sec());
95
96 stats.noteConfigRemoved(key);
97 stats.dumpStats(&output, false);
98 good = report.ParseFromArray(&output[0], output.size());
99 EXPECT_TRUE(good);
100 EXPECT_EQ(1, report.config_stats_size());
101 const auto& configReport2 = report.config_stats(0);
102 EXPECT_TRUE(configReport2.has_deletion_time_sec());
103 }
104
TEST(StatsdStatsTest,TestSubStats)105 TEST(StatsdStatsTest, TestSubStats) {
106 StatsdStats stats;
107 ConfigKey key(0, 12345);
108 stats.noteConfigReceived(key, 2, 3, 4, 5, {std::make_pair(123, 456)}, true);
109
110 stats.noteMatcherMatched(key, StringToId("matcher1"));
111 stats.noteMatcherMatched(key, StringToId("matcher1"));
112 stats.noteMatcherMatched(key, StringToId("matcher2"));
113
114 stats.noteConditionDimensionSize(key, StringToId("condition1"), 250);
115 stats.noteConditionDimensionSize(key, StringToId("condition1"), 240);
116
117 stats.noteMetricDimensionSize(key, StringToId("metric1"), 201);
118 stats.noteMetricDimensionSize(key, StringToId("metric1"), 202);
119
120 stats.noteAnomalyDeclared(key, StringToId("alert1"));
121 stats.noteAnomalyDeclared(key, StringToId("alert1"));
122 stats.noteAnomalyDeclared(key, StringToId("alert2"));
123
124 // broadcast-> 2
125 stats.noteBroadcastSent(key);
126 stats.noteBroadcastSent(key);
127
128 // data drop -> 1
129 stats.noteDataDropped(key, 123);
130
131 // dump report -> 3
132 stats.noteMetricsReportSent(key, 0);
133 stats.noteMetricsReportSent(key, 0);
134 stats.noteMetricsReportSent(key, 0);
135
136 // activation_time_sec -> 2
137 stats.noteActiveStatusChanged(key, true);
138 stats.noteActiveStatusChanged(key, true);
139
140 // deactivation_time_sec -> 1
141 stats.noteActiveStatusChanged(key, false);
142
143 vector<uint8_t> output;
144 stats.dumpStats(&output, true); // Dump and reset stats
145 StatsdStatsReport report;
146 bool good = report.ParseFromArray(&output[0], output.size());
147 EXPECT_TRUE(good);
148 EXPECT_EQ(1, report.config_stats_size());
149 const auto& configReport = report.config_stats(0);
150 EXPECT_EQ(2, configReport.broadcast_sent_time_sec_size());
151 EXPECT_EQ(1, configReport.data_drop_time_sec_size());
152 EXPECT_EQ(1, configReport.data_drop_bytes_size());
153 EXPECT_EQ(123, configReport.data_drop_bytes(0));
154 EXPECT_EQ(3, configReport.dump_report_time_sec_size());
155 EXPECT_EQ(3, configReport.dump_report_data_size_size());
156 EXPECT_EQ(2, configReport.activation_time_sec_size());
157 EXPECT_EQ(1, configReport.deactivation_time_sec_size());
158 EXPECT_EQ(1, configReport.annotation_size());
159 EXPECT_EQ(123, configReport.annotation(0).field_int64());
160 EXPECT_EQ(456, configReport.annotation(0).field_int32());
161
162 EXPECT_EQ(2, configReport.matcher_stats_size());
163 // matcher1 is the first in the list
164 if (configReport.matcher_stats(0).id() == StringToId("matcher1")) {
165 EXPECT_EQ(2, configReport.matcher_stats(0).matched_times());
166 EXPECT_EQ(1, configReport.matcher_stats(1).matched_times());
167 EXPECT_EQ(StringToId("matcher2"), configReport.matcher_stats(1).id());
168 } else {
169 // matcher1 is the second in the list.
170 EXPECT_EQ(1, configReport.matcher_stats(0).matched_times());
171 EXPECT_EQ(StringToId("matcher2"), configReport.matcher_stats(0).id());
172
173 EXPECT_EQ(2, configReport.matcher_stats(1).matched_times());
174 EXPECT_EQ(StringToId("matcher1"), configReport.matcher_stats(1).id());
175 }
176
177 EXPECT_EQ(2, configReport.alert_stats_size());
178 bool alert1first = configReport.alert_stats(0).id() == StringToId("alert1");
179 EXPECT_EQ(StringToId("alert1"), configReport.alert_stats(alert1first ? 0 : 1).id());
180 EXPECT_EQ(2, configReport.alert_stats(alert1first ? 0 : 1).alerted_times());
181 EXPECT_EQ(StringToId("alert2"), configReport.alert_stats(alert1first ? 1 : 0).id());
182 EXPECT_EQ(1, configReport.alert_stats(alert1first ? 1 : 0).alerted_times());
183
184 EXPECT_EQ(1, configReport.condition_stats_size());
185 EXPECT_EQ(StringToId("condition1"), configReport.condition_stats(0).id());
186 EXPECT_EQ(250, configReport.condition_stats(0).max_tuple_counts());
187
188 EXPECT_EQ(1, configReport.metric_stats_size());
189 EXPECT_EQ(StringToId("metric1"), configReport.metric_stats(0).id());
190 EXPECT_EQ(202, configReport.metric_stats(0).max_tuple_counts());
191
192 // after resetting the stats, some new events come
193 stats.noteMatcherMatched(key, StringToId("matcher99"));
194 stats.noteConditionDimensionSize(key, StringToId("condition99"), 300);
195 stats.noteMetricDimensionSize(key, StringToId("metric99tion99"), 270);
196 stats.noteAnomalyDeclared(key, StringToId("alert99"));
197
198 // now the config stats should only contain the stats about the new event.
199 stats.dumpStats(&output, false);
200 good = report.ParseFromArray(&output[0], output.size());
201 EXPECT_TRUE(good);
202 EXPECT_EQ(1, report.config_stats_size());
203 const auto& configReport2 = report.config_stats(0);
204 EXPECT_EQ(1, configReport2.matcher_stats_size());
205 EXPECT_EQ(StringToId("matcher99"), configReport2.matcher_stats(0).id());
206 EXPECT_EQ(1, configReport2.matcher_stats(0).matched_times());
207
208 EXPECT_EQ(1, configReport2.condition_stats_size());
209 EXPECT_EQ(StringToId("condition99"), configReport2.condition_stats(0).id());
210 EXPECT_EQ(300, configReport2.condition_stats(0).max_tuple_counts());
211
212 EXPECT_EQ(1, configReport2.metric_stats_size());
213 EXPECT_EQ(StringToId("metric99tion99"), configReport2.metric_stats(0).id());
214 EXPECT_EQ(270, configReport2.metric_stats(0).max_tuple_counts());
215
216 EXPECT_EQ(1, configReport2.alert_stats_size());
217 EXPECT_EQ(StringToId("alert99"), configReport2.alert_stats(0).id());
218 EXPECT_EQ(1, configReport2.alert_stats(0).alerted_times());
219 }
220
TEST(StatsdStatsTest,TestAtomLog)221 TEST(StatsdStatsTest, TestAtomLog) {
222 StatsdStats stats;
223 time_t now = time(nullptr);
224 // old event, we get it from the stats buffer. should be ignored.
225 stats.noteAtomLogged(android::util::SENSOR_STATE_CHANGED, 1000);
226
227 stats.noteAtomLogged(android::util::SENSOR_STATE_CHANGED, now + 1);
228 stats.noteAtomLogged(android::util::SENSOR_STATE_CHANGED, now + 2);
229 stats.noteAtomLogged(android::util::APP_CRASH_OCCURRED, now + 3);
230
231 vector<uint8_t> output;
232 stats.dumpStats(&output, false);
233 StatsdStatsReport report;
234 bool good = report.ParseFromArray(&output[0], output.size());
235 EXPECT_TRUE(good);
236
237 EXPECT_EQ(2, report.atom_stats_size());
238 bool sensorAtomGood = false;
239 bool dropboxAtomGood = false;
240
241 for (const auto& atomStats : report.atom_stats()) {
242 if (atomStats.tag() == android::util::SENSOR_STATE_CHANGED && atomStats.count() == 3) {
243 sensorAtomGood = true;
244 }
245 if (atomStats.tag() == android::util::APP_CRASH_OCCURRED && atomStats.count() == 1) {
246 dropboxAtomGood = true;
247 }
248 }
249
250 EXPECT_TRUE(dropboxAtomGood);
251 EXPECT_TRUE(sensorAtomGood);
252 }
253
TEST(StatsdStatsTest,TestNonPlatformAtomLog)254 TEST(StatsdStatsTest, TestNonPlatformAtomLog) {
255 StatsdStats stats;
256 time_t now = time(nullptr);
257 int newAtom1 = android::util::kMaxPushedAtomId + 1;
258 int newAtom2 = android::util::kMaxPushedAtomId + 2;
259
260 stats.noteAtomLogged(newAtom1, now + 1);
261 stats.noteAtomLogged(newAtom1, now + 2);
262 stats.noteAtomLogged(newAtom2, now + 3);
263
264 vector<uint8_t> output;
265 stats.dumpStats(&output, false);
266 StatsdStatsReport report;
267 bool good = report.ParseFromArray(&output[0], output.size());
268 EXPECT_TRUE(good);
269
270 EXPECT_EQ(2, report.atom_stats_size());
271 bool newAtom1Good = false;
272 bool newAtom2Good = false;
273
274 for (const auto& atomStats : report.atom_stats()) {
275 if (atomStats.tag() == newAtom1 && atomStats.count() == 2) {
276 newAtom1Good = true;
277 }
278 if (atomStats.tag() == newAtom2 && atomStats.count() == 1) {
279 newAtom2Good = true;
280 }
281 }
282
283 EXPECT_TRUE(newAtom1Good);
284 EXPECT_TRUE(newAtom2Good);
285 }
286
TEST(StatsdStatsTest,TestPullAtomStats)287 TEST(StatsdStatsTest, TestPullAtomStats) {
288 StatsdStats stats;
289
290 stats.updateMinPullIntervalSec(android::util::DISK_SPACE, 3333L);
291 stats.updateMinPullIntervalSec(android::util::DISK_SPACE, 2222L);
292 stats.updateMinPullIntervalSec(android::util::DISK_SPACE, 4444L);
293
294 stats.notePull(android::util::DISK_SPACE);
295 stats.notePullTime(android::util::DISK_SPACE, 1111L);
296 stats.notePullDelay(android::util::DISK_SPACE, 1111L);
297 stats.notePull(android::util::DISK_SPACE);
298 stats.notePullTime(android::util::DISK_SPACE, 3333L);
299 stats.notePullDelay(android::util::DISK_SPACE, 3335L);
300 stats.notePull(android::util::DISK_SPACE);
301 stats.notePullFromCache(android::util::DISK_SPACE);
302 stats.notePullerCallbackRegistrationChanged(android::util::DISK_SPACE, true);
303 stats.notePullerCallbackRegistrationChanged(android::util::DISK_SPACE, false);
304 stats.notePullerCallbackRegistrationChanged(android::util::DISK_SPACE, true);
305
306
307 vector<uint8_t> output;
308 stats.dumpStats(&output, false);
309 StatsdStatsReport report;
310 bool good = report.ParseFromArray(&output[0], output.size());
311 EXPECT_TRUE(good);
312
313 EXPECT_EQ(1, report.pulled_atom_stats_size());
314
315 EXPECT_EQ(android::util::DISK_SPACE, report.pulled_atom_stats(0).atom_id());
316 EXPECT_EQ(3, report.pulled_atom_stats(0).total_pull());
317 EXPECT_EQ(1, report.pulled_atom_stats(0).total_pull_from_cache());
318 EXPECT_EQ(2222L, report.pulled_atom_stats(0).min_pull_interval_sec());
319 EXPECT_EQ(2222L, report.pulled_atom_stats(0).average_pull_time_nanos());
320 EXPECT_EQ(3333L, report.pulled_atom_stats(0).max_pull_time_nanos());
321 EXPECT_EQ(2223L, report.pulled_atom_stats(0).average_pull_delay_nanos());
322 EXPECT_EQ(3335L, report.pulled_atom_stats(0).max_pull_delay_nanos());
323 EXPECT_EQ(2L, report.pulled_atom_stats(0).registered_count());
324 EXPECT_EQ(1L, report.pulled_atom_stats(0).unregistered_count());
325 }
326
TEST(StatsdStatsTest,TestAtomMetricsStats)327 TEST(StatsdStatsTest, TestAtomMetricsStats) {
328 StatsdStats stats;
329 time_t now = time(nullptr);
330 // old event, we get it from the stats buffer. should be ignored.
331 stats.noteBucketDropped(1000L);
332
333 stats.noteBucketBoundaryDelayNs(1000L, -1L);
334 stats.noteBucketBoundaryDelayNs(1000L, -10L);
335 stats.noteBucketBoundaryDelayNs(1000L, 2L);
336
337 stats.noteBucketBoundaryDelayNs(1001L, 1L);
338
339 vector<uint8_t> output;
340 stats.dumpStats(&output, false);
341 StatsdStatsReport report;
342 bool good = report.ParseFromArray(&output[0], output.size());
343 EXPECT_TRUE(good);
344
345 EXPECT_EQ(2, report.atom_metric_stats().size());
346
347 auto atomStats = report.atom_metric_stats(0);
348 EXPECT_EQ(1000L, atomStats.metric_id());
349 EXPECT_EQ(1L, atomStats.bucket_dropped());
350 EXPECT_EQ(-10L, atomStats.min_bucket_boundary_delay_ns());
351 EXPECT_EQ(2L, atomStats.max_bucket_boundary_delay_ns());
352
353 auto atomStats2 = report.atom_metric_stats(1);
354 EXPECT_EQ(1001L, atomStats2.metric_id());
355 EXPECT_EQ(0L, atomStats2.bucket_dropped());
356 EXPECT_EQ(0L, atomStats2.min_bucket_boundary_delay_ns());
357 EXPECT_EQ(1L, atomStats2.max_bucket_boundary_delay_ns());
358 }
359
TEST(StatsdStatsTest,TestAnomalyMonitor)360 TEST(StatsdStatsTest, TestAnomalyMonitor) {
361 StatsdStats stats;
362 stats.noteRegisteredAnomalyAlarmChanged();
363 stats.noteRegisteredAnomalyAlarmChanged();
364
365 vector<uint8_t> output;
366 stats.dumpStats(&output, false);
367 StatsdStatsReport report;
368 bool good = report.ParseFromArray(&output[0], output.size());
369 EXPECT_TRUE(good);
370
371 EXPECT_EQ(2, report.anomaly_alarm_stats().alarms_registered());
372 }
373
TEST(StatsdStatsTest,TestTimestampThreshold)374 TEST(StatsdStatsTest, TestTimestampThreshold) {
375 StatsdStats stats;
376 vector<int32_t> timestamps;
377 for (int i = 0; i < StatsdStats::kMaxTimestampCount; i++) {
378 timestamps.push_back(i);
379 }
380 ConfigKey key(0, 12345);
381 stats.noteConfigReceived(key, 2, 3, 4, 5, {}, true);
382
383 for (int i = 0; i < StatsdStats::kMaxTimestampCount; i++) {
384 stats.noteDataDropped(key, timestamps[i]);
385 stats.noteBroadcastSent(key, timestamps[i]);
386 stats.noteMetricsReportSent(key, 0, timestamps[i]);
387 stats.noteActiveStatusChanged(key, true, timestamps[i]);
388 stats.noteActiveStatusChanged(key, false, timestamps[i]);
389 }
390
391 int32_t newTimestamp = 10000;
392
393 // now it should trigger removing oldest timestamp
394 stats.noteDataDropped(key, 123, 10000);
395 stats.noteBroadcastSent(key, 10000);
396 stats.noteMetricsReportSent(key, 0, 10000);
397 stats.noteActiveStatusChanged(key, true, 10000);
398 stats.noteActiveStatusChanged(key, false, 10000);
399
400 EXPECT_TRUE(stats.mConfigStats.find(key) != stats.mConfigStats.end());
401 const auto& configStats = stats.mConfigStats[key];
402
403 size_t maxCount = StatsdStats::kMaxTimestampCount;
404 EXPECT_EQ(maxCount, configStats->broadcast_sent_time_sec.size());
405 EXPECT_EQ(maxCount, configStats->data_drop_time_sec.size());
406 EXPECT_EQ(maxCount, configStats->dump_report_stats.size());
407 EXPECT_EQ(maxCount, configStats->activation_time_sec.size());
408 EXPECT_EQ(maxCount, configStats->deactivation_time_sec.size());
409
410 // the oldest timestamp is the second timestamp in history
411 EXPECT_EQ(1, configStats->broadcast_sent_time_sec.front());
412 EXPECT_EQ(1, configStats->data_drop_bytes.front());
413 EXPECT_EQ(1, configStats->dump_report_stats.front().first);
414 EXPECT_EQ(1, configStats->activation_time_sec.front());
415 EXPECT_EQ(1, configStats->deactivation_time_sec.front());
416
417 // the last timestamp is the newest timestamp.
418 EXPECT_EQ(newTimestamp, configStats->broadcast_sent_time_sec.back());
419 EXPECT_EQ(newTimestamp, configStats->data_drop_time_sec.back());
420 EXPECT_EQ(123, configStats->data_drop_bytes.back());
421 EXPECT_EQ(newTimestamp, configStats->dump_report_stats.back().first);
422 EXPECT_EQ(newTimestamp, configStats->activation_time_sec.back());
423 EXPECT_EQ(newTimestamp, configStats->deactivation_time_sec.back());
424 }
425
TEST(StatsdStatsTest,TestSystemServerCrash)426 TEST(StatsdStatsTest, TestSystemServerCrash) {
427 StatsdStats stats;
428 vector<int32_t> timestamps;
429 for (int i = 0; i < StatsdStats::kMaxSystemServerRestarts; i++) {
430 timestamps.push_back(i);
431 stats.noteSystemServerRestart(timestamps[i]);
432 }
433 vector<uint8_t> output;
434 stats.dumpStats(&output, false);
435 StatsdStatsReport report;
436 EXPECT_TRUE(report.ParseFromArray(&output[0], output.size()));
437 const int maxCount = StatsdStats::kMaxSystemServerRestarts;
438 EXPECT_EQ(maxCount, (int)report.system_restart_sec_size());
439
440 stats.noteSystemServerRestart(StatsdStats::kMaxSystemServerRestarts + 1);
441 output.clear();
442 stats.dumpStats(&output, false);
443 EXPECT_TRUE(report.ParseFromArray(&output[0], output.size()));
444 EXPECT_EQ(maxCount, (int)report.system_restart_sec_size());
445 EXPECT_EQ(StatsdStats::kMaxSystemServerRestarts + 1, report.system_restart_sec(maxCount - 1));
446 }
447
TEST(StatsdStatsTest,TestActivationBroadcastGuardrailHit)448 TEST(StatsdStatsTest, TestActivationBroadcastGuardrailHit) {
449 StatsdStats stats;
450 int uid1 = 1;
451 int uid2 = 2;
452 stats.noteActivationBroadcastGuardrailHit(uid1, 10);
453 stats.noteActivationBroadcastGuardrailHit(uid1, 20);
454
455 // Test that we only keep 20 timestamps.
456 for (int i = 0; i < 100; i++) {
457 stats.noteActivationBroadcastGuardrailHit(uid2, i);
458 }
459
460 vector<uint8_t> output;
461 stats.dumpStats(&output, false);
462 StatsdStatsReport report;
463 EXPECT_TRUE(report.ParseFromArray(&output[0], output.size()));
464
465 EXPECT_EQ(2, report.activation_guardrail_stats_size());
466 bool uid1Good = false;
467 bool uid2Good = false;
468 for (const auto& guardrailTimes : report.activation_guardrail_stats()) {
469 if (uid1 == guardrailTimes.uid()) {
470 uid1Good = true;
471 EXPECT_EQ(2, guardrailTimes.guardrail_met_sec_size());
472 EXPECT_EQ(10, guardrailTimes.guardrail_met_sec(0));
473 EXPECT_EQ(20, guardrailTimes.guardrail_met_sec(1));
474 } else if (uid2 == guardrailTimes.uid()) {
475 int maxCount = StatsdStats::kMaxTimestampCount;
476 uid2Good = true;
477 EXPECT_EQ(maxCount, guardrailTimes.guardrail_met_sec_size());
478 for (int i = 0; i < maxCount; i++) {
479 EXPECT_EQ(100 - maxCount + i, guardrailTimes.guardrail_met_sec(i));
480 }
481 } else {
482 FAIL() << "Unexpected uid.";
483 }
484 }
485 EXPECT_TRUE(uid1Good);
486 EXPECT_TRUE(uid2Good);
487 }
488
489 } // namespace statsd
490 } // namespace os
491 } // namespace android
492 #else
493 GTEST_LOG_(INFO) << "This test does nothing.\n";
494 #endif
495