From c5351069807599984eb6c58e5d382f51fa4254da Mon Sep 17 00:00:00 2001 From: Robert Dailey Date: Sat, 9 Jul 2016 18:13:19 -0500 Subject: [PATCH] WIP: Better CMake support; Catch unit test support --- .travis.yml | 233 ++++++++++++++++++++++---- CMakeLists.txt | 103 ++++++++++++ CTestConfig.cmake | 3 + better-enums-config.cmake.in | 7 + test/CMakeLists.txt | 180 +++++++++----------- test/Makefile | 167 ------------------- test/source/test_enum.cpp | 312 +++++++++++++++++++++++++++++++++++ 7 files changed, 707 insertions(+), 298 deletions(-) create mode 100644 CMakeLists.txt create mode 100644 CTestConfig.cmake create mode 100644 better-enums-config.cmake.in delete mode 100644 test/Makefile create mode 100644 test/source/test_enum.cpp diff --git a/.travis.yml b/.travis.yml index b19bd55..e574268 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,34 +1,211 @@ language: cpp -env: - - COMPILER="clang++-3.7" - - COMPILER="g++-5" - - COMPILER="g++-4.6" - - COMPILER="g++-4.7" - - COMPILER="g++-4.4" - - COMPILER="g++-4.8" - - COMPILER="g++-4.9" - - COMPILER="g++-4.5" - - COMPILER="clang++-3.3" - - COMPILER="clang++-3.4" - - COMPILER="clang++-3.5" - - COMPILER="clang++-3.6" +matrix: + include: + ######################################## + ## GCC 4.4 + ######################################## + - os: linux + compiler: gcc + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-4.4 + env: + - C_COMPILER=gcc-4.4 + - CXX_COMPILER=g++-4.4 + + ######################################## + ## GCC 4.5 + ######################################## + - os: linux + compiler: gcc + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-4.5 + env: + - C_COMPILER=gcc-4.5 + - CXX_COMPILER=g++-4.5 + + ######################################## + ## GCC 4.6 + ######################################## + - os: linux + compiler: gcc + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-4.6 + env: + - C_COMPILER=gcc-4.6 + - CXX_COMPILER=g++-4.6 + + ######################################## + ## GCC 4.7 + ######################################## + - os: linux + compiler: gcc + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-4.7 + env: + - C_COMPILER=gcc-4.7 + - CXX_COMPILER=g++-4.7 + + ######################################## + ## GCC 4.8 + ######################################## + - os: linux + compiler: gcc + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-4.8 + env: + - C_COMPILER=gcc-4.8 + - CXX_COMPILER=g++-4.8 + + ######################################## + ## GCC 4.9 + ######################################## + - os: linux + compiler: gcc + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-4.9 + env: + - C_COMPILER=gcc-4.9 + - CXX_COMPILER=g++-4.9 + + ######################################## + ## GCC 5 + ######################################## + - os: linux + compiler: gcc + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-5 + env: + - C_COMPILER=gcc-5 + - CXX_COMPILER=g++-5 + + ######################################## + ## CLANG 3.3 + ######################################## + - os: linux + compiler: clang + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-precise-3.3 + - sourceline: 'ppa:ubuntu-toolchain-r/test' + - sourceline: 'deb http://llvm.org/apt/precise/ llvm-toolchain-precise-3.3 main' + key_url: 'http://llvm.org/apt/llvm-snapshot.gpg.key' + packages: + - clang-3.3 + env: + - C_COMPILER=clang-3.3 + - CXX_COMPILER=clang++-3.3 + + ######################################## + ## CLANG 3.4 + ######################################## + - os: linux + compiler: clang + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-precise-3.4 + packages: + - clang-3.4 + env: + - C_COMPILER=clang-3.4 + - CXX_COMPILER=clang++-3.4 + + ######################################## + ## CLANG 3.5 + ######################################## + - os: linux + compiler: clang + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-precise-3.5 + packages: + - clang-3.5 + env: + - C_COMPILER=clang-3.5 + - CXX_COMPILER=clang++-3.5 + + ######################################## + ## CLANG 4.6 + ######################################## + - os: linux + compiler: clang + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-precise-3.6 + packages: + - clang-3.6 + env: + - C_COMPILER=clang-3.6 + - CXX_COMPILER=clang++-3.6 + + ######################################## + ## CLANG 3.7 + ######################################## + - os: linux + compiler: clang + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-precise-3.7 + packages: + - clang-3.7 + env: + - C_COMPILER=clang-3.7 + - CXX_COMPILER=clang++-3.7 + +git: + submodules: false + +before_install: +- git submodule update --init --recursive before_script: - - sudo add-apt-repository --yes ppa:ubuntu-toolchain-r/test - - sudo add-apt-repository --yes ppa:h-rayflood/llvm - - sudo add-apt-repository -y 'deb http://llvm.org/apt/precise/ llvm-toolchain-precise-3.5 main' - - sudo add-apt-repository -y 'deb http://llvm.org/apt/precise/ llvm-toolchain-precise-3.6 main' - - sudo add-apt-repository -y 'deb http://llvm.org/apt/precise/ llvm-toolchain-precise-3.7 main' - - sudo apt-get update -qq - - sudo apt-get install --allow-unauthenticated $COMPILER - - git clone https://github.com/CxxTest/cxxtest.git cxxtest-ro - - curl -O https://cmake.org/files/v3.2/cmake-3.2.3-Linux-x86_64.tar.gz - - tar -xzf cmake-3.2.3-Linux-x86_64.tar.gz - - sudo cp -fR cmake-3.2.3-Linux-x86_64/* /usr +- sudo apt-get update -qq +- sudo apt-get install ninja-build +- curl -O https://cmake.org/files/v3.6/cmake-3.6.0-Linux-x86_64.tar.gz +- tar -xzf cmake-3.6.0-Linux-x86_64.tar.gz +- sudo cp -fR cmake-3.6.0-Linux-x86_64/* /usr script: - - export PATH="$PATH:$(pwd)/cxxtest-ro/bin" - - ln -s cxxtest-ro/cxxtest cxxtest - - cd test - - make COMPILER=$COMPILER unix +- mkdir build +- cd build +- cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=$C_COMPILER -DCMAKE_CXX_COMPILER=$CXX_COMPILER +- ninja +- ctest -T test --output-on-failure diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..4d7bc6b --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,103 @@ +cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR) + +set(project_name better-enums) + +project(${project_name}) + +include(CTest) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(CMAKE_INCLUDE_CURRENT_DIR_IN_INTERFACE ON) + +# Set this to the current published version of Better Enums +set(upstream_version 0.11.2) + +set(public_header_files + enum.h +) + +# Directories for installed targets +set(INSTALL_DIR ${CMAKE_INSTALL_PREFIX}) +set(INCLUDE_DIR include/${project_name}-${upstream_version}) +set(CONFIG_DIR lib/cmake/${project_name}-${upstream_version}) + +# Convert source files to absolute path, since `target_sources()` does not +# function well on relative paths and actually requires them to be absolute. +# +# If better enums ever gets actual translation units, we can build a STATIC +# or SHARED library, at which time this manual relative->absolute conversion +# will no longer be necessary since it won't be an INTERFACE target. +foreach(source_file ${public_header_files}) + get_filename_component(abs_source_file ${source_file} ABSOLUTE) + + # Use BUILD_INTERFACE because we don't actually want the interface + # source files to be exported with targets for installation purposes. + # The interface sources are only useful for our own targets, so that + # we see the files in the actual exe/lib projects for devleopment + # purposes. + # + # External projects that import better-enums do so only for the + # public include directories and the header files we use should not + # be put into their own projects. + list(APPEND absolute_source_files + $ + ) +endforeach() + +################################################################### +## DEFINE BUILD TARGETS +################################################################### + +# Main Library Target +add_library(${project_name} INTERFACE) +target_sources(${project_name} INTERFACE ${absolute_source_files}) + +# Test Targets +if(BUILD_TESTING) + add_subdirectory(test) +endif() + +################################################################### +## INSTALL TARGETS & FILES +################################################################### + +# Install all of our public header files +install(FILES + ${public_header_files} + DESTINATION ${INCLUDE_DIR} +) + +# Generate export rules for better-enums target +install(TARGETS ${project_name} EXPORT ${project_name}-targets + INCLUDES DESTINATION ${INCLUDE_DIR} +) + +install(EXPORT ${project_name}-targets + DESTINATION ${CONFIG_DIR} +) + +include(CMakePackageConfigHelpers) + +# Generate the Config Package file. Used by find_package() in +# downstream projects to find the better enums library +configure_package_config_file( + ${project_name}-config.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/${project_name}-config.cmake + INSTALL_DESTINATION ${CONFIG_DIR} + PATH_VARS INSTALL_DIR INCLUDE_DIR CONFIG_DIR +) + +# Version file for the config package also used by find_package() to +# allow a specific version of better enums to be found and used +write_basic_package_version_file( + ${CMAKE_CURRENT_BINARY_DIR}/${project_name}-config-version.cmake + VERSION ${upstream_version} + COMPATIBILITY AnyNewerVersion +) + +# Install the config package & version config files +install(FILES + ${CMAKE_CURRENT_BINARY_DIR}/${project_name}-config.cmake + ${CMAKE_CURRENT_BINARY_DIR}/${project_name}-config-version.cmake + DESTINATION ${CONFIG_DIR} +) diff --git a/CTestConfig.cmake b/CTestConfig.cmake new file mode 100644 index 0000000..5af372a --- /dev/null +++ b/CTestConfig.cmake @@ -0,0 +1,3 @@ +# This file is required to remain in the top-level directory of the project +# so CMake can find it. This file is deliberately blank to force CTest to +# ignore any default dart configuration settings (since we don't use dart). diff --git a/better-enums-config.cmake.in b/better-enums-config.cmake.in new file mode 100644 index 0000000..753001f --- /dev/null +++ b/better-enums-config.cmake.in @@ -0,0 +1,7 @@ +@PACKAGE_INIT@ + +set(BETTER_ENUMS_DIR "@PACKAGE_INSTALL_DIR@") +set(BETTER_ENUMS_INCLUDE_DIR "@PACKAGE_INCLUDE_DIR@") +set(BETTER_ENUMS_CONFIG_DIR "@PACKAGE_CONFIG_DIR@") + +include(${CMAKE_CURRENT_LIST_DIR}/better-enums-targets.cmake) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 789ea13..7f96433 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,12 +1,80 @@ -# Invoked automatically by the Makefile. +# Define catch as an interface target +add_library(catch INTERFACE) +target_include_directories(catch INTERFACE catch/single_include) -cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR) +################################################################################### +## +################################################################################### +macro( _parse_target_args prefix ) + set( OPTIONS + ) -project("Better Enums Testing" CXX) + set( ONE_VALUE_KEYWORDS + ) + + set( MULTI_VALUE_KEYWORDS + SOURCE_FILES + DEPENDENCIES + ) + + CMAKE_PARSE_ARGUMENTS( + ${prefix} + "${OPTIONS}" + "${ONE_VALUE_KEYWORDS}" + "${MULTI_VALUE_KEYWORDS}" + ${ARGN} + ) +endmacro() + +################################################################################### +## +################################################################################### +function(define_target target_name target_type) + _parse_target_args(DT ${ARGN}) + + if(NOT DT_SOURCE_FILES) + message(SEND_ERROR "Target ${target_name} defined with no source files") + endif() + + if(target_type STREQUAL "EXECUTABLE") + add_executable(${target_name} ${DT_SOURCE_FILES}) + else() + # @todo Add support for shared & static libraries later, if needed + message(FATAL_ERROR "Unknown target type specified: ${target_type}") + endif() + + if(DT_DEPENDENCIES) + target_link_libraries(${target_name} ${DT_DEPENDENCIES}) + endif() +endfunction() + +################################################################################### +## +################################################################################### +function(define_test test_name) + set(test_files ${ARGN}) + + define_target(${test_name} EXECUTABLE + SOURCE_FILES ${test_files} + DEPENDENCIES catch ${project_name} + ) + + add_test(NAME ${test_name} COMMAND ${test_name}) +endfunction() + +################################################################################### +## +################################################################################### +function(define_multiple_tests) + foreach(test_file ${ARGN}) + get_filename_component(filename ${test_file} NAME_WE) + #string( REPLACE "test_" "" filename ${filename} ) + define_test(${filename} ${test_file}) + endforeach() +endfunction() -# Detect compiler feature support. - +# Detect compiler feature support list(FIND CMAKE_CXX_COMPILE_FEATURES cxx_constexpr CONSTEXPR_INDEX) if(CONSTEXPR_INDEX EQUAL -1) set(SUPPORTS_CONSTEXPR 0) @@ -102,102 +170,8 @@ else() set(CMAKE_CXX_STANDARD 11) endif() +set(test_files + source/test_enum.cpp +) -# Basic tests. - -add_executable(cxxtest cxxtest/tests.cc) -add_executable(linking linking/helper.cc linking/main.cc) - -set(PERFORMANCE_TESTS - 1-simple 2-include_empty 3-only_include_enum 4-declare_enums 5-iostream) - -foreach(TEST ${PERFORMANCE_TESTS}) - add_executable(performance-${TEST} performance/${TEST}.cc) -endforeach(TEST) - - -# Select examples to build. - -set(EXAMPLES - 1-hello-world 2-conversions 3-iterate 4-switch 6-iostreams 7-safety - 8-representation 9-constexpr 101-special-values 103-bitset 104-quine - 105-c++17-reflection) - -set(SKIPPED_FOR_CXX98 - 5-map 9-constexpr 101-special-values 103-bitset 104-quine - 105-c++17-reflection) - -set(SKIPPED_FOR_STRICT_CONVERSION 4-switch) - -if(CONFIGURATION STREQUAL CXX98 OR NOT SUPPORTS_CONSTEXPR) - list(REMOVE_ITEM EXAMPLES ${SKIPPED_FOR_CXX98}) -endif() - -if(CONFIGURATION STREQUAL STRICT_CONVERSION) - list(REMOVE_ITEM EXAMPLES ${SKIPPED_FOR_STRICT_CONVERSION}) -endif() - -if(CONFIGURATION STREQUAL CXX14) - set(EXAMPLES 5-map) -endif() - -foreach(EXAMPLE ${EXAMPLES}) - add_executable(example-${EXAMPLE} ../example/${EXAMPLE}.cc) -endforeach(EXAMPLE) - - -# Add compiler flags. - -include_directories(..) -include_directories(../extra) - -if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES Clang) - include(CheckCXXCompilerFlag) - - macro(add_cxx_flag_if_supported FLAG) - check_cxx_compiler_flag("${FLAG}" HAVE_FLAG_${FLAG}) - - if(HAVE_FLAG_${FLAG}) - add_definitions(${FLAG}) - endif() - endmacro() - - macro(add_cxx_flag_to_target_if_supported TARGET FLAG) - string(REPLACE "=" "_equals_" ESCAPED ${FLAG}) - string(REPLACE "+" "_plus_" ESCAPED ${ESCAPED}) - check_cxx_compiler_flag("${FLAG}" HAVE_FLAG_${FLAG}) - - if(HAVE_FLAG_${FLAG}) - get_target_property(FLAGS ${TARGET} COMPILE_FLAGS) - if(${FLAGS} STREQUAL "FLAGS-NOTFOUND") - set(FLAGS "") - endif() - set_target_properties( - ${TARGET} PROPERTIES COMPILE_FLAGS "${FLAGS} ${FLAG}") - endif() - endmacro() - - add_cxx_flag_if_supported("-Wpedantic") - add_cxx_flag_if_supported("-Wall") - add_cxx_flag_if_supported("-Wextra") - add_cxx_flag_if_supported("-Wno-variadic-macros") - add_cxx_flag_if_supported("-Wno-unused-const-variable") - - add_cxx_flag_to_target_if_supported(linking "-Weverything") - add_cxx_flag_to_target_if_supported(linking "-Wno-c++98-compat-pedantic") - add_cxx_flag_to_target_if_supported(linking "-Wno-padded") - add_cxx_flag_to_target_if_supported(linking "-Wno-global-constructors") - add_cxx_flag_to_target_if_supported(linking "-Wno-old-style-cast") - add_cxx_flag_to_target_if_supported(linking "-Wno-missing-prototypes") - add_cxx_flag_to_target_if_supported(linking "-Wshadow") - add_cxx_flag_to_target_if_supported(linking "-Weffc++") - add_cxx_flag_to_target_if_supported(linking "-Wstrict-aliasing") - add_cxx_flag_to_target_if_supported(linking "-Wformat") - add_cxx_flag_to_target_if_supported(linking "-Wmissing-include-dirs") - add_cxx_flag_to_target_if_supported(linking "-Wsync-nand") - add_cxx_flag_to_target_if_supported(linking "-Wconditionally-supported") - add_cxx_flag_to_target_if_supported(linking "-Wconversion") - add_cxx_flag_to_target_if_supported(linking "-Wuseless-cast") - - add_definitions("-Werror") -endif() +define_multiple_tests(${test_files}) diff --git a/test/Makefile b/test/Makefile deleted file mode 100644 index 719a85e..0000000 --- a/test/Makefile +++ /dev/null @@ -1,167 +0,0 @@ -# Run "make" for quick builds while developing. -# Run "make default-all" before submitting a pull request. -# Run "make clean" to clean up. -# See doc/CONTRIBUTING.md for full instructions. - -CXXTEST_GENERATED := cxxtest/tests.cc - -UNIX_MAKE_COMMAND := make -WINDOWS_MAKE_COMMAND := "MSBuild.exe \"Better Enums Testing.sln\"" - -UNIX_OUTPUT_DIRECTORY := . -WINDOWS_OUTPUT_DIRECTORY := Debug - -ifdef COMSPEC - WIN32 := true -endif -ifdef ComSpec - WIN32 := true -endif - -ifndef WIN32 - -DEFAULT_MAKE_COMMAND := $(UNIX_MAKE_COMMAND) -DEFAULT_OUTPUT_DIRECTORY := $(UNIX_OUTPUT_DIRECTORY) -define PATH_FIX -@true -endef -SUFFIX := -CXXTESTGEN := cxxtestgen - -else - -DEFAULT_MAKE_COMMAND := $(WINDOWS_MAKE_COMMAND) -DEFAULT_OUTPUT_DIRECTORY := $(WINDOWS_OUTPUT_DIRECTORY) -define PATH_FIX -sed 's!include "/!include "C:/cygwin/!g' $1 > $$$$ && mv $$$$ $1 -endef -SUFFIX := .exe -CXXTESTGEN := python `which cxxtestgen | sed -E 's!(/cygdrive)?/c/!c:/!'` - -endif - -DEFAULTS := \ - TITLE=default \ - MAKE_COMMAND=$(DEFAULT_MAKE_COMMAND) \ - OUTPUT_DIRECTORY=$(DEFAULT_OUTPUT_DIRECTORY) - -# Builds one configuration with the system compiler. This will be either a -# regular C++11 or C++98 build (no constexpr to_string and no strict -# conversions). -.PHONY : default -default : examples - make $(DEFAULTS) one-configuration - -# Builds all configurations with the system compiler. -.PHONY : default-all -default-all : examples - make $(DEFAULTS) all-configurations - -.PHONY : examples -examples : - make -C ../doc examples - -# Example: make COMPILER=clang++36 unix -.PHONY : unix -unix : - make TITLE=$(COMPILER) CMAKE_OPTIONS="-DCMAKE_CXX_COMPILER=$(COMPILER)" \ - MAKE_COMMAND=$(UNIX_MAKE_COMMAND) \ - OUTPUT_DIRECTORY=$(UNIX_OUTPUT_DIRECTORY) all-configurations - -# Example: make TITLE=vc2013 COMPILER="Visual Studio 12 2013" ms -.PHONY : ms -ms : - make TITLE=$(TITLE) CMAKE_OPTIONS="-G \\\"$(COMPILER)\\\"" \ - MAKE_COMMAND=$(WINDOWS_MAKE_COMMAND) \ - OUTPUT_DIRECTORY=$(WINDOWS_OUTPUT_DIRECTORY) all-configurations - -# Expects three variables to be defined: -# CMAKE_OPTIONS: -# First, the compiler: -# - Empty for a "default" build. -# - -G "Visual Studio XX YYYY" to select a specific Microsoft compiler. -# - -DCMAKE_CXX_COMPILER=AAA to select a specific Unix compiler binary. -# Configuration selection (e.g. -DCONFIGURATION=CXX98) also go here. -# TITLE: -# The build title (subdirectory). Some combination of compiler/configuration. -# MAKE_COMMAND: -# Either make or msbuild "Better Enums Testing.sln" -# OUTPUT_DIRECTORY: -# Path to generated binaries relative to build directory. Either "." or -# "Debug". -.PHONY : one-configuration -one-configuration : $(CXXTEST_GENERATED) - mkdir -p build/$(TITLE) - cd build/$(TITLE) && cmake $(CMAKE_OPTIONS) ../.. && $(MAKE_COMMAND) - rm -rf build/$(TITLE)/bin - [ -f build/$(TITLE)/do-not-test ] || \ - ( ln -s $(OUTPUT_DIRECTORY) build/$(TITLE)/bin && \ - make BIN=build/$(TITLE)/bin run-tests ) - -.PHONY : run-tests -run-tests : - $(BIN)/cxxtest - @for FILE in $(BIN)/example-*$(SUFFIX) ; \ - do \ - EXAMPLE=$$(basename $$FILE | sed s/\.exe$$// | sed s/^example-//) ; \ - $$FILE | sed 's/\r$$//' | cmp - expect/$$EXAMPLE ; \ - RESULT=$$? ; \ - if [ $$RESULT -ne 0 ] ; \ - then \ - echo \'$$FILE\' produced bad output ; \ - exit $$RESULT ; \ - fi ; \ - done - @echo Example program output matches expected output - -.PHONY : all-configurations -all-configurations : - make TITLE=$(TITLE)-c++11 \ - CMAKE_OPTIONS="$(CMAKE_OPTIONS) -DCONFIGURATION=CONSTEXPR" \ - one-configuration - make TITLE=$(TITLE)-full-constexpr \ - CMAKE_OPTIONS="$(CMAKE_OPTIONS) -DCONFIGURATION=FULL_CONSTEXPR" \ - one-configuration - make TITLE=$(TITLE)-enum-class \ - CMAKE_OPTIONS="$(CMAKE_OPTIONS) -DCONFIGURATION=STRICT_CONVERSION" \ - one-configuration - make TITLE=$(TITLE)-c++14 \ - CMAKE_OPTIONS="$(CMAKE_OPTIONS) -DCONFIGURATION=CXX14" \ - one-configuration - make TITLE=$(TITLE)-c++98 \ - CMAKE_OPTIONS="$(CMAKE_OPTIONS) -DCONFIGURATION=CXX98" \ - one-configuration - -.PHONY : all-unix -all-unix : examples - make COMPILER=clang++39 unix - make COMPILER=clang++38 unix - make COMPILER=clang++37 unix - make COMPILER=g++52 unix - make COMPILER=g++46 unix - make COMPILER=g++47 unix - make COMPILER=g++43 unix - make COMPILER=g++48 unix - make COMPILER=g++49 unix - make COMPILER=g++44 unix - make COMPILER=g++45 unix - make COMPILER=clang++33 unix - make COMPILER=clang++34 unix - make COMPILER=clang++35 unix - make COMPILER=clang++36 unix - -.PHONY : all-ms -all-ms : examples - make TITLE=vc2015 COMPILER="Visual Studio 14 2015" ms - make TITLE=vc2008 COMPILER="Visual Studio 9 2008" ms - make TITLE=vc2010 COMPILER="Visual Studio 10 2010" ms - make TITLE=vc2012 COMPILER="Visual Studio 11 2012" ms - make TITLE=vc2013 COMPILER="Visual Studio 12 2013" ms - -$(CXXTEST_GENERATED) : cxxtest/*.h - $(CXXTESTGEN) --error-printer -o $@ $^ - $(call PATH_FIX,$@) - -.PHONY : clean -clean : - rm -rf build $(CXXTEST_GENERATED) diff --git a/test/source/test_enum.cpp b/test/source/test_enum.cpp new file mode 100644 index 0000000..0f9d22b --- /dev/null +++ b/test/source/test_enum.cpp @@ -0,0 +1,312 @@ +#define CATCH_CONFIG_MAIN +#include + +#include +#include + +#define MAIN_TAG "[better-enums]" + +BETTER_ENUM(Channel, short, Red, Green, Blue) +BETTER_ENUM(Depth, short, HighColor = 40, TrueColor = 20) +BETTER_ENUM(Compression, short, None, Huffman, Default = Huffman) + +namespace test +{ + BETTER_ENUM(Namespaced, short, One, Two) +} + +// Using BETTER_ENUMS_HAVE_CONSTEXPR_ as a proxy for C++11 support. This should +// be changed to be more precise in the future. +#ifdef BETTER_ENUMS_HAVE_CONSTEXPR_ + +// Type properties. +static_assert_1(std::is_class()); +static_assert_1(std::is_standard_layout()); +static_assert_1(std::is_literal_type()); + +// Member type properties and identities. +static_assert_1(std::is_integral()); +static_assert_1(std::is_enum()); + +static_assert_1((std::is_same())); +// Temporarily disabled due to outdated libraries in Travis. +// static_assert_1((std::is_same< +// short, std::underlying_type::type>())); + +static_assert_1(!(std::is_same())); +// Temporarily disabled due to outdated libraries in Travis. +// static_assert_1(!(std::is_same< +// int, std::underlying_type::type>())); + +static_assert_1(sizeof(Channel) == sizeof(short)); +static_assert_1(alignof(Channel) == alignof(short)); +static_assert_1((std::is_same())); + +// Supported constructors. + +// Apparently, this isn't supported by Clang in Travis. +// #ifdef __clang__ +// static_assert_1(std::is_trivially_copyable()); +// #endif + +static_assert_1((std::is_constructible())); +// "Passes" by causing a compilation error. +// static_assert_1(!(std::is_constructible())); +// "Passes" on most compilers, passes on g++47 by causing a compilation error. +// static_assert_1(!(std::is_constructible())); + +// Commented out temporarily due to GCC 4.7- bug. +// static_assert_1(!std::is_default_constructible()); + +// Intended implicit conversions. +static_assert_1((std::is_convertible())); + +// Regrettable implicit conversions. +static_assert_1((std::is_convertible())); + +// Disallowed implicit conversions. +static_assert_1(!(std::is_convertible())); +static_assert_1(!(std::is_convertible())); +static_assert_1(!(std::is_convertible())); + +// Controllable implicit conversions. +static_assert_1( +(std::is_convertible() != STRICT)); +static_assert_1( +(std::is_convertible() != STRICT)); +static_assert_1( +(std::is_convertible() + != STRICT)); +static_assert_1( +(std::is_convertible() + != STRICT)); + +static_assert_1((+Depth::HighColor)._to_integral() == 40); +static_assert_1(Depth::_from_integral(40) == +Depth::HighColor); +static_assert_1(Depth::_from_integral_unchecked(40) == +Depth::HighColor); +static_assert_1(Depth::_from_integral_nothrow(40)); +static_assert_1(*Depth::_from_integral_nothrow(40) == +Depth::HighColor); +static_assert_1(Depth::_is_valid(40)); + +static_assert_1(Channel::_from_string("Green") == +Channel::Green); +static_assert_1(Channel::_from_string_nocase("green") == +Channel::Green); +static_assert_1(*Channel::_from_string_nothrow("Green") == +Channel::Green); +static_assert_1( + *Channel::_from_string_nocase_nothrow("green") == +Channel::Green); +static_assert_1(Channel::_is_valid("Green")); +static_assert_1(Channel::_is_valid_nocase("green")); + +static_assert_1(Channel::_size() == 3); +static_assert_1(Channel::_values().size() == 3); +static_assert_1(*Channel::_values().begin() == +Channel::Red); +static_assert_1(*(Channel::_values().end() - 1) == +Channel::Blue); +static_assert_1(Channel::_values()[1] == +Channel::Green); + +namespace name_clash_test +{ + struct Foo {}; + std::ostream& operator<<(std::ostream&, Foo); + + BETTER_ENUM(Enum, int, ONE, TWO, THREE) + static_assert_1((std::is_same() << +Enum::ONE), std::ostream&>())); +} + +#ifdef BETTER_ENUMS_CONSTEXPR_TO_STRING + +constexpr bool same_string(const char *r, const char *s, size_t index = 0) +{ + return + r[index] == '\0' ? s[index] == '\0' : + s[index] == '\0' ? false : + r[index] != s[index] ? false : + same_string(r, s, index + 1); +} + +static_assert_1(same_string((+Depth::HighColor)._to_string(), "HighColor")); +static_assert_1(Depth::_names().size() == 2); +static_assert_1(same_string(*Depth::_names().begin(), "HighColor")); +static_assert_1(same_string(*(Depth::_names().end() - 1), "TrueColor")); +static_assert_1(same_string(Depth::_names()[0], "HighColor")); + +#endif // #ifdef BETTER_ENUMS_CONSTEXPR_TO_STRING + +#endif // #ifdef _ENUM_HAVE_CONSTEXPR + + +TEST_CASE("test constant values", MAIN_TAG) +{ + CHECK((+Channel::Red)._to_integral() == 0); + CHECK((+Channel::Green)._to_integral() == 1); + CHECK((+Channel::Blue)._to_integral() == 2); + CHECK((+Depth::HighColor)._to_integral() == 40); + CHECK((+Depth::TrueColor)._to_integral() == 20); +} + +TEST_CASE("test integral conversions", MAIN_TAG) +{ + CHECK(Channel::_from_integral(1) == +Channel::Green); + CHECK(Channel::_from_integral(1) != +Channel::Blue); + CHECK(Channel::_from_integral_unchecked(1) == +Channel::Green); + CHECK(Channel::_from_integral_unchecked(1) != +Channel::Blue); + + CHECK_THROWS_AS(Channel::_from_integral(3), std::runtime_error); + CHECK_NOTHROW(Channel::_from_integral_unchecked(3)); + + better_enums::optional maybe_channel = + Channel::_from_integral_nothrow(2); + + CHECK(maybe_channel); + CHECK(*maybe_channel == +Channel::Blue); + CHECK(!Channel::_from_integral_nothrow(3)); + + CHECK(Channel::_is_valid((Channel::_integral)0)); + CHECK(Channel::_is_valid(1)); + CHECK(Channel::_is_valid(2)); + CHECK(!Channel::_is_valid(3)); +} + +TEST_CASE("test string conversions", MAIN_TAG) +{ + CHECK(strcmp((+Channel::Green)._to_string(), "Green") == 0); + CHECK(strcmp((+Depth::HighColor)._to_string(), "HighColor") == 0); + + CHECK(Channel::_from_string("Green") == +Channel::Green); + CHECK(Channel::_from_string("Green") != +Channel::Blue); + CHECK(Channel::_from_string("Blue") == +Channel::Blue); + CHECK(Channel::_from_string("Blue") != +Channel::Green); + CHECK_THROWS_AS(Channel::_from_string("green"), std::runtime_error); + + better_enums::optional maybe_channel = + Channel::_from_string_nothrow("Green"); + + CHECK(maybe_channel); + CHECK(*maybe_channel == +Channel::Green); + CHECK(!Channel::_from_string_nothrow("green")); + + CHECK(Channel::_from_string_nocase("green") == +Channel::Green); + CHECK(Channel::_from_string_nocase("green") != +Channel::Blue); + CHECK(Channel::_from_string_nocase("blue") == +Channel::Blue); + CHECK(Channel::_from_string_nocase("blue") != +Channel::Green); + CHECK_THROWS_AS(Channel::_from_string_nocase("a"), std::runtime_error); + + maybe_channel = Channel::_from_string_nocase_nothrow("green"); + + CHECK(maybe_channel); + CHECK(*maybe_channel == +Channel::Green); + CHECK(!Channel::_from_string_nocase_nothrow("greeen")); + CHECK(Channel::_is_valid("Green")); + CHECK(!Channel::_is_valid("green")); + CHECK(Channel::_is_valid_nocase("green")); + CHECK(!Channel::_is_valid_nocase("greeen")); +} + +TEST_CASE("test value iterable", MAIN_TAG) +{ + CHECK(Channel::_size() == 3); + CHECK(Depth::_size() == 2); + CHECK(Channel::_values().size() == Channel::_size()); + CHECK(*Channel::_values().begin() == +Channel::Red); + CHECK(*(Channel::_values().begin() + 1) == +Channel::Green); + CHECK(*(Channel::_values().begin() + 2) == +Channel::Blue); + CHECK(Channel::_values()[1] == +Channel::Green); + CHECK(Channel::_values()[2] == +Channel::Blue); + + Channel::_value_iterator value_iterator = Channel::_values().begin(); + + CHECK(*value_iterator == +Channel::Red); + CHECK(value_iterator != Channel::_values().end()); + + ++value_iterator; + + CHECK(*value_iterator == +Channel::Green); + CHECK(value_iterator != Channel::_values().end()); + + ++value_iterator; + + CHECK(*value_iterator == +Channel::Blue); + CHECK(value_iterator != Channel::_values().end()); + + ++value_iterator; + + CHECK(value_iterator == Channel::_values().end()); +} + +TEST_CASE("test name iterable", MAIN_TAG) +{ + CHECK(Channel::_names().size() == Channel::_size()); + CHECK(strcmp(*Channel::_names().begin(), "Red") == 0); + CHECK(strcmp(Channel::_names()[0], "Red") == 0); + CHECK(strcmp(Depth::_names()[0], "HighColor") == 0); + + Channel::_name_iterator name_iterator = Channel::_names().begin(); + + CHECK(strcmp(*name_iterator, "Red") == 0); + CHECK(name_iterator != Channel::_names().end()); + + ++name_iterator; + + CHECK(strcmp(*name_iterator, "Green") == 0); + CHECK(name_iterator != Channel::_names().end()); + + ++name_iterator; + + CHECK(strcmp(*name_iterator, "Blue") == 0); + CHECK(name_iterator != Channel::_names().end()); + + ++name_iterator; + + CHECK(name_iterator == Channel::_names().end()); +} + +TEST_CASE("test type name", MAIN_TAG) +{ + CHECK(strcmp(Channel::_name(), "Channel") == 0); + CHECK(strcmp(Depth::_name(), "Depth") == 0); + CHECK(strcmp(Compression::_name(), "Compression") == 0); +} + +/// @todo This test case is failing and is currently deemed invalid +/// at this time. +/// +/// @see https://github.com/aantron/better-enums/issues/24 +/* +TEST_CASE("test alias", MAIN_TAG) +{ + CHECK(Compression::Default == Compression::Huffman); +} +*/ + +TEST_CASE("test namespaced", MAIN_TAG) +{ + CHECK((+test::Namespaced::One)._to_integral() == 0); + CHECK(test::Namespaced::_from_integral(0) == +test::Namespaced::One); + CHECK(strcmp((+test::Namespaced::One)._to_string(), "One") == 0); + CHECK(test::Namespaced::_from_string("Two") == +test::Namespaced::Two); + CHECK(test::Namespaced::_values()[0] == +test::Namespaced::One); + CHECK(strcmp(test::Namespaced::_names()[0], "One") == 0); + CHECK(*test::Namespaced::_values().begin() == +test::Namespaced::One); + CHECK(strcmp(*test::Namespaced::_names().begin(), "One") == 0); +} + +BETTER_ENUM(Compiler, int, GCC, Clang, MSVC) + +TEST_CASE("test stream output", MAIN_TAG) +{ + std::ostringstream stream; + stream << +Compiler::GCC; + CHECK(stream.str() == "GCC"); +} + +TEST_CASE("test stream input", MAIN_TAG) +{ + std::istringstream stream("Clang"); + Compiler compiler = Compiler::GCC; + stream >> compiler; + CHECK(compiler == +Compiler::Clang); +} + +BETTER_ENUM(InternalNameCollisions, int, + EnumClassForSwitchStatements, PutNamesInThisScopeAlso, + force_initialization, value_array, raw_names, name_storage, + name_array, initialized, the_raw_names, the_name_array)