This is an automated email from the ASF dual-hosted git repository. bneradt pushed a commit to branch doc-lexicon in repository https://gitbox.apache.org/repos/asf/trafficserver-libswoc.git
commit 0472985c417790f4e3aa903d2b4396a0cedbeccb Author: Alan M. Carroll <[email protected]> AuthorDate: Tue May 19 12:28:06 2020 -0500 CMake: load on Win32. --- code/CMakeLists.txt | 4 +- example/CMakeLists.txt | 9 ++- unit_tests/CMakeLists.txt | 4 +- unit_tests/ex_Lexicon.cc | 200 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 213 insertions(+), 4 deletions(-) diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index 60a3916..2220c81 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -45,7 +45,9 @@ set(CC_FILES ) add_library(libswoc STATIC ${CC_FILES}) -target_compile_options(libswoc PRIVATE -Wall -Wextra -Werror -Wnon-virtual-dtor -Wpedantic) +if (CMAKE_COMPILER_IS_GNUCXX) + target_compile_options(libswoc PRIVATE -Wall -Wextra -Werror -Wnon-virtual-dtor -Wpedantic) +endif() # Not quite sure how this works, but I think it generates one of two paths depending on the context. # That is, the generator functions return non-empty stri ngs only in the corresponding context. diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index 4150aa5..c29ac05 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -5,8 +5,13 @@ set(CMAKE_CXX_STANDARD 17) add_executable(ex_netdb ex_netdb.cc) target_link_libraries(ex_netdb PUBLIC libswoc) set_target_properties(ex_netdb PROPERTIES CLANG_FORMAT_DIRS ${CMAKE_CURRENT_SOURCE_DIR}) -target_compile_options(ex_netdb PRIVATE -Wall -Wextra -Werror) + +if (CMAKE_COMPILER_IS_GNUCXX) + target_compile_options(ex_netdb PRIVATE -Wall -Wextra -Werror) +endif() add_executable(ex_netcompact ex_netcompact.cc) target_link_libraries(ex_netcompact PUBLIC libswoc) -target_compile_options(ex_netcompact PRIVATE -Wall -Wextra -Werror) +if (CMAKE_COMPILER_IS_GNUCXX) + target_compile_options(ex_netcompact PRIVATE -Wall -Wextra -Werror) +endif() diff --git a/unit_tests/CMakeLists.txt b/unit_tests/CMakeLists.txt index a4295be..56d6fed 100644 --- a/unit_tests/CMakeLists.txt +++ b/unit_tests/CMakeLists.txt @@ -29,5 +29,7 @@ add_executable(test_libswoc target_link_libraries(test_libswoc PUBLIC libswoc) set_target_properties(test_libswoc PROPERTIES CLANG_FORMAT_DIRS ${CMAKE_CURRENT_SOURCE_DIR}) -target_compile_options(test_libswoc PRIVATE -Wall -Wextra -Werror -Wno-unused-parameter -Wno-format-truncation -Wno-stringop-overflow -Wno-invalid-offsetof) +if (CMAKE_COMPILER_IS_GNUCXX) + target_compile_options(test_libswoc PRIVATE -Wall -Wextra -Werror -Wno-unused-parameter -Wno-format-truncation -Wno-stringop-overflow -Wno-invalid-offsetof) +endif() #add_definitions(-DVERBOSE_EXAMPLE_OUTPUT=1) diff --git a/unit_tests/ex_Lexicon.cc b/unit_tests/ex_Lexicon.cc new file mode 100644 index 0000000..e2df5cc --- /dev/null +++ b/unit_tests/ex_Lexicon.cc @@ -0,0 +1,200 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Verizon Media 2020 +/** @file + + Lexicon unit tests. +*/ + +#include "swoc/Lexicon.h" +#include "catch.hpp" + +// Example code for documentatoin +// --- + +enum class Example { INVALID, Value_0, Value_1, Value_2, Value_3 }; + +using ExampleNames = swoc::Lexicon<Example>; + +namespace +{ +[[maybe_unused]] ExampleNames Static_Names { + {Example::Value_0, {"zero", "0"}}, {Example::Value_1, {"one", "1"}}, {Example::Value_2, {"two", "2"}}, + {Example::Value_3, {"three", "3"}}, + { + Example::INVALID, { "INVALID" } + } +}; +} + +TEST_CASE("Lexicon Example", "[libts][Lexicon]") +{ + ExampleNames exnames{{Example::Value_0, {"zero", "0"}}, + {Example::Value_1, {"one", "1"}}, + {Example::Value_2, {"two", "2"}}, + {Example::Value_3, {"three", "3"}}, + {Example::INVALID, {"INVALID"}}}; + + ExampleNames exnames2{{Example::Value_0, "zero"}, + {Example::Value_1, "one"}, + {Example::Value_2, "two"}, + {Example::Value_3, "three"}, + {Example::INVALID, "INVALID"}}; + + // Check constructing with just defaults. + ExampleNames def_names_1 { Example::INVALID }; + ExampleNames def_names_2 { "INVALID" }; + ExampleNames def_names_3 { Example::INVALID, "INVALID" }; + + exnames.set_default(Example::INVALID).set_default("INVALID"); + + REQUIRE(exnames[Example::INVALID] == "INVALID"); + REQUIRE(exnames[Example::Value_0] == "zero"); + REQUIRE(exnames["zero"] == Example::Value_0); + REQUIRE(exnames["Zero"] == Example::Value_0); + REQUIRE(exnames["ZERO"] == Example::Value_0); + REQUIRE(exnames["one"] == Example::Value_1); + REQUIRE(exnames["1"] == Example::Value_1); + REQUIRE(exnames["Evil Dave"] == Example::INVALID); + REQUIRE(exnames[static_cast<Example>(0xBADD00D)] == "INVALID"); + REQUIRE(exnames[exnames[static_cast<Example>(0xBADD00D)]] == Example::INVALID); + + REQUIRE(def_names_1["zero"] == Example::INVALID); + REQUIRE(def_names_2[Example::Value_0] == "INVALID"); + REQUIRE(def_names_3["zero"] == Example::INVALID); + REQUIRE(def_names_3[Example::Value_0] == "INVALID"); + + enum class Radio { INVALID, ALPHA, BRAVO, CHARLIE, DELTA }; + using Lex = swoc::Lexicon<Radio>; + Lex lex({{Radio::INVALID, {"Invalid"}}, + {Radio::ALPHA, {"Alpha"}}, + {Radio::BRAVO, {"Bravo", "Beta"}}, + {Radio::CHARLIE, {"Charlie"}}, + {Radio::DELTA, {"Delta"}}}); + + // test structured binding for iteration. + for ([[maybe_unused]] auto const &[key, name] : lex) { + } +}; + +// --- +// End example code. + +enum Values { NoValue, LowValue, HighValue, Priceless }; +enum Hex { A, B, C, D, E, F, INVALID }; + +using ValueLexicon = swoc::Lexicon<Values>; +using HexLexicon = swoc::Lexicon<Hex>; + +TEST_CASE("Lexicon Constructor", "[libts][Lexicon]") +{ + // Construct with a secondary name for NoValue + ValueLexicon vl{{NoValue, {"NoValue", "garbage"}}, {LowValue, {"LowValue"}}}; + + REQUIRE("LowValue" == vl[LowValue]); // Primary name + REQUIRE(NoValue == vl["NoValue"]); // Primary name + REQUIRE(NoValue == vl["garbage"]); // Secondary name + REQUIRE_THROWS_AS(vl["monkeys"], std::domain_error); // No default, so throw. + vl.set_default(NoValue); // Put in a default. + REQUIRE(NoValue == vl["monkeys"]); // Returns default instead of throw + REQUIRE(LowValue == vl["lowVALUE"]); // Check case insensitivity. + + REQUIRE(NoValue == vl["HighValue"]); // Not defined yet. + vl.define(HighValue, {"HighValue", "High_Value"}); // Add it. + REQUIRE(HighValue == vl["HighValue"]); // Verify it's there and is case insensitive. + REQUIRE(HighValue == vl["highVALUE"]); + REQUIRE(HighValue == vl["HIGH_VALUE"]); + REQUIRE("HighValue" == vl[HighValue]); // Verify value -> primary name. + + // A few more checks on primary/secondary. + REQUIRE(NoValue == vl["Priceless"]); + REQUIRE(NoValue == vl["unique"]); + vl.define(Priceless, "Priceless", "Unique"); + REQUIRE("Priceless" == vl[Priceless]); + REQUIRE(Priceless == vl["unique"]); + + // Check default handlers. + using LL = swoc::Lexicon<Hex>; + bool bad_value_p = false; + LL ll_1({{A, "A"}, {B, "B"}, {C, "C"}, {E, "E"}}); + ll_1.set_default([&bad_value_p](std::string_view name) mutable -> Hex { + bad_value_p = true; + return INVALID; + }); + ll_1.set_default([&bad_value_p](Hex value) mutable -> std::string_view { + bad_value_p = true; + return "INVALID"; + }); + REQUIRE(bad_value_p == false); + REQUIRE(INVALID == ll_1["F"]); + REQUIRE(bad_value_p == true); + bad_value_p = false; + REQUIRE("INVALID" == ll_1[F]); + REQUIRE(bad_value_p == true); + bad_value_p = false; + // Verify that INVALID / "INVALID" are equal because of the default handlers. + REQUIRE("INVALID" == ll_1[INVALID]); + REQUIRE(INVALID == ll_1["INVALID"]); + REQUIRE(bad_value_p == true); + // Define the value/name, verify the handlers are *not* invoked. + ll_1.define(INVALID, "INVALID"); + bad_value_p = false; + REQUIRE("INVALID" == ll_1[INVALID]); + REQUIRE(INVALID == ll_1["INVALID"]); + REQUIRE(bad_value_p == false); + + ll_1.define({D, "D"}); // Pair style + ll_1.define({F, {"F", "0xf"}}); // Definition style + REQUIRE(ll_1[D] == "D"); + REQUIRE(ll_1["0XF"] == F); + + // iteration + std::bitset<INVALID + 1> mark; + for (auto [value, name] : ll_1) { + if (mark[value]) { + std::cerr << "Lexicon: " << name << ':' << value << " double iterated" << std::endl; + mark.reset(); + break; + } + mark[value] = true; + } + REQUIRE(mark.all()); + + ValueLexicon v2(std::move(vl)); + REQUIRE(vl.count() == 0); + + REQUIRE("LowValue" == v2[LowValue]); // Primary name + REQUIRE(NoValue == v2["NoValue"]); // Primary name + REQUIRE(NoValue == v2["garbage"]); // Secondary name + + REQUIRE(HighValue == v2["highVALUE"]); + REQUIRE(HighValue == v2["HIGH_VALUE"]); + REQUIRE("HighValue" == v2[HighValue]); // Verify value -> primary name. + + // A few more checks on primary/secondary. + REQUIRE("Priceless" == v2[Priceless]); + REQUIRE(Priceless == v2["unique"]); + +}; + +TEST_CASE("Lexicon Constructor 2", "[libts][Lexicon]") +{ + // Check the various construction cases + // No defaults, value default, name default, both, both the other way. + const HexLexicon v1({{A, {"A", "ten"}}, {B, {"B", "eleven"}}}); + + const HexLexicon v2({{A, {"A", "ten"}}, {B, {"B", "eleven"}}}, INVALID); + + const HexLexicon v3({{A, {"A", "ten"}}, {B, {"B", "eleven"}}}, "Invalid"); + + const HexLexicon v4({{A, {"A", "ten"}}, {B, {"B", "eleven"}}}, "Invalid", INVALID); + + const HexLexicon v5{{{A, {"A", "ten"}}, {B, {"B", "eleven"}}}, INVALID, "Invalid"}; + + REQUIRE(v1["a"] == A); + REQUIRE(v2["q"] == INVALID); + REQUIRE(v3[C] == "Invalid"); + REQUIRE(v4["q"] == INVALID); + REQUIRE(v4[C] == "Invalid"); + REQUIRE(v5["q"] == INVALID); + REQUIRE(v5[C] == "Invalid"); +}
