Skip to content

Commit

Permalink
Improve test coverage.
Browse files Browse the repository at this point in the history
  • Loading branch information
ColinH committed Apr 2, 2024
1 parent 43520ce commit d1f75bf
Show file tree
Hide file tree
Showing 18 changed files with 143 additions and 32 deletions.
2 changes: 2 additions & 0 deletions .codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ignore:
- src/example/**/*
2 changes: 1 addition & 1 deletion include/tao/config/internal/phase2_additions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ namespace tao::config::internal

[[noreturn]] static void throw_type_error( const entry& l, const entry& r )
{
throw pegtl::parse_error( strcat( "incompatible or invalid type(s) in addition", l.kind(), "@", l.get_position(), " and ", r.kind(), "@", r.get_position() ), pegtl::position( 0, 0, 0, "(todo) location of '+'" ) );
throw pegtl::parse_error( strcat( "incompatible or invalid type(s) in addition ", l.kind(), "@", l.get_position(), " and ", r.kind(), "@", r.get_position() ), pegtl::position( 0, 0, 0, "(todo) location of '+'" ) );
}

static void process_object( object&& l, object& r )
Expand Down
6 changes: 3 additions & 3 deletions include/tao/config/internal/phase3_remove.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,17 +84,17 @@ namespace tao::config::internal
continue;
case entry_kind::ARRAY:
if( !e.get_array().function.empty() ) {
throw pegtl::parse_error( "unresolved function '" + e.get_array().function + '\'', e.get_array().position );
throw pegtl::parse_error( "function '" + e.get_array().function + "' could not be called", e.get_array().position );
}
phase3_remove( e.get_array() );
continue;
case entry_kind::OBJECT:
phase3_remove( e.get_object() );
continue;
case entry_kind::ASTERISK:
throw pegtl::parse_error( "unresolved asterisk", e.get_asterisk().position ); // Can happen when there are also unresolved references.
throw pegtl::parse_error( "asterisk could not be expanded", e.get_asterisk().position ); // Can happen when there are also unresolved references.
case entry_kind::REFERENCE:
throw pegtl::parse_error( "unresolved reference '" + e.get_reference().to_string() + '\'', e.get_reference().at( 0 ).position );
throw pegtl::parse_error( "reference '" + e.get_reference().to_string() + "' could not be resolved", e.get_reference().at( 0 ).position );
}
throw std::logic_error( "code should be unreachable" ); // LCOV_EXCL_LINE
}
Expand Down
6 changes: 3 additions & 3 deletions include/tao/config/internal/system_utility.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,14 @@ namespace tao::config::internal
}

#if !defined( _MSC_VER )
[[nodiscard]] inline std::string shell_popen_throws( const pegtl::position& /*pos*/, const std::string& script )
[[nodiscard]] inline std::string shell_popen_throws( const pegtl::position& pos, const std::string& script )
{
errno = 0;

std::unique_ptr< FILE, void ( * )( FILE* ) > file( ::popen( script.c_str(), "r" ), []( FILE* f ) { ::pclose( f ); } );

if( !file ) {
throw std::string( "TODO" );
throw pegtl::parse_error( "TODO: Better error message for shell function error.", pos );
// throw pegtl::parse_error( format( __FILE__, __LINE__, "popen failed", { { "command", script }, { "errno", errno } } ), pos );
}
std::string result;
Expand All @@ -81,7 +81,7 @@ namespace tao::config::internal
errno = 0;

if( ( errno != 0 ) || ( ::pclose( file.release() ) != 0 ) ) {
throw std::string( "TODO" );
throw pegtl::parse_error( "TODO: Better error message for shell function error.", pos );
}
return result;
}
Expand Down
9 changes: 2 additions & 7 deletions include/tao/config/key_part.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
#ifndef TAO_CONFIG_KEY_PART_HPP
#define TAO_CONFIG_KEY_PART_HPP

#include <cassert>
#include <ostream>
#include <stdexcept>
#include <string>
Expand Down Expand Up @@ -43,16 +42,12 @@ namespace tao::config

[[nodiscard]] std::size_t get_index() const noexcept
{
const auto* s = std::get_if< std::size_t >( &data );
assert( s != nullptr );
return *s;
return std::get< std::size_t >( data );
}

[[nodiscard]] const std::string& get_name() const noexcept
{
const auto* s = std::get_if< std::string >( &data );
assert( s != nullptr );
return *s;
return std::get< std::string >( data );
}

data_t data;
Expand Down
4 changes: 3 additions & 1 deletion src/test/config/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ set(testsources
access.cpp
assign.cpp
custom.cpp
debug_traits.cpp
enumerations.cpp
failure.cpp
multi_line_string_position.cpp
parse_key1.cpp
parse_key.cpp
parse_reference2.cpp
success.cpp
to_stream.cpp
)

# file(GLOB ...) is used to validate the above list of test_sources
Expand All @@ -27,7 +29,7 @@ foreach(testsourcefile ${testsources})
add_executable(${exename} ${testsourcefile})
target_link_libraries(${exename} PRIVATE taocpp::config)
set_target_properties(${exename} PROPERTIES
CXX_STANDARD 11
CXX_STANDARD 17
CXX_STANDARD_REQUIRED ON
CXX_EXTENSIONS OFF
)
Expand Down
1 change: 1 addition & 0 deletions src/test/config/access.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ namespace tao::config
TAO_CONFIG_TEST_ASSERT( access( v, key( "c.d.e.1" ) ).key == key( "c.d.e.1" ) );

TAO_CONFIG_TEST_THROWS( (void)access( v, key( "0" ) ) );
TAO_CONFIG_TEST_THROWS( (void)access( v, key( "a.0" ) ) );
TAO_CONFIG_TEST_THROWS( (void)access( v, key( "r" ) ) );
TAO_CONFIG_TEST_THROWS( (void)access( v, key( "c.d.e.f" ) ) );
TAO_CONFIG_TEST_THROWS( (void)access( v, key( "c.d.e.2" ) ) );
Expand Down
1 change: 1 addition & 0 deletions src/test/config/assign.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ namespace tao::config
TAO_CONFIG_TEST_THROWS( (void)assign( v, key( "0" ) ) );
TAO_CONFIG_TEST_THROWS( (void)assign( v, key( "c.d.e.f" ) ) );
TAO_CONFIG_TEST_THROWS( (void)assign( v, key( "c.d.e.2" ) ) );
TAO_CONFIG_TEST_THROWS( (void)assign( v, key( "a.0" ) ) );

TAO_CONFIG_TEST_ASSERT( assign( v, key( "r" ) ) == value( json::empty_object ) );
TAO_CONFIG_TEST_ASSERT( assign( v, key( "r.s.t" ) ) == value( json::empty_object ) );
Expand Down
29 changes: 29 additions & 0 deletions src/test/config/debug_traits.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright (c) 2021-2024 Dr. Colin Hirsch and Daniel Frey
// Please see LICENSE for license or visit https://github.com/taocpp/config/

#include <sstream>
#include <string>
#include <string_view>

#include "test.hpp"

#include <tao/config.hpp>
#include <tao/config/internal/to_stream.hpp>

namespace tao::config
{
void unit_test()
{
const std::string input = "a = 1 b { c = 42 d = [ null, true ] }";
internal::config_parser cp;
cp.parse( std::string_view( input ), __FUNCTION__ );
std::ostringstream oss;
internal::to_stream( oss, cp.st.root );
const std::string output = oss.str();
const std::string reference = "{position:\"(root):1:1\",object_data:{a:{remove:true,implicit:false,temporary:false,position:\"unit_test:1:1\",concat_list:[{type:\"unsigned\",data:1}]},b:{remove:false,implicit:false,temporary:false,position:\"unit_test:1:7\",concat_list:[{type:\"object\",data:{position:\"unit_test:1:9\",object_data:{c:{remove:true,implicit:false,temporary:false,position:\"unit_test:1:11\",concat_list:[{type:\"unsigned\",data:42}]},d:{remove:true,implicit:false,temporary:false,position:\"unit_test:1:18\",concat_list:[{type:\"array\",data:{position:\"unit_test:1:22\",function:\"\",array_data:[{remove:false,implicit:false,temporary:false,position:\"unit_test:1:24\",concat_list:[{type:\"null\"}]},{remove:false,implicit:false,temporary:false,position:\"unit_test:1:24\",concat_list:[{type:\"boolean\",data:true}]}]}}]}}}}]}}}";
TAO_CONFIG_TEST_ASSERT( output == reference );
}

} // namespace tao::config

#include "main.hpp"
23 changes: 7 additions & 16 deletions src/test/config/failure.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,45 +14,36 @@ const char* ansi_text = "\033[33m";

namespace tao
{
int failed = 0;
unsigned failed = 0;

template< template< typename... > class Traits >
void unit_test( const std::filesystem::path& path )
{
try {
// For a failure testcase to succeed the next line must throw an error.
const auto cc = config::basic_from_file< Traits >( path );
// LCOV_EXCL_START
const auto ccs = json::jaxn::to_string( cc );
++failed;
std::cerr << std::endl
<< "Testcase '" << path << "' failed error test!" << std::endl;
std::cerr << "<<< Config parsed as config <<<" << std::endl;
std::cerr << ccs << std::endl;
std::cerr << ">>> Config parsed as config >>>" << std::endl;
// LCOV_EXCL_STOP
}
catch( const pegtl::parse_error_base& e ) {
std::cout << ansi_text << "pegtl::parse_error: " << ansi_message << e.message() << ": " << ansi_source << e.position_string() << ansi_reset << std::endl;
}
catch( const std::exception& e ) {
std::cout << "std::exception: " << e.what() << std::endl;
}
catch( const std::string& s ) {
std::cout << "Exception with std::string: " << s << std::endl;
// TODO: Remove catch string when all temporary throw strings have been replaced with proper exceptions.
}
}

} // namespace tao

int main( int argc, char** argv )
int main()
{
if( argc > 1 ) {
for( int i = 1; i < argc; ++i ) {
std::cout << "Parsing " << argv[ i ] << std::endl;
tao::unit_test< tao::config::traits >( argv[ i ] );
}
return 0;
}

unsigned count = 0;

for( const auto& entry : std::filesystem::directory_iterator( "tests" ) ) {
Expand All @@ -62,8 +53,8 @@ int main( int argc, char** argv )
++count;
}
}
if( !tao::failed ) {
if( tao::failed == 0 ) {
std::cerr << "All " << count << " testcases passed." << std::endl;
}
return std::min( tao::failed, 127 );
return std::min( int( tao::failed ), 127 );
}
28 changes: 27 additions & 1 deletion src/test/config/success.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <algorithm>
#include <filesystem>
#include <iostream>
#include <stdexcept>

#include <tao/config.hpp>

Expand All @@ -19,10 +20,12 @@ namespace tao
#else
errno = 0;
if( ::setenv( name.c_str(), value.c_str(), 1 ) != 0 ) {
// LCOV_EXCL_START
const auto e = errno;
#endif
(void)e;
throw std::string( "setenv failed" );
throw std::runtime_error( "setenv failed" );
// LCOV_EXCL_STOP
}
}

Expand All @@ -31,16 +34,34 @@ namespace tao
{
try {
const auto cc = config::basic_from_file< Traits >( path );
const std::vector< std::filesystem::path > paths = { "tests/empty.success", path, "tests/empty.success" };
const auto cf = config::basic_from_files< Traits >( paths );

std::filesystem::path jaxn = path;
jaxn.replace_extension( ".jaxn" );
const auto cj = config::basic_from_file< Traits >( jaxn );
const auto jj = json::jaxn::basic_from_file< Traits >( jaxn );

const auto ccs = json::jaxn::to_string( cc );
const auto cfs = json::jaxn::to_string( cf );
const auto cjs = json::jaxn::to_string( cj );
const auto jjs = json::jaxn::to_string( jj );

if( ccs != cfs ) {
// LCOV_EXCL_START
++failed;
std::cerr << std::endl
<< "Testcase '" << path << "' failed config test!" << std::endl;
std::cerr << "<<< Config parsed as config <<<" << std::endl;
std::cerr << ccs << std::endl;
std::cerr << ">>> Config parsed as config >>>" << std::endl;
std::cerr << "<<< Config parsed as config with empty files <<<" << std::endl;
std::cerr << jjs << std::endl;
std::cerr << ">>> Config parsed as config with empty files >>>" << std::endl;
// LCOV_EXCL_STOP
}
if( ccs != jjs ) {
// LCOV_EXCL_START
++failed;
std::cerr << std::endl
<< "Testcase '" << path << "' failed config test!" << std::endl;
Expand All @@ -50,8 +71,10 @@ namespace tao
std::cerr << "<<< Reference data parsed as jaxn <<<" << std::endl;
std::cerr << jjs << std::endl;
std::cerr << ">>> Reference data parsed as jaxn >>>" << std::endl;
// LCOV_EXCL_STOP
}
if( ccs != cjs ) {
// LCOV_EXCL_START
++failed;
std::cerr << std::endl
<< "Testcase '" << path << "' failed identity test!" << std::endl;
Expand All @@ -61,8 +84,10 @@ namespace tao
std::cerr << "<<< Reference data parsed as config <<<" << std::endl;
std::cerr << cjs << std::endl;
std::cerr << ">>> Reference data parsed as config >>>" << std::endl;
// LCOV_EXCL_STOP
}
}
// LCOV_EXCL_START
catch( const std::exception& e ) {
std::cerr << "Testcase '" << path << "' failed with exception '" << e.what() << "'" << std::endl;
++failed;
Expand All @@ -71,6 +96,7 @@ namespace tao
std::cerr << "Testcase '" << path << "' failed with error '" << s << "'" << std::endl;
++failed;
}
// LCOV_EXCL_STOP
}

} // namespace tao
Expand Down
46 changes: 46 additions & 0 deletions src/test/config/to_stream.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright (c) 2024 Dr. Colin Hirsch and Daniel Frey
// Please see LICENSE for license or visit https://github.com/taocpp/config/

#include <iostream>
#include <sstream>
#include <string>

#include "test.hpp"

#include <tao/config.hpp>

namespace tao::config
{
void unit_test()
{
const std::string input = "a = 1 b { c = 42 d = [ null, true ] }";
const tao::config::value parsed = tao::config::from_string( input, "main" );
{
std::ostringstream oss;
tao::config::to_stream( oss, parsed );
const std::string string1 = oss.str();
const std::string reference1 = "{meta:{key:[],position:\"(root):1:1\"},data:{a:{meta:{key:[{key_kind:\"name\",key_data:{index:0,value:\"a\"}}],position:\"main:1:6\"},data:1},b:{meta:{key:[{key_kind:\"name\",key_data:{index:0,value:\"b\"}}],position:\"main:1:9\"},data:{c:{meta:{key:[{key_kind:\"name\",key_data:{index:0,value:\"b\"}},{key_kind:\"name\",key_data:{index:0,value:\"c\"}}],position:\"main:1:17\"},data:42},d:{meta:{key:[{key_kind:\"name\",key_data:{index:0,value:\"b\"}},{key_kind:\"name\",key_data:{index:0,value:\"d\"}}],position:\"main:1:22\"},data:[{meta:{key:[{key_kind:\"name\",key_data:{index:0,value:\"b\"}},{key_kind:\"name\",key_data:{index:0,value:\"d\"}},{key_kind:\"index\",key_data:{index:1,value:0}}],position:\"main:1:24\"},data:null},{meta:{key:[{key_kind:\"name\",key_data:{index:0,value:\"b\"}},{key_kind:\"name\",key_data:{index:0,value:\"d\"}},{key_kind:\"index\",key_data:{index:1,value:1}}],position:\"main:1:30\"},data:true}]}}}}}";
if( string1 != reference1 ) {
std::cerr << string1 << std::endl;
std::cerr << reference1 << std::endl;
std::cerr << "Config to_stream test 1 failed!" << std::endl;
++failed;
}
} {
std::ostringstream oss;
tao::config::to_stream( oss, parsed, 3 );
const std::string string2 = oss.str();
const std::string reference2 = "{\n meta: {\n key: [],\n position: \"(root):1:1\"\n },\n data: {\n a: {\n meta: {\n key: [\n {\n key_kind: \"name\",\n key_data: {\n index: 0,\n value: \"a\"\n }\n }\n ],\n position: \"main:1:6\"\n },\n data: 1\n },\n b: {\n meta: {\n key: [\n {\n key_kind: \"name\",\n key_data: {\n index: 0,\n value: \"b\"\n }\n }\n ],\n position: \"main:1:9\"\n },\n data: {\n c: {\n meta: {\n key: [\n {\n key_kind: \"name\",\n key_data: {\n index: 0,\n value: \"b\"\n }\n },\n {\n key_kind: \"name\",\n key_data: {\n index: 0,\n value: \"c\"\n }\n }\n ],\n position: \"main:1:17\"\n },\n data: 42\n },\n d: {\n meta: {\n key: [\n {\n key_kind: \"name\",\n key_data: {\n index: 0,\n value: \"b\"\n }\n },\n {\n key_kind: \"name\",\n key_data: {\n index: 0,\n value: \"d\"\n }\n }\n ],\n position: \"main:1:22\"\n },\n data: [\n {\n meta: {\n key: [\n {\n key_kind: \"name\",\n key_data: {\n index: 0,\n value: \"b\"\n }\n },\n {\n key_kind: \"name\",\n key_data: {\n index: 0,\n value: \"d\"\n }\n },\n {\n key_kind: \"index\",\n key_data: {\n index: 1,\n value: 0\n }\n }\n ],\n position: \"main:1:24\"\n },\n data: null\n },\n {\n meta: {\n key: [\n {\n key_kind: \"name\",\n key_data: {\n index: 0,\n value: \"b\"\n }\n },\n {\n key_kind: \"name\",\n key_data: {\n index: 0,\n value: \"d\"\n }\n },\n {\n key_kind: \"index\",\n key_data: {\n index: 1,\n value: 1\n }\n }\n ],\n position: \"main:1:30\"\n },\n data: true\n }\n ]\n }\n }\n }\n }\n}";
if( string2 != reference2 ) {
std:: cerr << string2.size() << " " << reference2.size() << std::endl;
std::cerr << string2 << std::endl;
std::cerr << reference2 << std::endl;
std::cerr << "Config to_stream test 2 failed!" << std::endl;
++failed;
}
}
}

} // namespace tao::config

#include "main.hpp"
5 changes: 5 additions & 0 deletions tests/binary.jaxn
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
a : $3031,
b : $3031,
c : $3031
}
3 changes: 3 additions & 0 deletions tests/binary.success
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
a = $3031
b = $30 + $31
c = $"\x30\x31"
2 changes: 2 additions & 0 deletions tests/complex_01.failure
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
a = (default (b) "foo")
b = (default (a) "bar")
4 changes: 4 additions & 0 deletions tests/complex_02.failure
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
a = {}
a.* = 42
a.a.b = (default (a.a.c) "foo")
a.a.c = (default (a.a.b) "bar")
2 changes: 2 additions & 0 deletions tests/reference_11.failure
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
a.b.c = (b.x)
a.b.x = (b.c)
2 changes: 2 additions & 0 deletions tests/reference_12.failure
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
a = ((y))
y = ((a))

0 comments on commit d1f75bf

Please sign in to comment.