module_spec_unittests.cc 15 KB
Newer Older
Naoki Kambe's avatar
Naoki Kambe committed
1
// Copyright (C) 2009, 2011  Internet Systems Consortium, Inc. ("ISC")
Jelte Jansen's avatar
Jelte Jansen committed
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.

#include <gtest/gtest.h>

17
#include <config/module_spec.h>
Jelte Jansen's avatar
Jelte Jansen committed
18

19 20
#include <fstream>

Naoki Kambe's avatar
Naoki Kambe committed
21 22
#include <boost/foreach.hpp>

23
#include <config/tests/data_def_unittests_config.h>
Jelte Jansen's avatar
Jelte Jansen committed
24 25

using namespace isc::data;
26
using namespace isc::config;
Jelte Jansen's avatar
Jelte Jansen committed
27

28
std::string specfile(const std::string& name) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
29
    return (std::string(TEST_DATA_PATH) + "/" + name);
Jelte Jansen's avatar
Jelte Jansen committed
30 31 32
}

void
33
moduleSpecError(const std::string& file,
Jelte Jansen's avatar
Jelte Jansen committed
34 35 36 37
               const std::string& error1,
               const std::string& error2 = "",
               const std::string& error3 = "")
{
38
    EXPECT_THROW(moduleSpecFromFile(specfile(file)), ModuleSpecError);
Jelte Jansen's avatar
Jelte Jansen committed
39
    try {
40
        ModuleSpec dd = moduleSpecFromFile(specfile(file));
41
    } catch (const ModuleSpecError& dde) {
Jelte Jansen's avatar
Jelte Jansen committed
42 43 44 45 46
        std::string ddew = dde.what();
        EXPECT_EQ(error1 + error2 + error3, ddew);
    }
}

47
TEST(ModuleSpec, ReadingSpecfiles) {
Jelte Jansen's avatar
Jelte Jansen committed
48 49
    // Tests whether we can open specfiles and if we get the
    // right parse errors
50
    ModuleSpec dd = moduleSpecFromFile(specfile("spec1.spec"));
51 52
    EXPECT_EQ(dd.getFullSpec()->get("module_name")
                              ->stringValue(), "Spec1");
53
    dd = moduleSpecFromFile(specfile("spec2.spec"));
54
    EXPECT_EQ(dd.getFullSpec()->get("config_data")->size(), 6);
55
    moduleSpecError("doesnotexist",
Jelte Jansen's avatar
Jelte Jansen committed
56 57 58
                   "Error opening ",
                   specfile("doesnotexist"),
                   ": No such file or directory");
59

60
    dd = moduleSpecFromFile(specfile("spec2.spec"));
61
    EXPECT_EQ("[ { \"command_args\": [ { \"item_default\": \"\", \"item_name\": \"message\", \"item_optional\": false, \"item_type\": \"string\" } ], \"command_description\": \"Print the given message to stdout\", \"command_name\": \"print_message\" }, { \"command_args\": [  ], \"command_description\": \"Shut down BIND 10\", \"command_name\": \"shutdown\" } ]", dd.getCommandsSpec()->str());
Naoki Kambe's avatar
Naoki Kambe committed
62
    EXPECT_EQ("[ { \"item_default\": \"1970-01-01T00:00:00Z\", \"item_description\": \"A dummy date time\", \"item_format\": \"date-time\", \"item_name\": \"dummy_time\", \"item_optional\": false, \"item_title\": \"Dummy Time\", \"item_type\": \"string\" } ]", dd.getStatisticsSpec()->str());
63
    EXPECT_EQ("Spec2", dd.getModuleName());
Jelte Jansen's avatar
Jelte Jansen committed
64 65 66 67 68 69
    EXPECT_EQ("", dd.getModuleDescription());

    dd = moduleSpecFromFile(specfile("spec25.spec"));
    EXPECT_EQ("Spec25", dd.getModuleName());
    EXPECT_EQ("Just an empty module", dd.getModuleDescription());
    EXPECT_THROW(moduleSpecFromFile(specfile("spec26.spec")), ModuleSpecError);
Naoki Kambe's avatar
Naoki Kambe committed
70 71 72 73 74
    EXPECT_THROW(moduleSpecFromFile(specfile("spec34.spec")), ModuleSpecError);
    EXPECT_THROW(moduleSpecFromFile(specfile("spec35.spec")), ModuleSpecError);
    EXPECT_THROW(moduleSpecFromFile(specfile("spec36.spec")), ModuleSpecError);
    EXPECT_THROW(moduleSpecFromFile(specfile("spec37.spec")), ModuleSpecError);
    EXPECT_THROW(moduleSpecFromFile(specfile("spec38.spec")), ModuleSpecError);
75

76 77
    std::ifstream file;
    file.open(specfile("spec1.spec").c_str());
78
    dd = moduleSpecFromFile(file);
79 80
    EXPECT_EQ(dd.getFullSpec()->get("module_name")
                              ->stringValue(), "Spec1");
Jelte Jansen's avatar
Jelte Jansen committed
81
    EXPECT_TRUE(isNull(dd.getCommandsSpec()));
Naoki Kambe's avatar
Naoki Kambe committed
82
    EXPECT_TRUE(isNull(dd.getStatisticsSpec()));
83 84 85 86 87

    std::ifstream file2;
    file2.open(specfile("spec8.spec").c_str());
    EXPECT_THROW(moduleSpecFromFile(file2), ModuleSpecError);

88 89
}

90
TEST(ModuleSpec, SpecfileItems) {
91
    moduleSpecError("spec3.spec",
92
                   "item_name missing in { \"item_default\": 1, \"item_optional\": false, \"item_type\": \"integer\" }");
93
    moduleSpecError("spec4.spec",
94
                   "item_type missing in { \"item_default\": 1, \"item_name\": \"item1\", \"item_optional\": false }");
95
    moduleSpecError("spec5.spec",
96
                   "item_optional missing in { \"item_default\": 1, \"item_name\": \"item1\", \"item_type\": \"integer\" }");
97
    moduleSpecError("spec6.spec",
98
                   "item_default missing in { \"item_name\": \"item1\", \"item_optional\": false, \"item_type\": \"integer\" }");
99
    moduleSpecError("spec9.spec",
100
                   "item_default not of type integer");
101
    moduleSpecError("spec10.spec",
102
                   "item_default not of type real");
103
    moduleSpecError("spec11.spec",
104
                   "item_default not of type boolean");
105
    moduleSpecError("spec12.spec",
106
                   "item_default not of type string");
107
    moduleSpecError("spec13.spec",
108
                   "item_default not of type list");
109
    moduleSpecError("spec14.spec",
110
                   "item_default not of type map");
111
    moduleSpecError("spec15.spec",
112 113 114
                   "badname is not a valid type name");
}

115
TEST(ModuleSpec, SpecfileConfigData) {
116
    moduleSpecError("spec7.spec",
117
                   "module_name missing in {  }");
118
    moduleSpecError("spec8.spec",
119
                   "No module_spec in specification");
120
    moduleSpecError("spec16.spec",
121
                   "config_data is not a list of elements");
122
    moduleSpecError("spec21.spec",
123 124 125
                   "commands is not a list of elements");
}

Naoki Kambe's avatar
Naoki Kambe committed
126 127 128 129 130 131
TEST(ModuleSpec, SpecfileStatistics) {
    moduleSpecError("spec36.spec", "item_default not valid type of item_format");
    moduleSpecError("spec37.spec", "statistics is not a list of elements");
    moduleSpecError("spec38.spec", "item_default not valid type of item_format");
}

132
TEST(ModuleSpec, SpecfileCommands) {
133
    moduleSpecError("spec17.spec",
134
                   "command_name missing in { \"command_args\": [ { \"item_default\": \"\", \"item_name\": \"message\", \"item_optional\": false, \"item_type\": \"string\" } ], \"command_description\": \"Print the given message to stdout\" }");
135
    moduleSpecError("spec18.spec",
136
                   "command_args missing in { \"command_description\": \"Print the given message to stdout\", \"command_name\": \"print_message\" }");
137
    moduleSpecError("spec19.spec",
138
                   "command_args not of type list");
139
    moduleSpecError("spec20.spec",
140 141 142 143
                   "somethingbad is not a valid type name");
}

bool
144
dataTest(const ModuleSpec& dd, const std::string& data_file_name) {
145 146 147
    std::ifstream data_file;

    data_file.open(specfile(data_file_name).c_str());
148
    ConstElementPtr data = Element::fromJSON(data_file, data_file_name);
149 150
    data_file.close();

151
    return (dd.validateConfig(data));
152 153
}

Naoki Kambe's avatar
Naoki Kambe committed
154 155 156 157 158 159 160 161 162 163 164
bool
statisticsTest(const ModuleSpec& dd, const std::string& data_file_name) {
    std::ifstream data_file;

    data_file.open(specfile(data_file_name).c_str());
    ConstElementPtr data = Element::fromJSON(data_file, data_file_name);
    data_file.close();

    return (dd.validateStatistics(data));
}

165
bool
166
dataTestWithErrors(const ModuleSpec& dd, const std::string& data_file_name,
167
                      ElementPtr errors)
168 169 170 171
{
    std::ifstream data_file;

    data_file.open(specfile(data_file_name).c_str());
172
    ConstElementPtr data = Element::fromJSON(data_file, data_file_name);
173 174
    data_file.close();

175
    return (dd.validateConfig(data, true, errors));
176 177
}

Naoki Kambe's avatar
Naoki Kambe committed
178 179 180 181 182 183 184 185 186 187 188 189 190
bool
statisticsTestWithErrors(const ModuleSpec& dd, const std::string& data_file_name,
                      ElementPtr errors)
{
    std::ifstream data_file;

    data_file.open(specfile(data_file_name).c_str());
    ConstElementPtr data = Element::fromJSON(data_file, data_file_name);
    data_file.close();

    return (dd.validateStatistics(data, true, errors));
}

191
TEST(ModuleSpec, DataValidation) {
192
    ModuleSpec dd = moduleSpecFromFile(specfile("spec22.spec"));
193

194 195 196 197 198 199 200 201 202
    EXPECT_TRUE(dataTest(dd, "data22_1.data"));
    EXPECT_FALSE(dataTest(dd, "data22_2.data"));
    EXPECT_FALSE(dataTest(dd, "data22_3.data"));
    EXPECT_FALSE(dataTest(dd, "data22_4.data"));
    EXPECT_FALSE(dataTest(dd, "data22_5.data"));
    EXPECT_TRUE(dataTest(dd, "data22_6.data"));
    EXPECT_TRUE(dataTest(dd, "data22_7.data"));
    EXPECT_FALSE(dataTest(dd, "data22_8.data"));
    EXPECT_FALSE(dataTest(dd, "data22_9.data"));
203

204 205 206 207
    // Test if "version" is allowed in config data
    // (same data as 22_7, but added "version")
    EXPECT_TRUE(dataTest(dd, "data22_10.data"));

208
    ElementPtr errors = Element::createList();
209
    EXPECT_FALSE(dataTestWithErrors(dd, "data22_8.data", errors));
210
    EXPECT_EQ("[ \"Type mismatch\" ]", errors->str());
211 212

    errors = Element::createList();
213
    EXPECT_FALSE(dataTestWithErrors(dd, "data22_9.data", errors));
214
    EXPECT_EQ("[ \"Unknown item value_does_not_exist\" ]", errors->str());
Jelte Jansen's avatar
Jelte Jansen committed
215
}
216

Naoki Kambe's avatar
Naoki Kambe committed
217 218 219 220 221 222 223 224 225 226 227
TEST(ModuleSpec, StatisticsValidation) {
    ModuleSpec dd = moduleSpecFromFile(specfile("spec33.spec"));

    EXPECT_TRUE(statisticsTest(dd, "data33_1.data"));
    EXPECT_FALSE(statisticsTest(dd, "data33_2.data"));

    ElementPtr errors = Element::createList();
    EXPECT_FALSE(statisticsTestWithErrors(dd, "data33_2.data", errors));
    EXPECT_EQ("[ \"Format mismatch\", \"Format mismatch\", \"Format mismatch\" ]", errors->str());
}

228 229 230 231 232
TEST(ModuleSpec, CommandValidation) {
    ModuleSpec dd = moduleSpecFromFile(specfile("spec2.spec"));
    ConstElementPtr arg = Element::fromJSON("{}");
    ElementPtr errors = Element::createList();

233
    EXPECT_TRUE(dd.validateCommand("shutdown", arg, errors));
234 235 236
    EXPECT_EQ(errors->size(), 0);

    errors = Element::createList();
237
    EXPECT_FALSE(dd.validateCommand("unknowncommand", arg, errors));
238 239 240 241
    EXPECT_EQ(errors->size(), 1);
    EXPECT_EQ(errors->get(0)->stringValue(), "Unknown command unknowncommand");

    errors = Element::createList();
242
    EXPECT_FALSE(dd.validateCommand("print_message", arg, errors));
243 244 245 246 247
    EXPECT_EQ(errors->size(), 1);
    EXPECT_EQ(errors->get(0)->stringValue(), "Non-optional value missing");

    errors = Element::createList();
    arg = Element::fromJSON("{ \"message\": \"Hello\" }");
248
    EXPECT_TRUE(dd.validateCommand("print_message", arg, errors));
249 250 251 252
    EXPECT_EQ(errors->size(), 0);

    errors = Element::createList();
    arg = Element::fromJSON("{ \"message\": \"Hello\", \"unknown_second_arg\": 1 }");
253
    EXPECT_FALSE(dd.validateCommand("print_message", arg, errors));
254 255 256 257 258
    EXPECT_EQ(errors->size(), 1);
    EXPECT_EQ(errors->get(0)->stringValue(), "Unknown item unknown_second_arg");

    errors = Element::createList();
    arg = Element::fromJSON("{ \"message\": 1 }");
259
    EXPECT_FALSE(dd.validateCommand("print_message", arg, errors));
260 261 262 263
    EXPECT_EQ(errors->size(), 1);
    EXPECT_EQ(errors->get(0)->stringValue(), "Type mismatch");

}
264

265
TEST(ModuleSpec, NamedSetValidation) {
266 267 268 269 270 271 272
    ModuleSpec dd = moduleSpecFromFile(specfile("spec32.spec"));

    ElementPtr errors = Element::createList();
    EXPECT_TRUE(dataTestWithErrors(dd, "data32_1.data", errors));
    EXPECT_FALSE(dataTest(dd, "data32_2.data"));
    EXPECT_FALSE(dataTest(dd, "data32_3.data"));
}
Naoki Kambe's avatar
Naoki Kambe committed
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289

TEST(ModuleSpec, CheckFormat) {

    const std::string json_begin = "{ \"module_spec\": { \"module_name\": \"Foo\", \"statistics\": [ { \"item_name\": \"dummy_time\", \"item_type\": \"string\", \"item_optional\": true, \"item_title\": \"Dummy Time\", \"item_description\": \"A dummy date time\"";
    const std::string json_end = " } ] } }";
    std::string item_default;
    std::string item_format;
    std::vector<std::string> specs;
    ConstElementPtr el;

    specs.clear();
    item_default = "\"item_default\": \"2011-05-27T19:42:57Z\",";
    item_format  = "\"item_format\": \"date-time\"";
    specs.push_back("," + item_default + item_format);
    item_default = "\"item_default\": \"2011-05-27\",";
    item_format  = "\"item_format\": \"date\"";
    specs.push_back("," + item_default + item_format);
290
    item_default = "\"item_default\": \"19:42:57\",";
Naoki Kambe's avatar
Naoki Kambe committed
291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365
    item_format  = "\"item_format\": \"time\"";
    specs.push_back("," + item_default + item_format);

    item_format  = "\"item_format\": \"date-time\"";
    specs.push_back("," + item_format);
    item_default = "";
    item_format  = "\"item_format\": \"date\"";
    specs.push_back("," + item_format);
    item_default = "";
    item_format  = "\"item_format\": \"time\"";
    specs.push_back("," + item_format);

    item_default = "\"item_default\": \"a\"";
    specs.push_back("," + item_default);
    item_default = "\"item_default\": \"b\"";
    specs.push_back("," + item_default);
    item_default = "\"item_default\": \"c\"";
    specs.push_back("," + item_default);

    item_format  = "\"item_format\": \"dummy\"";
    specs.push_back("," + item_format);

    specs.push_back("");

    BOOST_FOREACH(std::string s, specs) {
        el = Element::fromJSON(json_begin + s + json_end)->get("module_spec");
        EXPECT_NO_THROW(ModuleSpec(el, true));
    }

    specs.clear();
    item_default = "\"item_default\": \"2011-05-27T19:42:57Z\",";
    item_format  = "\"item_format\": \"dummy\"";
    specs.push_back("," + item_default + item_format);
    item_default = "\"item_default\": \"2011-05-27\",";
    item_format  = "\"item_format\": \"dummy\"";
    specs.push_back("," + item_default + item_format);
    item_default = "\"item_default\": \"19:42:57Z\",";
    item_format  = "\"item_format\": \"dummy\"";
    specs.push_back("," + item_default + item_format);

    item_default = "\"item_default\": \"2011-13-99T99:99:99Z\",";
    item_format  = "\"item_format\": \"date-time\"";
    specs.push_back("," + item_default + item_format);
    item_default = "\"item_default\": \"2011-13-99\",";
    item_format  = "\"item_format\": \"date\"";
    specs.push_back("," + item_default + item_format);
    item_default = "\"item_default\": \"99:99:99Z\",";
    item_format  = "\"item_format\": \"time\"";
    specs.push_back("," + item_default + item_format);

    item_default = "\"item_default\": \"1\",";
    item_format  = "\"item_format\": \"date-time\"";
    specs.push_back("," + item_default + item_format);
    item_default = "\"item_default\": \"1\",";
    item_format  = "\"item_format\": \"date\"";
    specs.push_back("," + item_default + item_format);
    item_default = "\"item_default\": \"1\",";
    item_format  = "\"item_format\": \"time\"";
    specs.push_back("," + item_default + item_format);

    item_default = "\"item_default\": \"\",";
    item_format  = "\"item_format\": \"date-time\"";
    specs.push_back("," + item_default + item_format);
    item_default = "\"item_default\": \"\",";
    item_format  = "\"item_format\": \"date\"";
    specs.push_back("," + item_default + item_format);
    item_default = "\"item_default\": \"\",";
    item_format  = "\"item_format\": \"time\"";
    specs.push_back("," + item_default + item_format);

    BOOST_FOREACH(std::string s, specs) {
        el = Element::fromJSON(json_begin + s + json_end)->get("module_spec");
        EXPECT_THROW(ModuleSpec(el, true), ModuleSpecError);
    }
}