logic_check_test.cc 11.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
//
// 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.

15
#include "creators.h"
16
#include <acl/logic_check.h>
17
#include <typeinfo>
18
#include <boost/shared_ptr.hpp> // for static_pointer_cast
19

20 21
using namespace std;
using namespace boost;
22
using namespace isc::acl;
23 24
using namespace isc::acl::tests;
using isc::data::Element;
25 26 27 28 29 30 31 32 33 34 35 36 37 38

namespace {

// Test the defs in AnyOfSpec
TEST(LogicOperators, AnyOfSpec) {
    EXPECT_FALSE(AnyOfSpec::start());
    EXPECT_FALSE(AnyOfSpec::terminate(false));
    EXPECT_TRUE(AnyOfSpec::terminate(true));
}

// Test the defs in AllOfSpec
TEST(LogicOperators, AllOfSpec) {
    EXPECT_TRUE(AllOfSpec::start());
    EXPECT_TRUE(AllOfSpec::terminate(false));
39
    EXPECT_FALSE(AllOfSpec::terminate(true));
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
}

// Generic test of one check
template<typename Mode>
void
testCheck(bool emptyResult) {
    // It can be created
    LogicOperator<Mode, Log> oper;
    // It is empty by default
    EXPECT_EQ(0, oper.getSubexpressions().size());
    // And returns true, as all 0 of the subexpressions return true
    Log log;
    EXPECT_EQ(emptyResult, oper.matches(log));
    log.checkFirst(0);
    // Fill it with some subexpressions
55
    typedef boost::shared_ptr<ConstCheck> CheckPtr;
56 57 58 59 60 61 62 63
    oper.addSubexpression(CheckPtr(new ConstCheck(emptyResult, 0)));
    oper.addSubexpression(CheckPtr(new ConstCheck(emptyResult, 1)));
    // Check what happens when only the default-valued are there
    EXPECT_EQ(2, oper.getSubexpressions().size());
    EXPECT_EQ(emptyResult, oper.matches(log));
    log.checkFirst(2);
    oper.addSubexpression(CheckPtr(new ConstCheck(!emptyResult, 2)));
    oper.addSubexpression(CheckPtr(new ConstCheck(!emptyResult, 3)));
64 65 66 67 68
    // They are listed there
    EXPECT_EQ(4, oper.getSubexpressions().size());
    // Now, the last one kills it, but the first ones will run, the fourth
    // won't
    EXPECT_EQ(!emptyResult, oper.matches(log));
69
    log.checkFirst(3);
70 71 72 73 74 75 76 77 78 79
}

TEST(LogicOperators, AllOf) {
    testCheck<AllOfSpec>(true);
}

TEST(LogicOperators, AnyOf) {
    testCheck<AnyOfSpec>(false);
}

80 81 82
// Fixture for the tests of the creators
class LogicCreatorTest : public ::testing::Test {
private:
83
    typedef boost::shared_ptr<Loader<Log>::CheckCreator> CreatorPtr;
84 85 86 87 88 89 90 91 92 93 94 95
public:
    // Register some creators, both tested ones and some auxiliary ones for
    // help
    LogicCreatorTest():
        loader_(REJECT)
    {
        loader_.registerCreator(CreatorPtr(new
            LogicCreator<AnyOfSpec, Log>("ANY")));
        loader_.registerCreator(CreatorPtr(new
            LogicCreator<AllOfSpec, Log>("ALL")));
        loader_.registerCreator(CreatorPtr(new ThrowCreator));
        loader_.registerCreator(CreatorPtr(new LogCreator));
96
        loader_.registerCreator(CreatorPtr(new NotCreator<Log>("NOT")));
97 98 99 100 101 102 103 104
    }
    // To mark which parts of the check did run
    Log log_;
    // The loader
    Loader<Log> loader_;
    // Some convenience shortcut names
    typedef LogicOperator<AnyOfSpec, Log> AnyOf;
    typedef LogicOperator<AllOfSpec, Log> AllOf;
105 106
    typedef boost::shared_ptr<AnyOf> AnyOfPtr;
    typedef boost::shared_ptr<AllOf> AllOfPtr;
107 108
    // Loads the JSON as a check and tries to convert it to the given check
    // subclass
109 110
    template<typename Result> boost::shared_ptr<Result> load(const string& JSON) {
        boost::shared_ptr<Check<Log> > result;
111
        EXPECT_NO_THROW(result = loader_.loadCheck(Element::fromJSON(JSON)));
112 113 114 115 116 117 118 119 120 121 122 123 124
        /*
         * Optimally, we would use a dynamic_pointer_cast here to both
         * convert the pointer and to check the type is correct. However,
         * clang++ seems to be confused by templates and creates two typeids
         * for the same templated type (even with the same parameters),
         * therfore considering the types different, even if they are the same.
         * This leads to false alarm in the test. Luckily, it generates the
         * same name for both typeids, so we use them instead (which is enough
         * to test the correct type of Check is returned). Then we can safely
         * cast statically, as we don't use any kind of nasty things like
         * multiple inheritance.
         */
        EXPECT_STREQ(typeid(Result).name(), typeid(*result.get()).name());
125
        boost::shared_ptr<Result>
126
            resultConverted(static_pointer_cast<Result>(result));
127
        EXPECT_NE(boost::shared_ptr<Result>(), resultConverted);
128 129 130 131 132 133 134 135 136 137 138 139 140 141
        return (resultConverted);
    }
};

// Test it can load empty ones
TEST_F(LogicCreatorTest, empty) {
    AnyOfPtr emptyAny(load<AnyOf>("{\"ANY\": []}"));
    EXPECT_EQ(0, emptyAny->getSubexpressions().size());
    AllOfPtr emptyAll(load<AllOf>("{\"ALL\": []}"));
    EXPECT_EQ(0, emptyAll->getSubexpressions().size());
}

// Test it rejects invalid inputs (not a list as a parameter)
TEST_F(LogicCreatorTest, invalid) {
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
    EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"ANY\": null}")),
                 LoaderError);
    EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"ANY\": {}}")),
                 LoaderError);
    EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"ANY\": true}")),
                 LoaderError);
    EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"ANY\": 42}")),
                 LoaderError);
    EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"ANY\": \"hello\"}")),
                 LoaderError);
    EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"ALL\": null}")),
                 LoaderError);
    EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"ALL\": {}}")),
                 LoaderError);
    EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"ALL\": true}")),
                 LoaderError);
    EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"ALL\": 42}")),
                 LoaderError);
    EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"ALL\": \"hello\"}")),
                 LoaderError);
162 163 164 165
}

// Exceptions from subexpression creation isn't caught
TEST_F(LogicCreatorTest, propagate) {
166 167
    EXPECT_THROW(loader_.loadCheck(
                     Element::fromJSON("{\"ANY\": [{\"throw\": null}]}")),
168
                 TestCreatorError);
169 170
    EXPECT_THROW(loader_.loadCheck(
                     Element::fromJSON("{\"ALL\": [{\"throw\": null}]}")),
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
                 TestCreatorError);
}

// We can create more complex ANY check and run it correctly
TEST_F(LogicCreatorTest, anyRun) {
    AnyOfPtr any(load<AnyOf>("{\"ANY\": ["
                             "    {\"logcheck\": [0, false]},"
                             "    {\"logcheck\": [1, true]},"
                             "    {\"logcheck\": [2, true]}"
                             "]}"));
    EXPECT_EQ(3, any->getSubexpressions().size());
    EXPECT_TRUE(any->matches(log_));
    log_.checkFirst(2);
}

// We can create more complex ALL check and run it correctly
TEST_F(LogicCreatorTest, allRun) {
    AllOfPtr any(load<AllOf>("{\"ALL\": ["
                             "    {\"logcheck\": [0, true]},"
                             "    {\"logcheck\": [1, false]},"
                             "    {\"logcheck\": [2, false]}"
                             "]}"));
    EXPECT_EQ(3, any->getSubexpressions().size());
    EXPECT_FALSE(any->matches(log_));
    log_.checkFirst(2);
}

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
// Or is able to return false
TEST_F(LogicCreatorTest, anyFalse) {
    AnyOfPtr any(load<AnyOf>("{\"ANY\": ["
                             "    {\"logcheck\": [0, false]},"
                             "    {\"logcheck\": [1, false]},"
                             "    {\"logcheck\": [2, false]}"
                             "]}"));
    EXPECT_EQ(3, any->getSubexpressions().size());
    EXPECT_FALSE(any->matches(log_));
    log_.checkFirst(3);
}

// And is able to return true
TEST_F(LogicCreatorTest, andTrue) {
    AllOfPtr all(load<AllOf>("{\"ALL\": ["
                             "    {\"logcheck\": [0, true]},"
                             "    {\"logcheck\": [1, true]},"
                             "    {\"logcheck\": [2, true]}"
                             "]}"));
    EXPECT_EQ(3, all->getSubexpressions().size());
    EXPECT_TRUE(all->matches(log_));
    log_.checkFirst(3);
}

// We can nest them together
TEST_F(LogicCreatorTest, nested) {
    AllOfPtr all(load<AllOf>("{\"ALL\": ["
                             "    {\"ANY\": ["
                             "        {\"logcheck\": [0, true]},"
                             "        {\"logcheck\": [2, true]},"
                             "    ]},"
                             "    {\"logcheck\": [1, false]}"
                             "]}"));
    EXPECT_EQ(2, all->getSubexpressions().size());
232 233 234 235 236 237
    /*
     * This has the same problem as load function above, and we use the
     * same solution here.
     */
    ASSERT_STREQ(typeid(LogicOperator<AnyOfSpec, Log>).name(),
                 typeid(*all->getSubexpressions()[0]).name());
238
    const LogicOperator<AnyOfSpec, Log>*
239
        any(static_cast<const LogicOperator<AnyOfSpec, Log>*>
240 241 242 243 244 245
            (all->getSubexpressions()[0]));
    EXPECT_EQ(2, any->getSubexpressions().size());
    EXPECT_FALSE(all->matches(log_));
    log_.checkFirst(2);
}

246
void notTest(bool value) {
247 248
    NotOperator<Log> notOp(boost::shared_ptr<Check<Log> >(
                                new ConstCheck(value, 0)));
249 250 251 252 253 254 255 256 257 258 259 260 261 262
    Log log;
    // It returns negated value
    EXPECT_EQ(!value, notOp.matches(log));
    // And runs the only one thing there
    log.checkFirst(1);
    // Check the getSubexpressions does sane things
    ASSERT_EQ(1, notOp.getSubexpressions().size());
    EXPECT_EQ(value, notOp.getSubexpressions()[0]->matches(log));
}

TEST(Not, trueValue) {
    notTest(true);
}

263 264
TEST(Not, falseValue) {
    notTest(false);
265 266
}

267 268 269 270 271 272 273 274 275 276 277 278 279
TEST_F(LogicCreatorTest, notInvalid) {
    EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"NOT\": null}")),
                 LoaderError);
    EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"NOT\": \"hello\"}")),
                 LoaderError);
    EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"NOT\": true}")),
                 LoaderError);
    EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"NOT\": 42}")),
                 LoaderError);
    EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"NOT\": []}")),
                 LoaderError);
    EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"NOT\": [{"
                                                     "\"logcheck\": [0, true]"
280
                                                     "}]}")),
281 282 283 284
                 LoaderError);
}

TEST_F(LogicCreatorTest, notValid) {
285
    boost::shared_ptr<NotOperator<Log> > notOp(load<NotOperator<Log> >("{\"NOT\":"
286 287
                                                                "  {\"logcheck\":"
                                                                "     [0, true]}}"));
288 289 290 291
    EXPECT_FALSE(notOp->matches(log_));
    log_.checkFirst(1);
}

292
}