1/*
2 * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24#include "precompiled.hpp"
25#include "logging/logFileStreamOutput.hpp"
26#include "logging/logLevel.hpp"
27#include "logging/logOutput.hpp"
28#include "logging/logOutputList.hpp"
29#include "runtime/os.hpp"
30#include "unittest.hpp"
31
32// Count the outputs in the given list, starting from the specified level
33static size_t output_count(LogOutputList* list, LogLevelType from = LogLevel::Error) {
34 size_t count = 0;
35 for (LogOutputList::Iterator it = list->iterator(from); it != list->end(); it++) {
36 count++;
37 }
38 return count;
39}
40
41// Get the level for an output in the given list
42static LogLevelType find_output_level(LogOutputList* list, LogOutput* o) {
43 for (size_t levelnum = 1; levelnum < LogLevel::Count; levelnum++) {
44 LogLevelType level = static_cast<LogLevelType>(levelnum);
45 for (LogOutputList::Iterator it = list->iterator(level); it != list->end(); it++) {
46 if (*it == o) {
47 return level;
48 }
49 }
50 }
51 return LogLevel::Off;
52}
53
54// Create a dummy output pointer with the specified id.
55// This dummy pointer should not be used for anything
56// but pointer comparisons with other dummies.
57static LogOutput* dummy_output(size_t id) {
58 return reinterpret_cast<LogOutput*>(id + 1);
59}
60
61// Randomly update and verify some outputs some number of times
62TEST(LogOutputList, set_output_level_update) {
63 const size_t TestOutputCount = 10;
64 const size_t TestIterations = 10000;
65 LogOutputList list;
66 size_t outputs_on_level[LogLevel::Count];
67 LogLevelType expected_level_for_output[TestOutputCount];
68
69 os::init_random(0x4711);
70 for (size_t i = 0; i < LogLevel::Count; i++) {
71 outputs_on_level[i] = 0;
72 }
73 outputs_on_level[LogLevel::Off] = TestOutputCount;
74 for (size_t i = 0; i < TestOutputCount; i++) {
75 expected_level_for_output[i] = LogLevel::Off;
76 }
77
78 for (size_t iteration = 0; iteration < TestIterations; iteration++) {
79 size_t output_idx = os::random() % TestOutputCount;
80 size_t levelnum = os::random() % LogLevel::Count;
81 LogLevelType level = static_cast<LogLevelType>(levelnum);
82
83 // Update the expectations
84 outputs_on_level[expected_level_for_output[output_idx]]--;
85 outputs_on_level[levelnum]++;
86 expected_level_for_output[output_idx] = level;
87
88 // Update the actual list
89 list.set_output_level(dummy_output(output_idx), level);
90
91 // Verify expected levels
92 for (size_t i = 0; i < TestOutputCount; i++) {
93 ASSERT_EQ(expected_level_for_output[i], find_output_level(&list, dummy_output(i)));
94 }
95 // Verify output counts
96 size_t expected_count = 0;
97 for (size_t i = 1; i < LogLevel::Count; i++) {
98 expected_count += outputs_on_level[i];
99 ASSERT_EQ(expected_count, output_count(&list, static_cast<LogLevelType>(i)));
100 }
101 ASSERT_EQ(TestOutputCount, expected_count + outputs_on_level[LogLevel::Off]);
102 }
103}
104
105// Test removing outputs from a LogOutputList
106TEST(LogOutputList, set_output_level_remove) {
107 LogOutputList list;
108
109 // Add three dummy outputs per loglevel
110 for (size_t i = 1; i < LogLevel::Count; i++) {
111 list.set_output_level(dummy_output(i), static_cast<LogLevelType>(i));
112 list.set_output_level(dummy_output(i*10), static_cast<LogLevelType>(i));
113 list.set_output_level(dummy_output(i*100), static_cast<LogLevelType>(i));
114 }
115
116 // Verify that they have been added successfully
117 // (Count - 1 since we don't count LogLevel::Off)
118 EXPECT_EQ(3u * (LogLevel::Count - 1), output_count(&list));
119 // Now remove the second output from each loglevel
120 for (size_t i = 1; i < LogLevel::Count; i++) {
121 list.set_output_level(dummy_output(i*10), LogLevel::Off);
122 }
123 // Make sure they have been successfully removed
124 EXPECT_EQ(2u * (LogLevel::Count - 1), output_count(&list));
125
126 // Now remove the remaining outputs
127 for (size_t i = 1; i < LogLevel::Count; i++) {
128 list.set_output_level(dummy_output(i), LogLevel::Off);
129 list.set_output_level(dummy_output(i*100), LogLevel::Off);
130 }
131 EXPECT_EQ(0u, output_count(&list));
132}
133
134// Test adding to a LogOutputList
135TEST(LogOutputList, set_output_level_add) {
136 LogOutputList list;
137
138 // First add 5 outputs to Info level
139 for (size_t i = 10; i < 15; i++) {
140 list.set_output_level(dummy_output(i), LogLevel::Info);
141 }
142
143 // Verify that they have been added successfully
144 size_t count = 0;
145 for (LogOutputList::Iterator it = list.iterator(); it != list.end(); it++) {
146 ASSERT_EQ(dummy_output(10 + count++), *it);
147 }
148 ASSERT_EQ(5u, count);
149
150 // Now add more outputs, but on all different levels
151 for (size_t i = 5; i < 10; i++) {
152 list.set_output_level(dummy_output(i), LogLevel::Warning);
153 }
154 for (size_t i = 0; i < 5; i++) {
155 list.set_output_level(dummy_output(i), LogLevel::Error);
156 }
157 for (size_t i = 15; i < 20; i++) {
158 list.set_output_level(dummy_output(i), LogLevel::Debug);
159 }
160 for (size_t i = 20; i < 25; i++) {
161 list.set_output_level(dummy_output(i), LogLevel::Trace);
162 }
163
164 // Verify that that all outputs have been added, and that the order is Error, Warning, Info, Debug, Trace
165 count = 0;
166 for (LogOutputList::Iterator it = list.iterator(); it != list.end(); it++) {
167 ASSERT_EQ(dummy_output(count++), *it);
168 }
169 ASSERT_EQ(25u, count);
170}
171
172// Test is_level() on lists with a single output on different levels
173TEST(LogOutputList, is_level_single_output) {
174 for (size_t i = LogLevel::First; i < LogLevel::Count; i++) {
175 LogLevelType level = static_cast<LogLevelType>(i);
176 LogOutputList list;
177 list.set_output_level(&StdoutLog, level);
178 for (size_t j = LogLevel::First; j < LogLevel::Count; j++) {
179 LogLevelType other = static_cast<LogLevelType>(j);
180 // Verify that levels finer than the current level for stdout are reported as disabled,
181 // and levels equal to or included in the current level are reported as enabled
182 if (other >= level) {
183 EXPECT_TRUE(list.is_level(other))
184 << LogLevel::name(other) << " >= " << LogLevel::name(level) << " but is_level() returns false";
185 } else {
186 EXPECT_FALSE(list.is_level(other))
187 << LogLevel::name(other) << " < " << LogLevel::name(level) << " but is_level() returns true";
188 }
189 }
190 }
191}
192
193// Test is_level() with an empty list
194TEST(LogOutputList, is_level_empty) {
195 LogOutputList emptylist;
196 for (size_t i = LogLevel::First; i < LogLevel::Count; i++) {
197 LogLevelType other = static_cast<LogLevelType>(i);
198 EXPECT_FALSE(emptylist.is_level(other)) << "is_level() returns true even though the list is empty";
199 }
200}
201
202// Test is_level() on lists with two outputs on different levels
203TEST(LogOutputList, is_level_multiple_outputs) {
204 for (size_t i = LogLevel::First; i < LogLevel::Count - 1; i++) {
205 LogOutput* dummy1 = &StdoutLog;
206 LogOutput* dummy2 = &StderrLog;
207 LogLevelType first = static_cast<LogLevelType>(i);
208 LogLevelType second = static_cast<LogLevelType>(i + 1);
209 LogOutputList list;
210 list.set_output_level(dummy1, first);
211 list.set_output_level(dummy2, second);
212 for (size_t j = LogLevel::First; j < LogLevel::Count; j++) {
213 LogLevelType other = static_cast<LogLevelType>(j);
214 // The first output's level will be the finest, expect it's level to be reported by the list
215 if (other >= first) {
216 EXPECT_TRUE(list.is_level(other))
217 << LogLevel::name(other) << " >= " << LogLevel::name(first) << " but is_level() returns false";
218 } else {
219 EXPECT_FALSE(list.is_level(other))
220 << LogLevel::name(other) << " < " << LogLevel::name(first) << " but is_level() returns true";
221 }
222 }
223 }
224}
225
226TEST(LogOutputList, level_for) {
227 LogOutputList list;
228
229 // Ask the empty list about stdout, stderr
230 EXPECT_EQ(LogLevel::Off, list.level_for(&StdoutLog));
231 EXPECT_EQ(LogLevel::Off, list.level_for(&StderrLog));
232
233 // Ask for level in a list with two outputs on different levels
234 list.set_output_level(&StdoutLog, LogLevel::Info);
235 list.set_output_level(&StderrLog, LogLevel::Trace);
236 EXPECT_EQ(LogLevel::Info, list.level_for(&StdoutLog));
237 EXPECT_EQ(LogLevel::Trace, list.level_for(&StderrLog));
238
239 // Remove and ask again
240 list.set_output_level(&StdoutLog, LogLevel::Off);
241 EXPECT_EQ(LogLevel::Off, list.level_for(&StdoutLog));
242 EXPECT_EQ(LogLevel::Trace, list.level_for(&StderrLog));
243
244 // Ask about an unknown output
245 LogOutput* dummy = dummy_output(4711);
246 EXPECT_EQ(LogLevel::Off, list.level_for(dummy));
247
248 for (size_t i = LogLevel::First; i <= LogLevel::Last; i++) {
249 LogLevelType level = static_cast<LogLevelType>(i);
250 list.set_output_level(dummy, level);
251 EXPECT_EQ(level, list.level_for(dummy));
252 }
253
254 // Make sure the stderr level is still the same
255 EXPECT_EQ(LogLevel::Trace, list.level_for(&StderrLog));
256}
257