Add tests and fix minor problems in specifier parser
This commit is contained in:
		| @@ -5,4 +5,5 @@ aux_source_directory("src" SOURCES) | ||||
| add_library(${PROJECT_NAME} STATIC ${SOURCES}) | ||||
| target_include_directories(${PROJECT_NAME} PUBLIC include) | ||||
|  | ||||
|  | ||||
| # Add tests. This is not included in the overall build | ||||
| add_subdirectory(test) | ||||
| @@ -2,16 +2,44 @@ | ||||
| #define _SIMPLE_PRINTF_H_ | ||||
|  | ||||
| #include <stddef.h> | ||||
| #include <stdbool.h> | ||||
|  | ||||
| #ifndef SIMPLE_PRINTF_ATTR_FORMAT | ||||
| #   if ((__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3))) | ||||
| #       define SIMPLE_PRINTF_ATTR_FORMAT(fmt, args) __attribute__((__format__(__printf__, fmt, args))) | ||||
| #   else | ||||
| #       define SIMPLE_PRINTF_ATTR_FORMAT(fmt, args) | ||||
| #   endif | ||||
| #if ((__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3))) | ||||
| #define SIMPLE_PRINTF_ATTR_FORMAT(fmt, args) __attribute__((__format__(__printf__, fmt, args))) | ||||
| #else | ||||
| #   define SIMPLE_PRINTF_ATTR_FORMAT(fmt, args) | ||||
| #define SIMPLE_PRINTF_ATTR_FORMAT(fmt, args) | ||||
| #endif | ||||
| #else | ||||
| #define SIMPLE_PRINTF_ATTR_FORMAT(fmt, args) | ||||
| #endif | ||||
|  | ||||
| enum format_specifier_type { | ||||
| 	FMT_INVALID = 0, | ||||
| 	FMT_INTEGER, | ||||
| 	FMT_UNSIGNED, | ||||
| 	FMT_STRING, | ||||
| 	FMT_HEXUINT_CAPITALIZED, | ||||
| 	FMT_HEXUINT, | ||||
| 	FMT_CHAR, | ||||
| 	FMT_SIZE, | ||||
| 	FMT_PERCENT, | ||||
| }; | ||||
|  | ||||
| struct format_specifier { | ||||
| 	enum format_specifier_type specifier; | ||||
| 	size_t length; /**< @brief lenght of formatted output.*/ | ||||
| 	bool zero_pad; | ||||
| 	size_t length_of_provided_specifier; | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * @brief Helper function to parse printf format specifiers | ||||
|  *  | ||||
|  * @param start Start of parsing | ||||
|  * @param[out] result Result of format specifier parsing | ||||
|  */ | ||||
| void parse_format_specifier(const char *start, struct format_specifier *result); | ||||
|  | ||||
| /** | ||||
|  * @brief Simple implementation of snprintf | ||||
|   | ||||
| @@ -2,25 +2,6 @@ | ||||
| #include <stddef.h> | ||||
| #include <stdbool.h> | ||||
|  | ||||
| enum format_specifier_type { | ||||
| 	FMT_INVALID = 0, | ||||
| 	FMT_INTEGER, | ||||
| 	FMT_UNSIGNED, | ||||
| 	FMT_STRING, | ||||
| 	FMT_HEXUINT_CAPITALIZED, | ||||
| 	FMT_HEXUINT, | ||||
| 	FMT_CHAR, | ||||
| 	FMT_SIZE, | ||||
| 	FMT_PERCENT, | ||||
| }; | ||||
|  | ||||
| struct format_specifier { | ||||
| 	enum format_specifier_type specifier; | ||||
| 	size_t length; /**< @brief lenght of formatted output.*/ | ||||
| 	bool zero_pad;  | ||||
| 	size_t length_of_provided_specifier; | ||||
| }; | ||||
|  | ||||
| enum format_parser_state { | ||||
| 	PARSER_PERCENT = 0, | ||||
| 	PARSER_SECOND, | ||||
| @@ -87,7 +68,7 @@ static enum format_specifier_type resolve_specifier(char c) | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| static void parse_format_specifier(const char *start, struct format_specifier *result) | ||||
| void parse_format_specifier(const char *start, struct format_specifier *result) | ||||
| { | ||||
| 	size_t fmt_len = 1ull; | ||||
| 	bool keep_parsing = true; | ||||
| @@ -115,15 +96,19 @@ static void parse_format_specifier(const char *start, struct format_specifier *r | ||||
| 		switch (state) { | ||||
| 		case PARSER_PERCENT: | ||||
| 			if (*ptr == '%') { | ||||
| 				advance_ptr++; | ||||
| 				advance_ptr = true; | ||||
| 				next_state = PARSER_SECOND; | ||||
| 			} else { | ||||
| 				keep_parsing = false; | ||||
| 			} | ||||
| 			break; | ||||
| 		case PARSER_SECOND: | ||||
| 			if (*ptr == '0') { | ||||
| 				result->zero_pad = true; | ||||
| 				advance_ptr++; | ||||
| 				advance_ptr = true; | ||||
| 				next_state = PARSER_NUM; | ||||
| 				current_token = ptr + 1; | ||||
| 				token_length = 0; | ||||
| 			} else if (*ptr > '9' || *ptr < '0') { | ||||
| 				/* Specifier */ | ||||
| 				next_state = PARSER_SPECIFIER; | ||||
|   | ||||
							
								
								
									
										11
									
								
								test/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								test/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| project(simple-printf-test) | ||||
|  | ||||
| add_subdirectory(catch2 EXCLUDE_FROM_ALL) | ||||
|  | ||||
| add_custom_target(test "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}" "-r compact" "-s" DEPENDS ${PROJECT_NAME}) | ||||
|  | ||||
| include_directories("${CMAKE_CURRENT_SOURCE_DIR}/catch-framework") | ||||
| aux_source_directory("src" TEST_SOURCES) | ||||
|  | ||||
| add_executable(${PROJECT_NAME} EXCLUDE_FROM_ALL ${TEST_SOURCES}) | ||||
| target_link_libraries(${PROJECT_NAME} PRIVATE simple-printf Catch2::Catch2WithMain) | ||||
							
								
								
									
										114
									
								
								test/src/tests.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								test/src/tests.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,114 @@ | ||||
| #include <array> | ||||
| #include <catch2/catch_test_macros.hpp> | ||||
| #include <cstdio> | ||||
| #include <string> | ||||
| #include <iostream> | ||||
|  | ||||
| extern "C" { | ||||
| #include <simple-printf/simple-printf.h> | ||||
| } | ||||
|  | ||||
| TEST_CASE("Parse Invalid Specifier") | ||||
| { | ||||
| 	const char *tst = "%m"; | ||||
| 	const char *tst2 = "%45m"; | ||||
| 	const char *tst3 = "%014m"; | ||||
| 	struct format_specifier result; | ||||
|  | ||||
|         parse_format_specifier(tst, &result); | ||||
|         REQUIRE(result.specifier == FMT_INVALID); | ||||
|  | ||||
| 	parse_format_specifier(tst, &result); | ||||
|         REQUIRE(result.specifier == FMT_INVALID); | ||||
|  | ||||
| 	parse_format_specifier(tst, &result); | ||||
|         REQUIRE(result.specifier == FMT_INVALID); | ||||
| } | ||||
|  | ||||
| TEST_CASE("Parse Format Specifier c", "Char") | ||||
| { | ||||
| 	const char *tst = "%c"; | ||||
| 	struct format_specifier result; | ||||
|  | ||||
| 	parse_format_specifier(tst, &result); | ||||
|  | ||||
| 	REQUIRE(result.length_of_provided_specifier == 2); | ||||
| 	REQUIRE(result.specifier == FMT_CHAR); | ||||
| 	REQUIRE(result.zero_pad == false); | ||||
| } | ||||
|  | ||||
| TEST_CASE("Parse Format Specifier s", "String") | ||||
| { | ||||
| 	const char *tst = "%s"; | ||||
| 	struct format_specifier result; | ||||
|  | ||||
| 	parse_format_specifier(tst, &result); | ||||
|  | ||||
| 	REQUIRE(result.length_of_provided_specifier == 2); | ||||
| 	REQUIRE(result.specifier == FMT_STRING); | ||||
|         REQUIRE(result.length == 0); | ||||
| 	REQUIRE(result.zero_pad == false); | ||||
| } | ||||
|  | ||||
| TEST_CASE("Parse Format Specifier 04s", "String") | ||||
| { | ||||
| 	const char *tst = "%04s"; | ||||
| 	struct format_specifier result; | ||||
|  | ||||
| 	parse_format_specifier(tst, &result); | ||||
|  | ||||
| 	REQUIRE(result.specifier == FMT_STRING); | ||||
|         REQUIRE(result.length_of_provided_specifier == 4); | ||||
|         REQUIRE(result.length == 4); | ||||
| 	REQUIRE(result.zero_pad == true); | ||||
| } | ||||
|  | ||||
| TEST_CASE("Parse Format Specifier 08x", "hex") | ||||
| { | ||||
| 	const char *tst = "%08x"; | ||||
| 	struct format_specifier result; | ||||
|  | ||||
| 	parse_format_specifier(tst, &result); | ||||
|  | ||||
| 	REQUIRE(result.specifier == FMT_HEXUINT); | ||||
|         REQUIRE(result.length_of_provided_specifier == 4); | ||||
|         REQUIRE(result.length == 8); | ||||
| 	REQUIRE(result.zero_pad == true); | ||||
| } | ||||
|  | ||||
| TEST_CASE("Parse Format Specifier 08x with stuff", "hex") | ||||
| { | ||||
| 	const std::array<std::string, 8> tst_arr  { | ||||
|                 std::move(std::string("%08xff458578")), | ||||
|                 std::move(std::string("%08x45578")), | ||||
|                 std::move(std::string("%08xx458578")), | ||||
|                 std::move(std::string("%08x44458578")), | ||||
|                 std::move(std::string("%08x45sdf458578")), | ||||
|                 std::move(std::string("%08xsfsdf4558578")), | ||||
|                 std::move(std::string("%08xX")), | ||||
|                 std::move(std::string("%08xdsda")) | ||||
|         };	 | ||||
|          | ||||
|         struct format_specifier result; | ||||
|  | ||||
|         for (const auto &tst : tst_arr) { | ||||
|                 parse_format_specifier(tst.c_str(), &result); | ||||
|                 REQUIRE(result.specifier == FMT_HEXUINT); | ||||
|                 REQUIRE(result.length_of_provided_specifier == 4); | ||||
|                 REQUIRE(result.length == 8); | ||||
|                 REQUIRE(result.zero_pad == true); | ||||
|         } | ||||
| } | ||||
|  | ||||
| TEST_CASE("Parse Percent spefifier") | ||||
| { | ||||
| 	const char *tst = "%%"; | ||||
| 	struct format_specifier result; | ||||
|  | ||||
| 	parse_format_specifier(tst, &result); | ||||
|  | ||||
| 	REQUIRE(result.specifier == FMT_PERCENT); | ||||
|         REQUIRE(result.length_of_provided_specifier == 2); | ||||
|         REQUIRE(result.length == 0); | ||||
| 	REQUIRE(result.zero_pad == false); | ||||
| } | ||||
		Reference in New Issue
	
	Block a user