Add CMake macro and actually test the generated code
This commit is contained in:
parent
14428c8890
commit
8679263f09
|
@ -27,8 +27,10 @@ else()
|
|||
message(FATAL_ERROR "Specified directory for c++utilities sources \"${BUNDLED_CPP_UTILITIES_PATH}\" does not exist.")
|
||||
endif()
|
||||
|
||||
# add helper library for using RapidJSON
|
||||
# add header-only library containing JSONSerializable and helper for using RapidJSON
|
||||
add_subdirectory(lib)
|
||||
# allow inclusion of CMake modules from that lib in other parts of the project
|
||||
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/lib/cmake/modules" "${CMAKE_MODULE_PATH}")
|
||||
|
||||
# add code generator
|
||||
add_subdirectory(moc)
|
||||
|
|
|
@ -16,6 +16,9 @@ set(TEST_SRC_FILES
|
|||
tests/cppunit.cpp
|
||||
tests/jsonreflector.cpp
|
||||
)
|
||||
set(CMAKE_MODULE_FILES
|
||||
cmake/modules/ReflectionGenerator.cmake
|
||||
)
|
||||
set(DOC_FILES
|
||||
README.md
|
||||
)
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
cmake_minimum_required(VERSION 3.3.0 FATAL_ERROR)
|
||||
|
||||
# prevent multiple inclusion
|
||||
if(DEFINED REFLECTION_GENERATOR_MODULE_LOADED)
|
||||
return()
|
||||
endif()
|
||||
set(REFLECTION_GENERATOR_MODULE_LOADED YES)
|
||||
|
||||
# find code generator
|
||||
set(REFLECTION_GENERATOR_EXECUTABLE reflective_rapidjson_moc)
|
||||
if(CMAKE_CROSSCOMPILING OR NOT TARGET "${REFLECTION_GENERATOR_EXECUTABLE}")
|
||||
# find "reflective_rapidjson_moc" from path
|
||||
find_program(REFLECTION_GENERATOR_EXECUTABLE "${REFLECTION_GENERATOR_EXECUTABLE}")
|
||||
endif()
|
||||
if(NOT REFLECTION_GENERATOR_EXECUTABLE)
|
||||
message(FATAL_ERROR "Unable to find executable of generator for reflection code.")
|
||||
endif()
|
||||
|
||||
# define helper functions
|
||||
include(CMakeParseArguments)
|
||||
function(add_reflection_generator_invocation)
|
||||
# parse arguments
|
||||
set(OPTIONAL_ARGS)
|
||||
set(ONE_VALUE_ARGS OUTPUT_FILE OUTPUT_NAME)
|
||||
set(MULTI_VALUE_ARGS INPUT_FILES GENERATORS OUTPUT_LISTS)
|
||||
cmake_parse_arguments(ARGS "${OPTIONAL_ARGS}" "${ONE_VALUE_ARGS}" "${MULTI_VALUE_ARGS}" ${ARGN})
|
||||
|
||||
# determine file name or file path if none specified
|
||||
if(OUTPUT_FILE AND OUTPUT_NAME)
|
||||
message(FATAL_ERROR "Specify either OUTPUT_NAME or OUTPUT_FILE but not both.")
|
||||
endif()
|
||||
if(NOT ARGS_OUTPUT_FILE)
|
||||
if(NOT ARGS_OUTPUT_NAME)
|
||||
set(ARGS_OUTPUT_NAME "reflection.h")
|
||||
endif()
|
||||
set(ARGS_OUTPUT_FILE "${CMAKE_CURRENT_BINARY_DIR}/${ARGS_OUTPUT_NAME}")
|
||||
endif()
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT "${ARGS_OUTPUT_FILE}"
|
||||
COMMAND "${REFLECTION_GENERATOR_EXECUTABLE}"
|
||||
-o "${ARGS_OUTPUT_FILE}"
|
||||
-i ${ARGS_INPUT_FILES}
|
||||
-g ${ARGS_GENERATORS}
|
||||
DEPENDS "${ARGS_INPUT_FILES}"
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
COMMENT "Generating reflection code"
|
||||
)
|
||||
|
||||
# append the output file to lists specified via OUTPUT_LISTS
|
||||
if(ARGS_OUTPUT_LISTS)
|
||||
foreach(OUTPUT_LIST ${ARGS_OUTPUT_LISTS})
|
||||
set("${OUTPUT_LIST}" "${${OUTPUT_LIST}};${ARGS_OUTPUT_FILE}" PARENT_SCOPE)
|
||||
endforeach()
|
||||
endif()
|
||||
endfunction()
|
|
@ -39,6 +39,14 @@ list(APPEND PRIVATE_LIBRARIES clangTooling)
|
|||
# also add reflective_rapidjson
|
||||
list(APPEND PRIVATE_LIBRARIES reflective_rapidjson)
|
||||
|
||||
# trigger code generator for tests
|
||||
include(ReflectionGenerator)
|
||||
add_reflection_generator_invocation(
|
||||
INPUT_FILES tests/overall.cpp
|
||||
GENERATORS json
|
||||
OUTPUT_LISTS TEST_SRC_FILES
|
||||
)
|
||||
|
||||
# include modules to apply configuration
|
||||
include(BasicConfig)
|
||||
include(WindowsResources)
|
||||
|
@ -47,4 +55,3 @@ include(TestTarget)
|
|||
include(ShellCompletion)
|
||||
include(Doxygen)
|
||||
include(ConfigHeader)
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "./frontendaction.h"
|
||||
#include "./consumer.h"
|
||||
#include "./generator.h"
|
||||
|
||||
#include <c++utilities/application/global.h>
|
||||
|
||||
|
@ -17,11 +18,13 @@ FrontendAction::CreateASTConsumer(clang::CompilerInstance &compilerInstance, llv
|
|||
{
|
||||
VAR_UNUSED(inputFile)
|
||||
|
||||
// propagate compiler instance to factory
|
||||
m_factory.setCompilerInstance(&compilerInstance);
|
||||
|
||||
// configure frontent, preporocessor and language options
|
||||
clang::FrontendOptions &frontendOpts = compilerInstance.getFrontendOpts();
|
||||
clang::Preprocessor &pp = compilerInstance.getPreprocessor();
|
||||
clang::LangOptions &lngOpts = compilerInstance.getLangOpts();
|
||||
|
||||
// configure frontent, preporocessor and language options
|
||||
frontendOpts.SkipFunctionBodies = true;
|
||||
pp.enableIncrementalProcessing(true);
|
||||
pp.SetSuppressIncludeNotFoundError(true);
|
||||
|
|
|
@ -83,24 +83,24 @@ void JSONSerializationCodeGenerator::generate(ostream &os) const
|
|||
return;
|
||||
}
|
||||
|
||||
// put everything into ReflectiveRapidJSON::Reflector
|
||||
// put everything into namespace ReflectiveRapidJSON::Reflector
|
||||
os << "namespace ReflectiveRapidJSON {\n"
|
||||
"namespace Reflector {\n";
|
||||
"namespace Reflector {\n\n";
|
||||
|
||||
// add push and pull functions for each class, for an example of the resulting
|
||||
// output, see ../lib/tests/jsonserializable.cpp (code under comment "pretend serialization code...")
|
||||
for (const RelevantClass &relevantClass : m_relevantClasses) {
|
||||
// print push method
|
||||
os << "template <> inline void push<::" << relevantClass.qualifiedName << ">(const " << relevantClass.qualifiedName
|
||||
<< " &reflectable, Value::Object &value, Document::AllocatorType &allocator)\n{\n";
|
||||
os << "template <> inline void push<::" << relevantClass.qualifiedName << ">(const ::" << relevantClass.qualifiedName
|
||||
<< " &reflectable, ::RAPIDJSON_NAMESPACE::Value::Object &value, ::RAPIDJSON_NAMESPACE::Document::AllocatorType &allocator)\n{\n";
|
||||
for (const clang::FieldDecl *field : relevantClass.record->fields()) {
|
||||
os << " push(reflectable." << field->getName() << ", \"" << field->getName() << "\", value, allocator);\n";
|
||||
}
|
||||
os << "}\n";
|
||||
|
||||
// print pull method
|
||||
os << "template <> inline void pull<::" << relevantClass.qualifiedName << ">(" << relevantClass.qualifiedName
|
||||
<< " &reflectable, const GenericValue<UTF8<char>>::ConstObject &value)\n{\n";
|
||||
os << "template <> inline void pull<::" << relevantClass.qualifiedName << ">(::" << relevantClass.qualifiedName
|
||||
<< " &reflectable, const ::RAPIDJSON_NAMESPACE::GenericValue<::RAPIDJSON_NAMESPACE::UTF8<char>>::ConstObject &value)\n{\n";
|
||||
for (const clang::FieldDecl *field : relevantClass.record->fields()) {
|
||||
os << " pull(reflectable." << field->getName() << ", \"" << field->getName() << "\", value);\n";
|
||||
}
|
||||
|
@ -126,10 +126,13 @@ CodeFactory::ToolInvocation::ToolInvocation(CodeFactory &factory)
|
|||
fileManager.Retain();
|
||||
}
|
||||
|
||||
CodeFactory::CodeFactory(const char *applicationPath, const std::vector<const char *> &sourceFiles, std::ostream &os)
|
||||
CodeFactory::CodeFactory(
|
||||
const char *applicationPath, const std::vector<const char *> &sourceFiles, const std::vector<const char *> &clangOptions, std::ostream &os)
|
||||
: m_applicationPath(applicationPath)
|
||||
, m_sourceFiles(sourceFiles)
|
||||
, m_clangOptions(clangOptions)
|
||||
, m_os(os)
|
||||
, m_compilerInstance(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -145,8 +148,9 @@ std::vector<string> CodeFactory::makeClangArgs() const
|
|||
static const initializer_list<const char *> flags
|
||||
= { m_applicationPath, "-x", "c++", "-fPIE", "-fPIC", "-Wno-microsoft", "-Wno-pragma-once-outside-header", "-std=c++14", "-fsyntax-only" };
|
||||
vector<string> clangArgs;
|
||||
clangArgs.reserve(flags.size() + m_sourceFiles.size());
|
||||
clangArgs.reserve(flags.size() + m_clangOptions.size() + m_sourceFiles.size());
|
||||
clangArgs.insert(clangArgs.end(), flags.begin(), flags.end());
|
||||
clangArgs.insert(clangArgs.end(), m_clangOptions.cbegin(), m_clangOptions.cend());
|
||||
clangArgs.insert(clangArgs.end(), m_sourceFiles.cbegin(), m_sourceFiles.cend());
|
||||
return clangArgs;
|
||||
}
|
||||
|
|
|
@ -10,16 +10,19 @@ namespace clang {
|
|||
class Decl;
|
||||
class NamedDecl;
|
||||
class CXXRecordDecl;
|
||||
class CompilerInstance;
|
||||
} // namespace clang
|
||||
|
||||
namespace ReflectiveRapidJSON {
|
||||
|
||||
class CodeFactory;
|
||||
|
||||
/*!
|
||||
* \brief The CodeGenerator class is the base for generators used by the CodeFactory class.
|
||||
*/
|
||||
class CodeGenerator {
|
||||
public:
|
||||
CodeGenerator();
|
||||
CodeGenerator(CodeFactory &factory);
|
||||
virtual ~CodeGenerator();
|
||||
|
||||
virtual void addDeclaration(clang::Decl *decl);
|
||||
|
@ -28,20 +31,30 @@ public:
|
|||
virtual void generate(std::ostream &os) const = 0;
|
||||
|
||||
protected:
|
||||
CodeFactory &factory() const;
|
||||
static bool inheritsFromInstantiationOf(clang::CXXRecordDecl *record, const char *templateClass);
|
||||
|
||||
private:
|
||||
CodeFactory &m_factory;
|
||||
};
|
||||
|
||||
inline CodeGenerator::CodeGenerator()
|
||||
inline CodeGenerator::CodeGenerator(CodeFactory &factory)
|
||||
: m_factory(factory)
|
||||
{
|
||||
}
|
||||
|
||||
inline CodeFactory &CodeGenerator::factory() const
|
||||
{
|
||||
return m_factory;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief The JSONSerializationCodeGenerator class generates code for JSON (de)serialization
|
||||
* of objects inheriting from an instantiation of JSONSerializable.
|
||||
*/
|
||||
class JSONSerializationCodeGenerator : public CodeGenerator {
|
||||
public:
|
||||
JSONSerializationCodeGenerator();
|
||||
JSONSerializationCodeGenerator(CodeFactory &factory);
|
||||
|
||||
void addDeclaration(clang::Decl *decl) override;
|
||||
void generate(std::ostream &os) const override;
|
||||
|
@ -57,7 +70,8 @@ private:
|
|||
std::vector<RelevantClass> m_relevantClasses;
|
||||
};
|
||||
|
||||
inline JSONSerializationCodeGenerator::JSONSerializationCodeGenerator()
|
||||
inline JSONSerializationCodeGenerator::JSONSerializationCodeGenerator(CodeFactory &factory)
|
||||
: CodeGenerator(factory)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -75,15 +89,18 @@ inline JSONSerializationCodeGenerator::RelevantClass::RelevantClass(const std::s
|
|||
*/
|
||||
class CodeFactory {
|
||||
public:
|
||||
CodeFactory(const char *applicationPath, const std::vector<const char *> &sourceFiles, std::ostream &os);
|
||||
CodeFactory(
|
||||
const char *applicationPath, const std::vector<const char *> &sourceFiles, const std::vector<const char *> &clangOptions, std::ostream &os);
|
||||
~CodeFactory();
|
||||
|
||||
std::vector<std::unique_ptr<CodeGenerator>> &generators();
|
||||
const std::vector<std::unique_ptr<CodeGenerator>> &generators() const;
|
||||
template <typename GeneratorType> void addGenerator();
|
||||
|
||||
void addDeclaration(clang::Decl *decl);
|
||||
bool readAST();
|
||||
bool generate() const;
|
||||
clang::CompilerInstance *compilerInstance();
|
||||
void setCompilerInstance(clang::CompilerInstance *compilerInstance);
|
||||
|
||||
private:
|
||||
struct ToolInvocation;
|
||||
|
@ -92,14 +109,16 @@ private:
|
|||
|
||||
const char *const m_applicationPath;
|
||||
const std::vector<const char *> &m_sourceFiles;
|
||||
const std::vector<const char *> &m_clangOptions;
|
||||
std::ostream &m_os;
|
||||
std::vector<std::unique_ptr<CodeGenerator>> m_generators;
|
||||
std::unique_ptr<ToolInvocation> m_toolInvocation;
|
||||
clang::CompilerInstance *m_compilerInstance;
|
||||
};
|
||||
|
||||
inline std::vector<std::unique_ptr<CodeGenerator>> &CodeFactory::generators()
|
||||
template <typename GeneratorType> void CodeFactory::addGenerator()
|
||||
{
|
||||
return m_generators;
|
||||
m_generators.emplace_back(std::make_unique<GeneratorType>(*this));
|
||||
}
|
||||
|
||||
inline const std::vector<std::unique_ptr<CodeGenerator>> &CodeFactory::generators() const
|
||||
|
@ -107,6 +126,16 @@ inline const std::vector<std::unique_ptr<CodeGenerator>> &CodeFactory::generator
|
|||
return m_generators;
|
||||
}
|
||||
|
||||
inline clang::CompilerInstance *CodeFactory::compilerInstance()
|
||||
{
|
||||
return m_compilerInstance;
|
||||
}
|
||||
|
||||
inline void CodeFactory::setCompilerInstance(clang::CompilerInstance *compilerInstance)
|
||||
{
|
||||
m_compilerInstance = compilerInstance;
|
||||
}
|
||||
|
||||
} // namespace ReflectiveRapidJSON
|
||||
|
||||
#endif // REFLECTIVE_RAPIDJSON_GENERATOR_H
|
||||
|
|
32
moc/main.cpp
32
moc/main.cpp
|
@ -29,17 +29,16 @@ int main(int argc, char *argv[])
|
|||
Argument inputFilesArg("input-files", 'i', "specifies the input files");
|
||||
inputFilesArg.setValueNames({ "path" });
|
||||
inputFilesArg.setRequiredValueCount(Argument::varValueCount);
|
||||
Argument outputFileArg("output-file", 'o', "specifies the output file");
|
||||
outputFileArg.setValueNames({ "path" });
|
||||
outputFileArg.setRequiredValueCount(1);
|
||||
outputFileArg.setCombinable(true);
|
||||
Argument generatorsArgs("generators", 'g', "specifies the generators (by default all generators are enabled)");
|
||||
generatorsArgs.setValueNames({ "json" });
|
||||
generatorsArgs.setRequiredValueCount(Argument::varValueCount);
|
||||
generatorsArgs.setCombinable(true);
|
||||
ConfigValueArgument outputFileArg("output-file", 'o', "specifies the output file", { "path" });
|
||||
Argument generatorsArg("generators", 'g', "specifies the generators (by default all generators are enabled)");
|
||||
generatorsArg.setValueNames({ "json" });
|
||||
generatorsArg.setPreDefinedCompletionValues({ "json" });
|
||||
generatorsArg.setRequiredValueCount(Argument::varValueCount);
|
||||
generatorsArg.setCombinable(true);
|
||||
ConfigValueArgument clangOptionsArg("clang-opt", 'c', "specifies an argument to be passed to Clang", { "option" });
|
||||
HelpArgument helpArg(parser);
|
||||
NoColorArgument noColorArg;
|
||||
parser.setMainArguments({ &inputFilesArg, &outputFileArg, &noColorArg, &helpArg });
|
||||
parser.setMainArguments({ &inputFilesArg, &outputFileArg, &generatorsArg, &clangOptionsArg, &noColorArg, &helpArg });
|
||||
|
||||
// parse arguments
|
||||
parser.parseArgsOrExit(argc, argv);
|
||||
|
@ -57,21 +56,22 @@ int main(int argc, char *argv[])
|
|||
ofstream outputFile;
|
||||
if (outputFileArg.isPresent()) {
|
||||
outputFile.exceptions(ios_base::badbit | ios_base::failbit);
|
||||
outputFile.open(outputFileArg.values(0).front(), ios_base::out | ios_base::binary);
|
||||
outputFile.open(outputFileArg.values(0).front(), ios_base::out | ios_base::trunc | ios_base::binary);
|
||||
os = &outputFile;
|
||||
} else {
|
||||
os = &cout;
|
||||
}
|
||||
|
||||
// configure code generator
|
||||
CodeFactory factory(parser.executable(), inputFilesArg.values(0), *os);
|
||||
auto &generators(factory.generators());
|
||||
vector<const char *> defaultClangOptions;
|
||||
CodeFactory factory(
|
||||
parser.executable(), inputFilesArg.values(0), clangOptionsArg.isPresent() ? clangOptionsArg.values(0) : defaultClangOptions, *os);
|
||||
// add only specified generators if the --generator argument is present
|
||||
if (generatorsArgs.isPresent()) {
|
||||
if (generatorsArg.isPresent()) {
|
||||
// find and construct generators by name
|
||||
for (vector<const char *> generatorName : generatorsArgs.values(0)) {
|
||||
for (const char *generatorName : generatorsArg.values(0)) {
|
||||
if (!strcmp(generatorName, "json")) {
|
||||
generators.emplace_back(std::make_unique<JSONSerializationCodeGenerator>());
|
||||
factory.addGenerator<JSONSerializationCodeGenerator>();
|
||||
} else {
|
||||
cerr << Phrases::Error << "The specified generator \"" << generatorName << "\" does not exist." << Phrases::EndFlush;
|
||||
return -5;
|
||||
|
@ -79,7 +79,7 @@ int main(int argc, char *argv[])
|
|||
}
|
||||
} else {
|
||||
// add default generators
|
||||
generators.emplace_back(std::make_unique<JSONSerializationCodeGenerator>());
|
||||
factory.addGenerator<JSONSerializationCodeGenerator>();
|
||||
}
|
||||
|
||||
// read AST elements from input files
|
||||
|
|
|
@ -19,11 +19,13 @@ struct Person : public ReflectiveRapidJSON::JSONSerializable<Person>
|
|||
struct NonReflectableClass
|
||||
{
|
||||
int foo;
|
||||
}
|
||||
};
|
||||
|
||||
struct SomeOtherNonReflectableClass : public NonReflectableClass
|
||||
{
|
||||
int bar;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace TestNamespace2 {
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
namespace ReflectiveRapidJSON {
|
||||
namespace Reflector {
|
||||
template <> inline void push<::TestNamespace1::Person>(const TestNamespace1::Person &reflectable, Value::Object &value, Document::AllocatorType &allocator)
|
||||
|
||||
template <> inline void push<::TestNamespace1::Person>(const ::TestNamespace1::Person &reflectable, ::RAPIDJSON_NAMESPACE::Value::Object &value, ::RAPIDJSON_NAMESPACE::Document::AllocatorType &allocator)
|
||||
{
|
||||
push(reflectable.age, "age", value, allocator);
|
||||
push(reflectable.alive, "alive", value, allocator);
|
||||
}
|
||||
template <> inline void pull<::TestNamespace1::Person>(TestNamespace1::Person &reflectable, const GenericValue<UTF8<char>>::ConstObject &value)
|
||||
template <> inline void pull<::TestNamespace1::Person>(::TestNamespace1::Person &reflectable, const ::RAPIDJSON_NAMESPACE::GenericValue<::RAPIDJSON_NAMESPACE::UTF8<char>>::ConstObject &value)
|
||||
{
|
||||
pull(reflectable.age, "age", value);
|
||||
pull(reflectable.alive, "alive", value);
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#include "../generator.h"
|
||||
|
||||
#include "../../lib/jsonserializable.h"
|
||||
|
||||
#include "resources/config.h"
|
||||
|
||||
#include <c++utilities/conversion/stringbuilder.h>
|
||||
|
@ -22,6 +24,16 @@ using namespace TestUtilities;
|
|||
using namespace ConversionUtilities;
|
||||
using namespace ReflectiveRapidJSON;
|
||||
|
||||
/*!
|
||||
* \brief The TestStruct struct inherits from JSONSerializable and should hence have functional fromJson()
|
||||
* and toJson() methods. This is asserted in OverallTests::testIncludingGeneratedHeader();
|
||||
*/
|
||||
struct TestStruct : public JSONSerializable<TestStruct> {
|
||||
int someInt = 0;
|
||||
string someString = "foo";
|
||||
string yetAnotherString = "bar";
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief The OverallTests class tests the overall functionality of the code generator (CLI and generator itself).
|
||||
*/
|
||||
|
@ -29,6 +41,7 @@ class OverallTests : public TestFixture {
|
|||
CPPUNIT_TEST_SUITE(OverallTests);
|
||||
CPPUNIT_TEST(testGeneratorItself);
|
||||
CPPUNIT_TEST(testCLI);
|
||||
CPPUNIT_TEST(testIncludingGeneratedHeader);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
|
@ -37,6 +50,7 @@ public:
|
|||
|
||||
void testGeneratorItself();
|
||||
void testCLI();
|
||||
void testIncludingGeneratedHeader();
|
||||
|
||||
private:
|
||||
vector<string> m_expectedCode;
|
||||
|
@ -81,19 +95,28 @@ void OverallTests::tearDown()
|
|||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Tests whether the generator works by using it directly.
|
||||
*/
|
||||
void OverallTests::testGeneratorItself()
|
||||
{
|
||||
const string inputFilePath(testFilePath("some_structs.h"));
|
||||
const vector<const char *> inputFiles{ inputFilePath.data() };
|
||||
const vector<const char *> clangOptions{};
|
||||
|
||||
stringstream buffer;
|
||||
CodeFactory factory(TestApplication::appPath(), inputFiles, buffer);
|
||||
factory.generators().emplace_back(make_unique<JSONSerializationCodeGenerator>());
|
||||
CodeFactory factory(TestApplication::appPath(), inputFiles, clangOptions, buffer);
|
||||
factory.addGenerator<JSONSerializationCodeGenerator>();
|
||||
CPPUNIT_ASSERT(factory.readAST());
|
||||
CPPUNIT_ASSERT(factory.generate());
|
||||
assertEqualityLinewise(m_expectedCode, toArrayOfLines(buffer.str()));
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Test the generator via CLI.
|
||||
* \remarks Only available under UNIX (like) systems so far, because TESTUTILS_ASSERT_EXEC has not been implemented
|
||||
* for other platforms.
|
||||
*/
|
||||
void OverallTests::testCLI()
|
||||
{
|
||||
#ifdef PLATFORM_UNIX
|
||||
|
@ -105,3 +128,28 @@ void OverallTests::testCLI()
|
|||
assertEqualityLinewise(m_expectedCode, toArrayOfLines(stdout));
|
||||
#endif
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Tests whether the generated reflection code actually works.
|
||||
*/
|
||||
void OverallTests::testIncludingGeneratedHeader()
|
||||
{
|
||||
TestStruct test;
|
||||
test.someInt = 42;
|
||||
test.someString = "the answer";
|
||||
test.yetAnotherString = "but what was the question";
|
||||
const string expectedJSON("{\"someInt\":42,\"someString\":\"the answer\",\"yetAnotherString\":\"but what was the question\"}");
|
||||
|
||||
// test serialization
|
||||
CPPUNIT_ASSERT_EQUAL(expectedJSON, string(test.toJson().GetString()));
|
||||
|
||||
// test deserialization
|
||||
const TestStruct parsedTest(TestStruct::fromJson(expectedJSON));
|
||||
CPPUNIT_ASSERT_EQUAL(test.someInt, parsedTest.someInt);
|
||||
CPPUNIT_ASSERT_EQUAL(test.someString, parsedTest.someString);
|
||||
CPPUNIT_ASSERT_EQUAL(test.yetAnotherString, parsedTest.yetAnotherString);
|
||||
}
|
||||
|
||||
// include file required for reflection of TestStruct; generation of this header is triggered using
|
||||
// the CMake function add_reflection_generator_invocation()
|
||||
#include "reflection.h"
|
||||
|
|
Loading…
Reference in New Issue