Skip to content

Commit

Permalink
Test verification of expected examples (#34)
Browse files Browse the repository at this point in the history
We generate and pass the tests for verifying expected examples. The
deficiencies in regex standard library block us from running
the verification of unexpected examples.
  • Loading branch information
mristin authored Mar 15, 2024
1 parent d2bb09f commit f0f1849
Show file tree
Hide file tree
Showing 1,236 changed files with 2,471 additions and 103 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ if (${BUILD_TESTS})
# region Verification
add_executable(
test_verification
test/test_verification.cpp
test/test_verification.generated.cpp
)
target_link_libraries(
test_verification
Expand Down
211 changes: 211 additions & 0 deletions dev_scripts/test_codegen/generate_test_verification.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
"""Generate the code to test the verification of both the positives and negatives."""

import io
import os
import pathlib
import sys
from typing import List

import aas_core_codegen.naming
from aas_core_codegen.common import Stripped, Identifier
from aas_core_codegen.cpp import (
naming as cpp_naming,
common as cpp_common,
)
from aas_core_codegen.cpp.common import (
INDENT as I,
INDENT2 as II,
INDENT3 as III,
INDENT4 as IIII,
INDENT5 as IIIII,
INDENT6 as IIIIII,
)

import test_codegen.common
import test_codegen.test_data_io


def main() -> int:
"""Execute the main routine."""
symbol_table = test_codegen.common.load_symbol_table()

this_path = pathlib.Path(os.path.realpath(__file__))
repo_root = this_path.parent.parent.parent

warning = test_codegen.common.generate_warning_comment(
this_path.relative_to(repo_root)
)

# noinspection PyListCreation
blocks = [
warning,
Stripped(
"""\
#include "./common.hpp"
#include "./common_xmlization.hpp"
#include <aas_core/aas_3_0/verification.hpp>
#include <aas_core/aas_3_0/xmlization.hpp>
#define CATCH_CONFIG_MAIN
#include <catch2/catch.hpp>
namespace aas = aas_core::aas_3_0;"""
),
Stripped(
f"""\
void AssertNoVerificationError(
{I}const std::filesystem::path& xml_path
) {{
{I}std::shared_ptr<
{II}aas::types::IClass
{I}> instance(
{II}test::common::xmlization::MustDeserializeFile(xml_path)
{I});
{I}std::vector<std::string> error_messages;
{I}for (
{II}const aas::verification::Error &error
{II}: aas::verification::RecursiveVerification(instance)
{I}) {{
{II}error_messages.emplace_back(
{III}aas::common::Concat(
{IIII}aas::common::WstringToUtf8(error.path.ToWstring()),
{IIII}": ",
{IIII}aas::common::WstringToUtf8(error.cause)
{III})
{II});
{I}}}
{I}if (!error_messages.empty()) {{
{II}std::vector<std::string> parts;
{II}parts.emplace_back("Expected no error messages from ");
{II}parts.emplace_back(xml_path.string());
{II}parts.emplace_back(", but got:\\n");
{II}for (std::string& error_message : error_messages) {{
{III}parts.emplace_back(error_message);
{II}}}
{II}INFO(test::common::JoinStrings(parts, ""))
{II}CHECK(error_messages.empty());
{I}}}
}}"""
),
Stripped(
f"""\
const std::filesystem::path& DetermineXmlDir() {{
{I}static aas::common::optional<std::filesystem::path> result;
{I}if (!result.has_value()) {{
{II}result = test::common::DetermineTestDataDir() / "Xml";
{I}}}
{I}return *result;
}}"""
),
Stripped(
f"""\
// NOTE (mristin):
// We test later in the further tests many more values, but the following unit tests
// make the debugging a bit easier."""
),
Stripped(
f"""\
// region Manual tests
// NOTE (mristin):
// We test later in the further tests many more values, but the following unit tests
// make the debugging a bit easier.
TEST_CASE("Test IsXsDate on a common value") {{
{I}CHECK(aas::verification::IsXsDate(L"2022-04-01-02:00"));
}}
TEST_CASE("Test IsXsDate on a large negative year") {{
{I}CHECK(aas::verification::IsXsDate(L"-12345678901234567890123456789012345678901234567890-04-01"));
}}
TEST_CASE("Test IsXsDate on a BC 5 as a leap year") {{
{I}CHECK(aas::verification::IsXsDate(L"-0005-02-29"));
}}
TEST_CASE("Test IsXsDateTime") {{
{I}CHECK(aas::verification::IsXsDateTime(L"-0811-10-21T24:00:00.000000Z"));
}}
TEST_CASE("Test IsXsDouble") {{
{I}CHECK(aas::verification::IsXsDouble(L"+76E-86"));
}}
// endregion Manual tests"""
),
] # type: List[Stripped]

environment_cls = symbol_table.must_find_concrete_class(Identifier("Environment"))

for cls in symbol_table.concrete_classes:
# fmt: off
container_cls = (
test_codegen.test_data_io.determine_container_class(
cls=cls,
test_data_dir=repo_root / "test_data",
environment_cls=environment_cls
)
)
# fmt: on

xml_class_name = aas_core_codegen.naming.xml_class_name(cls.name)

# NOTE (mristin):
# So far, we do not really have a convention how to generate the contained-in
# directory name, so we assume that we use camel case with a model type.
container_model_type = aas_core_codegen.naming.json_model_type(
container_cls.name
)
contained_in_dir_name = (
"SelfContained"
if container_cls is cls
else f"ContainedIn{container_model_type}"
)

cls_name = cpp_naming.class_name(cls.name)

blocks.append(
Stripped(
f"""\
TEST_CASE("Test verification of a valid {cls_name}") {{
{I}const std::deque<std::filesystem::path> paths(
{II}test::common::FindFilesBySuffixRecursively(
{III}DetermineXmlDir()
{IIII}/ {cpp_common.string_literal(contained_in_dir_name)}
{IIII}/ "Expected"
{IIII}/ {cpp_common.string_literal(xml_class_name)},
{III}".xml"
{II})
{I});
{I}for (const std::filesystem::path& path : paths) {{
{II}AssertNoVerificationError(path);
{I}}}
}}"""
)
)

blocks.append(warning)

writer = io.StringIO()
for i, block in enumerate(blocks):
if i > 0:
writer.write("\n\n")

writer.write(block)

writer.write("\n")

target_pth = repo_root / "test/test_verification.generated.cpp"
target_pth.write_text(writer.getvalue(), encoding="utf-8")

return 0


if __name__ == "__main__":
sys.exit(main())
Original file line number Diff line number Diff line change
Expand Up @@ -53,47 +53,16 @@ def main() -> int:
Stripped(
f"""\
void AssertRoundTrip(
{I}const std::filesystem::path &path
{I}const std::filesystem::path& path
) {{
{I}std::ifstream ifs(path, std::ios::binary);
{I}aas::common::expected<
{II}std::shared_ptr<aas::types::IClass>,
{II}aas::xmlization::DeserializationError
{I}> deserialized = aas::xmlization::From(
{II}ifs
{I}std::shared_ptr<
{II}aas::types::IClass
{I}> deserialized(
{II}test::common::xmlization::MustDeserializeFile(path)
{I});
{I}if (ifs.bad()) {{
{II}throw std::runtime_error(
{III}aas::common::Concat(
{IIII}"The file stream is in the bad mode after "
{IIII}"reading and parsing the file as XML: ",
{IIII}path.string()
{III})
{II});
{I}}}
{I}if (!deserialized.has_value()) {{
{II}INFO(
{III}aas::common::Concat(
{IIII}"Failed to de-serialize from ",
{IIII}path.string(),
{IIII}": ",
{IIII}aas::common::WstringToUtf8(
{IIIII}deserialized.error().path.ToWstring()
{IIII}),
{IIII}": ",
{IIII}aas::common::WstringToUtf8(
{IIIII}deserialized.error().cause
{IIII})
{III})
{II})
{II}REQUIRE(deserialized.has_value());
{I}}}
{I}std::stringstream ss;
{I}aas::xmlization::Serialize(*deserialized.value(), {{}}, ss);
{I}aas::xmlization::Serialize(*deserialized, {{}}, ss);
{I}std::string expected_xml = test::common::MustReadString(path);
Expand Down
44 changes: 44 additions & 0 deletions test/common_xmlization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,50 @@ std::string CanonicalizeXml(
return result;
}

std::shared_ptr<
aas_core::aas_3_0::types::IClass
> MustDeserializeFile(
const std::filesystem::path& path
) {
std::ifstream ifs(path, std::ios::binary);

aas::common::expected<
std::shared_ptr<aas::types::IClass>,
aas::xmlization::DeserializationError
> deserialized = aas::xmlization::From(
ifs
);

if (ifs.bad()) {
throw std::runtime_error(
aas::common::Concat(
"The file stream is in the bad mode after "
"reading and parsing the file as XML: ",
path.string()
)
);
}

if (!deserialized.has_value()) {
throw std::runtime_error(
aas::common::Concat(
"Failed to de-serialize from ",
path.string(),
": ",
aas::common::WstringToUtf8(
deserialized.error().path.ToWstring()
),
": ",
aas::common::WstringToUtf8(
deserialized.error().cause
)
)
);
}

return std::move(deserialized.value());
}

} // namespace xmlization
} // namespace common
} // namespace test
6 changes: 6 additions & 0 deletions test/common_xmlization.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ std::string CanonicalizeXml(
const std::string& xml
);

std::shared_ptr<
aas_core::aas_3_0::types::IClass
> MustDeserializeFile(
const std::filesystem::path& path
);

} // namespace xmlization
} // namespace common
} // namespace test
Expand Down
28 changes: 0 additions & 28 deletions test/test_verification.cpp

This file was deleted.

Loading

0 comments on commit f0f1849

Please sign in to comment.