377 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			377 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
 * Copyright (c) 2016 MariaDB Corporation Ab
 | 
						|
 *
 | 
						|
 * Use of this software is governed by the Business Source License included
 | 
						|
 * in the LICENSE.TXT file and at www.mariadb.com/bsl11.
 | 
						|
 *
 | 
						|
 * Change Date: 2023-12-18
 | 
						|
 *
 | 
						|
 * On the date above, in accordance with the Business Source License, use
 | 
						|
 * of this software will be governed by version 2 or later of the General
 | 
						|
 * Public License.
 | 
						|
 */
 | 
						|
 | 
						|
// To ensure that ss_info_assert asserts also when builing in non-debug mode.
 | 
						|
#ifndef SS_DEBUG
 | 
						|
#define SS_DEBUG
 | 
						|
#endif
 | 
						|
 | 
						|
#include <iostream>
 | 
						|
#include <maxbase/log.hh>
 | 
						|
#include <maxscale/config2.hh>
 | 
						|
#include "../internal/server.hh"
 | 
						|
#include "test_utils.hh"
 | 
						|
 | 
						|
using namespace std;
 | 
						|
 | 
						|
inline ostream& operator<<(ostream& out, const std::chrono::seconds& x)
 | 
						|
{
 | 
						|
    out << x.count();
 | 
						|
    return out;
 | 
						|
}
 | 
						|
 | 
						|
inline ostream& operator<<(ostream& out, const std::chrono::milliseconds& x)
 | 
						|
{
 | 
						|
    out << x.count();
 | 
						|
    return out;
 | 
						|
}
 | 
						|
 | 
						|
config::Specification specification("test_module", config::Specification::FILTER);
 | 
						|
 | 
						|
config::ParamBool
 | 
						|
    param_bool(&specification,
 | 
						|
               "boolean_parameter",
 | 
						|
               "Specifies whether something is enabled.");
 | 
						|
 | 
						|
config::ParamCount
 | 
						|
    param_count(&specification,
 | 
						|
                "count_parameter",
 | 
						|
                "Specifies the cardinality of something.");
 | 
						|
 | 
						|
config::ParamDuration<std::chrono::seconds>
 | 
						|
param_duration_1(&specification,
 | 
						|
                 "duration_parameter_1",
 | 
						|
                 "Specifies the duration of something.",
 | 
						|
                 mxs::config::INTERPRET_AS_SECONDS);
 | 
						|
 | 
						|
config::ParamDuration<std::chrono::milliseconds>
 | 
						|
param_duration_2(&specification,
 | 
						|
                 "duration_parameter_2",
 | 
						|
                 "Specifies the duration of something.",
 | 
						|
                 mxs::config::INTERPRET_AS_MILLISECONDS);
 | 
						|
 | 
						|
enum Enum
 | 
						|
{
 | 
						|
    ENUM_ONE,
 | 
						|
    ENUM_TWO
 | 
						|
};
 | 
						|
 | 
						|
config::ParamEnum<Enum>
 | 
						|
param_enum(&specification,
 | 
						|
           "enum_parameter",
 | 
						|
           "Specifies a range of values.",
 | 
						|
{
 | 
						|
    {ENUM_ONE, "one"},
 | 
						|
    {ENUM_TWO, "two"}
 | 
						|
});
 | 
						|
 | 
						|
config::ParamInteger
 | 
						|
    param_integer(&specification,
 | 
						|
                  "integer_parameter",
 | 
						|
                  "Specifies a number.");
 | 
						|
 | 
						|
config::ParamPath
 | 
						|
    param_path(&specification,
 | 
						|
               "path_parameter",
 | 
						|
               "Specifies the path of something.",
 | 
						|
               config::ParamPath::F);
 | 
						|
 | 
						|
config::ParamServer
 | 
						|
param_server(&specification,
 | 
						|
             "server_parameter",
 | 
						|
             "Specifies a server.");
 | 
						|
 | 
						|
config::ParamSize
 | 
						|
    param_size(&specification,
 | 
						|
               "size_parameter",
 | 
						|
               "Specifies the size of something.");
 | 
						|
 | 
						|
config::ParamString
 | 
						|
    param_string(&specification,
 | 
						|
                 "string_parameter",
 | 
						|
                 "Specifies the name of something.");
 | 
						|
 | 
						|
template<class T>
 | 
						|
struct TestEntry
 | 
						|
{
 | 
						|
    const char* zText;
 | 
						|
    bool        valid;
 | 
						|
    T           value;
 | 
						|
};
 | 
						|
 | 
						|
#define elements_in_array(x) (sizeof(x) / sizeof(x[0]))
 | 
						|
 | 
						|
template<class T>
 | 
						|
int test(T& value, const TestEntry<typename T::value_type>* pEntries, int nEntries)
 | 
						|
{
 | 
						|
    const config::Param& param = value.parameter();
 | 
						|
 | 
						|
    cout << "Testing " << param.type() << " parameter " << param.name() << "." << endl;
 | 
						|
 | 
						|
    int nErrors = 0;
 | 
						|
 | 
						|
    for (int i = 0; i < nEntries; ++i)
 | 
						|
    {
 | 
						|
        const auto& entry = pEntries[i];
 | 
						|
 | 
						|
        std::string message;
 | 
						|
        bool validated = param.validate(entry.zText, &message);
 | 
						|
 | 
						|
        if (entry.valid && validated)
 | 
						|
        {
 | 
						|
            param.set(value, entry.zText);
 | 
						|
 | 
						|
            if (value != entry.value)
 | 
						|
            {
 | 
						|
                cout << value.to_string() << " != " << entry.value << endl;
 | 
						|
                ++nErrors;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else if (entry.valid && !validated)
 | 
						|
        {
 | 
						|
            cout << "Expected \"" << entry.zText << "\" to BE valid for " << param.type()
 | 
						|
                 << " parameter " << param.name() << ", but it was NOT validated: " << message << endl;
 | 
						|
            ++nErrors;
 | 
						|
        }
 | 
						|
        else if (!entry.valid && validated)
 | 
						|
        {
 | 
						|
            cout << "Expected \"" << entry.zText << "\" NOT to be valid for " << param.type()
 | 
						|
                 << " parameter " << param.name() << ", but it WAS validated." << endl;
 | 
						|
            ++nErrors;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    return nErrors;
 | 
						|
}
 | 
						|
 | 
						|
int test_bool(config::Bool& value)
 | 
						|
{
 | 
						|
    static const TestEntry<config::Bool::value_type> entries[] =
 | 
						|
    {
 | 
						|
        {"1",     true, true },
 | 
						|
        {"0",     true, false},
 | 
						|
        {"true",  true, true },
 | 
						|
        {"false", true, false},
 | 
						|
        {"on",    true, true },
 | 
						|
        {"off",   true, false},
 | 
						|
 | 
						|
        {"2",     false},
 | 
						|
        {"truth", false},
 | 
						|
        {"%&",    false},
 | 
						|
        {"-1",    false},
 | 
						|
    };
 | 
						|
 | 
						|
    return test(value, entries, elements_in_array(entries));
 | 
						|
}
 | 
						|
 | 
						|
int test_count(config::Count& value)
 | 
						|
{
 | 
						|
    static const TestEntry<config::Count::value_type> entries[] =
 | 
						|
    {
 | 
						|
        {"1",    true, 1   },
 | 
						|
        {"9999", true, 9999},
 | 
						|
        {"0",    true, 0   },
 | 
						|
 | 
						|
        {"0x45", false},
 | 
						|
        {"blah", false},
 | 
						|
        {"-1",   false},
 | 
						|
    };
 | 
						|
 | 
						|
    return test(value, entries, elements_in_array(entries));
 | 
						|
}
 | 
						|
 | 
						|
int test_duration(config::Duration<std::chrono::seconds>& value)
 | 
						|
{
 | 
						|
    static const TestEntry<config::Duration<std::chrono::seconds>::value_type> entries[] =
 | 
						|
    {
 | 
						|
        {"1",      true, std::chrono::seconds {1   }},
 | 
						|
        {"1ms",    true, std::chrono::seconds {0   }},
 | 
						|
        {"1s",     true, std::chrono::seconds {1   }},
 | 
						|
        {"1m",     true, std::chrono::seconds {60  }},
 | 
						|
        {"1h",     true, std::chrono::seconds {3600}},
 | 
						|
 | 
						|
        {"1x",     false},
 | 
						|
        {"a",      false},
 | 
						|
        {"-",      false},
 | 
						|
        {"second", false}
 | 
						|
    };
 | 
						|
 | 
						|
    return test(value, entries, elements_in_array(entries));
 | 
						|
}
 | 
						|
 | 
						|
int test_duration(config::Duration<std::chrono::milliseconds>& value)
 | 
						|
{
 | 
						|
    static const TestEntry<config::Duration<std::chrono::milliseconds>::value_type> entries[] =
 | 
						|
    {
 | 
						|
        {"1",      true, std::chrono::milliseconds {1      }},
 | 
						|
        {"1ms",    true, std::chrono::milliseconds {1      }},
 | 
						|
        {"1s",     true, std::chrono::milliseconds {1000   }},
 | 
						|
        {"1m",     true, std::chrono::milliseconds {60000  }},
 | 
						|
        {"1h",     true, std::chrono::milliseconds {3600000}},
 | 
						|
 | 
						|
        {"1x",     false},
 | 
						|
        {"a",      false},
 | 
						|
        {"-",      false},
 | 
						|
        {"second", false}
 | 
						|
    };
 | 
						|
 | 
						|
    return test(value, entries, elements_in_array(entries));
 | 
						|
}
 | 
						|
 | 
						|
int test_enum(config::Enum<Enum>& value)
 | 
						|
{
 | 
						|
    static const TestEntry<Enum> entries[] =
 | 
						|
    {
 | 
						|
        {"one",  true, ENUM_ONE},
 | 
						|
        {"two",  true, ENUM_TWO},
 | 
						|
 | 
						|
        {"blah", false},
 | 
						|
        {"1",    false},
 | 
						|
        {"ones", false}
 | 
						|
    };
 | 
						|
 | 
						|
    return test(value, entries, elements_in_array(entries));
 | 
						|
}
 | 
						|
 | 
						|
int test_integer(config::Integer& value)
 | 
						|
{
 | 
						|
    static const TestEntry<config::Integer::value_type> entries[] =
 | 
						|
    {
 | 
						|
        {"0",           true, 0          },
 | 
						|
        {"-1",          true, -1         },
 | 
						|
        {"1",           true, 1          },
 | 
						|
        {"-2147483648", true, -2147483648},
 | 
						|
        {"2147483647",  true, 2147483647 },
 | 
						|
 | 
						|
        {"-2147483649", false},
 | 
						|
        {"2147483648",  false},
 | 
						|
        {"0x10",        false},
 | 
						|
    };
 | 
						|
 | 
						|
    return test(value, entries, elements_in_array(entries));
 | 
						|
}
 | 
						|
 | 
						|
int test_path(config::Path& value)
 | 
						|
{
 | 
						|
    static char path[PATH_MAX];
 | 
						|
    static char* strpath = getcwd(path, sizeof(path));
 | 
						|
 | 
						|
    static const TestEntry<config::Path::value_type> entries[] =
 | 
						|
    {
 | 
						|
        {strpath,        true, strpath},
 | 
						|
        {"/tmp",         true, "/tmp" },
 | 
						|
 | 
						|
        {"non-existent", false}
 | 
						|
    };
 | 
						|
 | 
						|
    return test(value, entries, elements_in_array(entries));
 | 
						|
}
 | 
						|
 | 
						|
int test_server(config::Server& value)
 | 
						|
{
 | 
						|
    MXS_CONFIG_PARAMETER params1;
 | 
						|
    params1.set(CN_PROTOCOL, "mariadbbackend");
 | 
						|
    params1.set(CN_PERSISTMAXTIME, "0");
 | 
						|
    params1.set(CN_RANK, "primary");
 | 
						|
 | 
						|
    std::unique_ptr<Server> sServer1(Server::server_alloc("TheServer1", params1));
 | 
						|
    mxb_assert(sServer1.get());
 | 
						|
 | 
						|
    const TestEntry<config::Server::value_type> entries[] =
 | 
						|
    {
 | 
						|
        { "TheServer1", true, sServer1.get() },
 | 
						|
        { "TheServer0", false },
 | 
						|
    };
 | 
						|
 | 
						|
    return test(value, entries, elements_in_array(entries));
 | 
						|
}
 | 
						|
 | 
						|
int test_size(config::Size& value)
 | 
						|
{
 | 
						|
    static const TestEntry<config::Size::value_type> entries[] =
 | 
						|
    {
 | 
						|
        {"0",     true, 0  },
 | 
						|
        {"100",   true, 100},
 | 
						|
 | 
						|
        {"-100",  false},
 | 
						|
        {"0x100", false},
 | 
						|
    };
 | 
						|
 | 
						|
    return test(value, entries, elements_in_array(entries));
 | 
						|
}
 | 
						|
 | 
						|
int test_string(config::String& value)
 | 
						|
{
 | 
						|
    static const TestEntry<config::String::value_type> entries[] =
 | 
						|
    {
 | 
						|
        {"blah",     true, "blah"  },
 | 
						|
        {"\"blah\"", true, "blah"  },
 | 
						|
        {"'blah'",   true, "blah"  },
 | 
						|
        {"123",      true, "123"   },
 | 
						|
        {"`blah`",   true, "`blah`"},
 | 
						|
 | 
						|
        {"'blah\"",  false}
 | 
						|
    };
 | 
						|
 | 
						|
    return test(value, entries, elements_in_array(entries));
 | 
						|
}
 | 
						|
 | 
						|
int main()
 | 
						|
{
 | 
						|
    init_test_env();
 | 
						|
 | 
						|
    for_each(specification.cbegin(), specification.cend(), [](const config::Specification::value_type& p) {
 | 
						|
                 cout << p.second->documentation() << endl;
 | 
						|
             });
 | 
						|
 | 
						|
    cout << endl;
 | 
						|
 | 
						|
    specification.document(cout);
 | 
						|
 | 
						|
    config::Configuration configuration("test", &specification);
 | 
						|
 | 
						|
    int nErrors = 0;
 | 
						|
 | 
						|
    config::Bool value_bool(&configuration, ¶m_bool);
 | 
						|
    nErrors += test_bool(value_bool);
 | 
						|
 | 
						|
    config::Count value_count(&configuration, ¶m_count);
 | 
						|
    nErrors += test_count(value_count);
 | 
						|
 | 
						|
    config::Duration<std::chrono::seconds> value_duration_1(&configuration, ¶m_duration_1);
 | 
						|
    nErrors += test_duration(value_duration_1);
 | 
						|
 | 
						|
    config::Duration<std::chrono::milliseconds> value_duration_2(&configuration, ¶m_duration_2);
 | 
						|
    nErrors += test_duration(value_duration_2);
 | 
						|
 | 
						|
    config::Enum<Enum> value_enum(&configuration, ¶m_enum);
 | 
						|
    nErrors += test_enum(value_enum);
 | 
						|
 | 
						|
    config::Integer value_integer(&configuration, ¶m_integer);
 | 
						|
    nErrors += test_integer(value_integer);
 | 
						|
 | 
						|
    config::Path value_path(&configuration, ¶m_path);
 | 
						|
    nErrors += test_path(value_path);
 | 
						|
 | 
						|
    config::Server value_server(&configuration, ¶m_server);
 | 
						|
    nErrors += test_server(value_server);
 | 
						|
 | 
						|
    config::Size value_size(&configuration, ¶m_size);
 | 
						|
    nErrors += test_size(value_size);
 | 
						|
 | 
						|
    config::String value_string(&configuration, ¶m_string);
 | 
						|
    nErrors += test_string(value_string);
 | 
						|
 | 
						|
    return nErrors ? EXIT_FAILURE : EXIT_SUCCESS;
 | 
						|
}
 |