Files
openvehicle-api/tests/unit_tests/idl_compiler/compiler_test.cpp
tompzf 6ed4b1534e Precommit (#1)
* first commit

* cleanup
2025-11-04 13:28:06 +01:00

556 lines
13 KiB
C++

#include "includes.h"
#include "parser_test.h"
#include "../../../sdv_executables/sdv_idl_compiler/parser.h"
#include "../../../sdv_executables/sdv_idl_compiler/generator/context.cpp"
#include "../../../sdv_executables/sdv_idl_compiler/generator/ps_class_generator_base.cpp"
#include "../../../sdv_executables/sdv_idl_compiler/generator/definition_generator.cpp"
#include "../../../sdv_executables/sdv_idl_compiler/generator/proxy_generator.cpp"
#include "../../../sdv_executables/sdv_idl_compiler/generator/stub_generator.cpp"
#include "../../../sdv_executables/sdv_idl_compiler/generator/serdes_generator.cpp"
#include "../../../sdv_executables/sdv_idl_compiler/generator/cmake_generator.cpp"
/**
* @brief IDL file management class.
*/
class CIdlFile
{
public:
/**
* @brief Constructor
* @param[in] rssIdlCode Reference to the string containing the code to store in the IDL file.
*/
CIdlFile(const std::string& rssIdlCode) : m_pathIdl(std::filesystem::current_path() / "test.idl")
{
// Start clean
CleanUp();
// Create a stream and write the code.
std::ofstream fstreamIdlFile(m_pathIdl.c_str());
fstreamIdlFile << rssIdlCode;
fstreamIdlFile.close();
}
/**
* @brief Destructor
*/
~CIdlFile()
{
CleanUp();
}
/**
* @brief Get the full path to the IDL file.
* @return The path.
*/
std::filesystem::path PathIdl() const { return m_pathIdl; }
/**
* @brief Get the header file content (if one was created).
* @return The string containing the header file content.
*/
std::string Header() const
{
std::filesystem::path pathHdr = m_pathIdl;
pathHdr.replace_extension(".h");
std::ifstream fstream(pathHdr.c_str());
std::stringstream sstreamContent;
sstreamContent << fstream.rdbuf();
return sstreamContent.str();
}
private:
/**
* @brief Clean up before and after the compilation.
*/
void CleanUp()
{
try
{
if (std::filesystem::exists(m_pathIdl))
std::filesystem::remove(m_pathIdl);
} catch (const std::filesystem::filesystem_error&)
{}
std::filesystem::path pathHdr = m_pathIdl;
pathHdr.replace_extension(".h");
try
{
if (std::filesystem::exists(pathHdr))
std::filesystem::remove(pathHdr);
} catch (const std::filesystem::filesystem_error&)
{}
std::filesystem::path pathPSDir = m_pathIdl;
pathPSDir.remove_filename();
pathPSDir /= "ps";
if (std::filesystem::exists(pathPSDir))
{
try
{
std::filesystem::remove_all(pathPSDir);
} catch (const std::filesystem::filesystem_error&)
{}
}
std::filesystem::path pathSerDesDir = m_pathIdl;
pathSerDesDir.remove_filename();
pathSerDesDir /= "serdes";
if (std::filesystem::exists(pathSerDesDir))
{
try
{
std::filesystem::remove_all(pathSerDesDir);
} catch (const std::filesystem::filesystem_error&)
{}
}
}
std::filesystem::path m_pathIdl; ///< Path to the IDL file
};
/**
* @brief Class managing a test compilation.
*/
class CTestCompile : public CIdlFile
{
public:
/**
* @brief Constructor triggering a test compilation using the provided code.
* @param[in] rssIdlCode Reference to the IDL code to compile.
*/
CTestCompile(const std::string& rssIdlCode) : CIdlFile(rssIdlCode)
{
CIdlCompilerEnvironment environment;
try
{
// Parse file
CParser parser(PathIdl(), environment);
parser.Parse();
// Generate definition
CDefinitionGenerator defgen(&parser);
defgen.Generate();
// Proxy code
CProxyGenerator proxygen(&parser);
proxygen.Generate();
// Stub code
CStubGenerator stubgen(&parser);
stubgen.Generate();
// Serdes code
CSerdesGenerator serdesgen(&parser);
serdesgen.Generate();
// CMake code generation
CIdlCompilerCMakeGenerator cmakegen(&parser);
cmakegen.Generate("proxystub");
}
catch (const CCompileException& rexcept)
{
std::stringstream sstreamError;
if (!rexcept.GetPath().empty())
std::cout << rexcept.GetPath() << "(line=" << rexcept.GetLineNo() << ", col=" << rexcept.GetColNo() <<
") error: " << rexcept.GetReason() << std::endl;
else
std::cout << "error: " << rexcept.GetReason() << std::endl;
return;
}
// Compilation was successful
m_bResult = true;
}
/**
* @brief Return the result.
*/
operator bool() const
{
return m_bResult;
}
private:
bool m_bResult = false; ///< The result value of the test.
};
using CCompilerTest = CParserTest;
TEST_F(CCompilerTest, EmptyFile)
{
// Tests bug fix: #380794
EXPECT_TRUE(CTestCompile(""));
EXPECT_TRUE(CTestCompile("/// @file Empty file without any definitions"));
}
TEST_F(CCompilerTest, SkipCodePreCompiled)
{
// Partially skip code
EXPECT_TRUE(CTestCompile(R"code(/// @file Skip code by using a conditional pre-compile statement
struct S1
{};
#if 0
module mod
{};
#endif
struct S2
{};)code"));
// Tests bug fix: #380794
// Completely skip code
EXPECT_TRUE(CTestCompile(R"code(/// @file Skip complete file by using a conditional pre-compile statement
#if 0
module mod
{};
#endif)code"));
}
TEST_F(CCompilerTest, SkipCodeCommenting)
{
// Partially skip code
EXPECT_TRUE(CTestCompile(R"code(/// @file Skip code by using a commented out code
struct S1
{};
//module mod
//{};
struct S2
{};)code"));
// Tests bug fix: #380794
// Completely skip code
EXPECT_TRUE(CTestCompile(R"code(/// @file Skip code by using a commented out code
//struct S1
//{};
//module mod
//{};
//struct S2
//{};)code"));
// Tests bug fix: #380790
// Skip large chunk
std::stringstream sstream;
sstream << R"code(/// @file Skip code by using a commented out code
struct S1
{};
)code";
for (size_t n = 0; n < 20000; n++)
{
sstream << R"code(// struct STest {
// this is a test with lots of comments
// which should be skipped and not be processed
// and should not lead to any unexpected behaviour
// like crashes or giant comment blocks for the definition
// following...
// };
)code";
}
sstream << R"code(
struct S2
{};)code";
EXPECT_TRUE(CTestCompile(sstream.str()));
}
TEST_F(CCompilerTest, GeneratedCodeWithTypedefOfArray)
{
const char szIdl[] = R"code(struct LidarPointField {};
const int32 maxLidarPoints = 32;
typedef LidarPointField LidarPointsField_t;
typedef LidarPointField LidarPointsAry[maxLidarPoints];
struct LidarPoints
{
uint32 pointNumber;
LidarPointsField_t point;
LidarPointsAry lidarPointsList;
};)code";
const char szHdr[] = R"code(struct LidarPointField{};
static const int32_t maxLidarPoints = 32;
typedef LidarPointField LidarPointsField_t;
typedef LidarPointField LidarPointsAry[maxLidarPoints];
struct LidarPoints
{
uint32_t pointNumber;
LidarPointsField_t point;
LidarPointsAry lidarPointsList;
};)code";
// Tests bug fix: #385982
CTestCompile test(szIdl);
EXPECT_TRUE(test);
EXPECT_CPPEQ(test.Header(), szHdr);
}
TEST_F(CCompilerTest, ExplicitSpecifyModule)
{
const char szIdl[] = R"code(module MyAPI
{
struct VehSpd {};
module interfaces
{
struct VehSpd
{
MyAPI::VehSpd notifyStatus;
};
};
};)code";
const char szHdr[] = R"code(namespace MyAPI
{
struct VehSpd
{};
namespace interfaces
{
struct VehSpd
{
MyAPI::VehSpd notifyStatus;
};
}
}
)code";
// Tests bug fix: #384857
CTestCompile test(szIdl);
EXPECT_TRUE(test);
EXPECT_CPPEQ(test.Header(), szHdr);
}
// NOTE 11.04.2025: The C++ comparison function is not working correctly.
TEST_F(CCompilerTest, DISABLED_ForwardDeclUnion)
{
const char szIdlTypeBased[] = R"code(union UTest;
/**
* @brief Standard union based on a integer.
*/
union UTest switch (uint32)
{
case 10: boolean bVal; ///< Bool value
case 20: uint64 uiVal; ///< 64-bit int value
case 30: float fVal; ///< Float value
default: string ssVal; ///< String value
};)code";
const char szHdrTypeBased[] = R"code(struct UTest;
struct UTest;
/**
* @brief Standard union based on a integer.
*/
struct UTest
{
/** Constructor */
UTest()
{
construct_switch_value();
}
/** Destructor */
~UTest()
{
destruct_switch_value();
}
private:
/** Constructor helper function for switch_value */
void construct_switch_value(uint32_t val = uint32_t{})
{
switch_value = val;
switch (val)
{
case 10:
new (&bVal) bool;
break;
case 20:
new (&uiVal) uint64_t;
break;
case 30:
new (&fVal) float;
break;
default:
new (&ssVal) sdv::string;
break;
}
}
/** Destructor helper function for switch_value */
void destruct_switch_value()
{
switch (switch_value)
{
case 10:
break;
case 20:
break;
case 30:
break;
default:
ssVal.~string_base();
break;
}
}
public:
/** Set the switch type for the union UTest */
void switch_to(uint32_t val)
{
// Anything to do?
if (switch_value == val) return;
// Assign the new value...
switch_value = val;
// Destruct and construct switch_value...
destruct_switch_value();
construct_switch_value(val);
}
/** Get the switch value */
uint32_t get_switch() const
{
return switch_value;
}
private:
uint32_t switch_value; ///< Union switch variable.
public:
union /*switch(switch_value)*/
{
// case 10:
/// Bool value
bool bVal;
// case 20:
/// 64-bit int value
uint64_t uiVal;
// case 30:
/// Float value
float fVal;
// default
/// String value
sdv::string ssVal;
};
};
)code";
// Tests bug fix: #380792
CTestCompile testTypeBased(szIdlTypeBased);
EXPECT_TRUE(testTypeBased);
EXPECT_CPPEQ(testTypeBased.Header(), szHdrTypeBased);
const char szIdlVarBased[] = R"code(struct S {
// Forward declaration
union UTest;
int32 iVal;
/**
* @brief Standard union based on a integer value.
*/
union UTest switch (iVal)
{
case 10: boolean bVal; ///< Bool value
case 20: uint64 uiVal; ///< 64-bit int value
case 30: float fVal; ///< Float value
default: char cVal; ///< Character value
};
};)code";
const char szHdrVarBased[] = R"code(struct S {
union UTest;
int32_t iVal;
union UTest
{
UTest() {}
~UTest() {}
bool bVal;
uint64_t uiVal;
float fVal;
char cVal;
};
};
)code";
// Tests bug fix: #380792
CTestCompile testVarBased(szIdlVarBased);
EXPECT_TRUE(testVarBased);
EXPECT_CPPEQ(testVarBased.Header(), szHdrVarBased);
}
// NOTE 11.04.2025: The C++ comparison function is not working correctly.
TEST_F(CCompilerTest, DISABLED_UnionWithComplexMembers)
{
const char szIdlTypeBased[] = R"code(/**
* @brief Standard union based on a integer.
*/
union UTest switch (uint32)
{
case 10: boolean bVal; ///< Bool value
case 20: uint64 uiVal; ///< 64-bit int value
case 30: float fVal; ///< Float value
default: string ssVal; ///< String value
};)code";
const char szHdrTypeBased[] = R"code(struct UTest
{
UTest(uint32_t val = 10)
{
switch_value = val;
switch (val)
{
case 10: new (&bVal) bool; break;
case 20: new (&uiVal) uint64_t; break;
case 30: new (&fVal) float; break;
default: new (&ssVal) sdv::string; break;
}
}
~UTest()
{
switch (switch_value)
{
case 10: break;
case 20: break;
case 30: break;
default: ssVal.~string_base(); break;
}
}
void switch_to(uint32_t val)
{
if (val == switch_value) return;
this->~UTest();
new (this) UTest(val);
}
uint32_t switch_value;
union
{
bool bVal;
uint64_t uiVal;
float fVal;
sdv::string ssVal;
};
};
)code";
// Tests bug fix: #380792
CTestCompile testTypeBased(szIdlTypeBased);
EXPECT_TRUE(testTypeBased);
EXPECT_CPPEQ(testTypeBased.Header(), szHdrTypeBased);
}