1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
// Copyright (C) 2011-2020 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#include <config.h>

#include <algorithm><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <string><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <gtest/gtest.h><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.

#include <log/log_messages.h>
#include <log/message_dictionary.h>
#include <log/message_exception.h>
#include <log/message_reader.h>

using namespace isc;
using namespace isc::log;
using namespace std;

class MessageReaderTest : public ::testing::Test {
protected:
    MessageReaderTest() : dictionary_(), reader_()
    {
        dictionary_ = new MessageDictionary();
        reader_.setDictionary(dictionary_);
    }

    ~MessageReaderTest() {
        delete dictionary_;
    }

    MessageDictionary*  dictionary_;    // Dictionary to add messages to
    MessageReader       reader_;        // Default reader object
};


// Check the get/set dictionary calls (using a local reader and dictionary).

TEST_F(MessageReaderTest, GetSetDictionary) {
    MessageReader reader;
    EXPECT_TRUE(reader.getDictionary() == NULL);

    MessageDictionary dictionary;
    reader.setDictionary(&dictionary);
    EXPECT_EQ(&dictionary, reader.getDictionary());
}

// Check for parsing blank lines and comments.  These should not add to the
// dictionary and each parse should return success.

TEST_F(MessageReaderTest, BlanksAndComments) {

    // Ensure that the dictionary is empty.
    EXPECT_EQ(0, dictionary_->size());

    // Add a number of blank lines and comments and check that (a) they are
    // parsed successfully ...
    EXPECT_NO_THROW(reader_.processLine(""));
    EXPECT_NO_THROW(reader_.processLine(" "));
    EXPECT_NO_THROW(reader_.processLine(" \n "));
    EXPECT_NO_THROW(reader_.processLine("# This is a comment"));
    EXPECT_NO_THROW(reader_.processLine("\t\t # Another comment"));
    EXPECT_NO_THROW(reader_.processLine("  A description line"));
    EXPECT_NO_THROW(reader_.processLine("# A comment"));
    EXPECT_NO_THROW(reader_.processLine("  +# A description line"));

    // ... and (b) nothing gets added to either the map or the not-added section.
    EXPECT_EQ(0, dictionary_->size());
    vector<string> not_added = reader_.getNotAdded();
    EXPECT_EQ(0, not_added.size());
}


// Local test to check that processLine generates the right exception.

void
processLineException(MessageReader& reader, const char* what,
    const MessageID& expected) {

    try {
        reader.processLine(what);
        FAIL() << "MessageReader::processLine() should throw an exception " <<
            " with message ID " << expected << " for '" << what << "'\n";
    } catch (const MessageException& e) {
        EXPECT_EQ(boost::lexical_cast<string>(expected),
            boost::lexical_cast<string>(e.id()));
    } catch (...) {
        FAIL() << "Unknown exception thrown by MessageReader::processLine()\n";
    }
}

// Check that it recognizes invalid directives

TEST_F(MessageReaderTest, InvalidDirectives) {

    // Check that a "$" with nothing else generates an error
    processLineException(reader_, "$", LOG_UNRECOGNIZED_DIRECTIVE);
    processLineException(reader_, "$xyz", LOG_UNRECOGNIZED_DIRECTIVE);
}

// Check that it can parse a prefix

TEST_F(MessageReaderTest, Prefix) {

    // Check that no $PREFIX is present
    EXPECT_EQ(string(""), reader_.getPrefix());

    // Check that a $PREFIX directive with no argument is OK
    EXPECT_NO_THROW(reader_.processLine("$PREFIX"));

    // Check a $PREFIX with multiple arguments is invalid
    processLineException(reader_, "$prefix A B", LOG_PREFIX_EXTRA_ARGS);

    // Prefixes should be alphanumeric (with underscores) and not start
    // with a number.
    processLineException(reader_, "$prefix ab[cd", LOG_PREFIX_INVALID_ARG);
    processLineException(reader_, "$prefix 123", LOG_PREFIX_INVALID_ARG);
    processLineException(reader_, "$prefix 1ABC", LOG_PREFIX_INVALID_ARG);

    // A valid prefix should be accepted
    EXPECT_NO_THROW(reader_.processLine("$PREFIX   dlm__"));
    EXPECT_EQ(string("dlm__"), reader_.getPrefix());

    // And check that the parser fails on invalid prefixes...
    processLineException(reader_, "$prefix 1ABC", LOG_PREFIX_INVALID_ARG);

    // Check that we can clear the prefix as well
    reader_.clearPrefix();
    EXPECT_EQ(string(""), reader_.getPrefix());

    EXPECT_NO_THROW(reader_.processLine("$PREFIX   dlm__"));
    EXPECT_EQ(string("dlm__"), reader_.getPrefix());
    EXPECT_NO_THROW(reader_.processLine("$PREFIX"));
    EXPECT_EQ(string(""), reader_.getPrefix());
}

// Check that it can parse a namespace

TEST_F(MessageReaderTest, Namespace) {

    // Check that no $NAMESPACE is present
    EXPECT_EQ(string(""), reader_.getNamespace());

    // Check that a $NAMESPACE directive with no argument generates an error.
    processLineException(reader_, "$NAMESPACE", LOG_NAMESPACE_NO_ARGS);

    // Check a $NAMESPACE with multiple arguments is invalid
    processLineException(reader_, "$namespace A B", LOG_NAMESPACE_EXTRA_ARGS);

    // Namespaces should be alphanumeric (with underscores and colons)
    processLineException(reader_, "$namespace ab[cd", LOG_NAMESPACE_INVALID_ARG);

    // A valid $NAMESPACE should be accepted
    EXPECT_NO_THROW(reader_.processLine("$NAMESPACE isc"));
    EXPECT_EQ(string("isc"), reader_.getNamespace());

    // (Check that we can clear the namespace)
    reader_.clearNamespace();
    EXPECT_EQ(string(""), reader_.getNamespace());

    // Check that a valid namespace can include colons
    EXPECT_NO_THROW(reader_.processLine("$NAMESPACE isc::log"));
    EXPECT_EQ(string("isc::log"), reader_.getNamespace());

    // Check that the indication of the anonymous namespace will be recognized.
    reader_.clearNamespace();
    EXPECT_NO_THROW(reader_.processLine("$NAMESPACE ::"));
    EXPECT_EQ(string("::"), reader_.getNamespace());

    // ... and that another $NAMESPACE is rejected
    processLineException(reader_, "$NAMESPACE ABC", LOG_DUPLICATE_NAMESPACE);
}

// Check that it can parse a line

TEST_F(MessageReaderTest, ValidMessageAddDefault) {

    // Add a couple of valid messages
    reader_.processLine("% GLOBAL1\t\tthis is message global one\n");
    reader_.processLine("%GLOBAL2 this is message global two");

    // ... and check them
    EXPECT_EQ(string("this is message global one"),
        dictionary_->getText("GLOBAL1"));
    EXPECT_EQ(string("this is message global two"),
        dictionary_->getText("GLOBAL2"));
    EXPECT_EQ(2, dictionary_->size());

    // ... and ensure no messages were not added
    vector<string> not_added = reader_.getNotAdded();
    EXPECT_EQ(0, not_added.size());
}

TEST_F(MessageReaderTest, ValidMessageAdd) {

    // Add a couple of valid messages
    reader_.processLine("%GLOBAL1\t\tthis is message global one\n",
        MessageReader::ADD);
    reader_.processLine("% GLOBAL2 this is message global two",
        MessageReader::ADD);

    // ... and check them
    EXPECT_EQ(string("this is message global one"),
        dictionary_->getText("GLOBAL1"));
    EXPECT_EQ(string("this is message global two"),
        dictionary_->getText("GLOBAL2"));
    EXPECT_EQ(2, dictionary_->size());

    // ... and ensure no messages were not added
    vector<string> not_added = reader_.getNotAdded();
    EXPECT_EQ(0, not_added.size());
}

TEST_F(MessageReaderTest, ValidMessageReplace) {

    dictionary_->add("GLOBAL1", "original global1 message");
    dictionary_->add("GLOBAL2", "original global2 message");

    // Replace a couple of valid messages
    reader_.processLine("% GLOBAL1\t\tthis is message global one\n",
        MessageReader::REPLACE);
    reader_.processLine("% GLOBAL2 this is message global two",
        MessageReader::REPLACE);

    // ... and check them
    EXPECT_EQ(string("this is message global one"),
        dictionary_->getText("GLOBAL1"));
    EXPECT_EQ(string("this is message global two"),
        dictionary_->getText("GLOBAL2"));
    EXPECT_EQ(2, dictionary_->size());

    // ... and ensure no messages were not added
    vector<string> not_added = reader_.getNotAdded();
    EXPECT_EQ(0, not_added.size());
}

// Do checks on overflows, although this essentially duplicates the checks
// in MessageDictionary.

TEST_F(MessageReaderTest, Overflows) {

    // Add a couple of valid messages
    reader_.processLine("% GLOBAL1\t\tthis is message global one\n");
    reader_.processLine("% GLOBAL2 this is message global two");

    // Add a duplicate in ADD mode.
    reader_.processLine("% GLOBAL1\t\tthis is a replacement for global one");

    // Replace a non-existent one in REPLACE mode
    reader_.processLine("% LOCAL\t\tthis is a new message",
        MessageReader::REPLACE);

    // Check what is in the dictionary.
    EXPECT_EQ(string("this is message global one"),
        dictionary_->getText("GLOBAL1"));
    EXPECT_EQ(string("this is message global two"),
        dictionary_->getText("GLOBAL2"));
    EXPECT_EQ(2, dictionary_->size());

    // ... and ensure no overflows
    vector<string> not_added = reader_.getNotAdded();
    ASSERT_EQ(2, not_added.size());

    sort(not_added.begin(), not_added.end());
    EXPECT_EQ(string("GLOBAL1"), not_added[0]);
    EXPECT_EQ(string("LOCAL"), not_added[1]);
}