// SPDX-License-Identifier: LGPL-2.1-or-later #include #include "Base/Quantity.h" #include "App/Application.h" #include "App/Document.h" #include "App/DocumentObject.h" #include "App/Expression.h" #include "App/ExpressionParser.h" #include "src/App/InitApplication.h" // clang-format off class ExpressionParserTest: public ::testing::Test { protected: static void SetUpTestSuite() { tests::initApplication(); } void SetUp() override { _doc_name = App::GetApplication().getUniqueDocumentName("test"); _this_doc = App::GetApplication().newDocument(_doc_name.c_str(), "testUser"); _this_obj = _this_doc -> addObject("Sketcher::SketchObject"); } void TearDown() override { App::GetApplication().closeDocument(_doc_name.c_str()); } std::string doc_name() { return _doc_name; } App::Document* this_doc() { return _this_doc; } App::DocumentObject* this_obj() { return _this_obj; } Base::Quantity parse_expression_text_as_quantity(const char* expression_text) { const auto expression = App::ExpressionParser::parse(this_obj(), expression_text); return App::any_cast(expression->getValueAsAny()); } Base::Quantity parse_quantity_text_as_quantity(const char* quantity_text) { return Base::Quantity::parse(quantity_text); } private: std::string _doc_name; App::Document* _this_doc {}; App::DocumentObject* _this_obj {}; }; // https://github.com/FreeCAD/FreeCAD/issues/11965 TEST_F(ExpressionParserTest, functionPARSEQUANT) { EXPECT_ANY_THROW(App::ExpressionParser::parse(this_obj(), "parsequant()")) << "should not parse empty"; EXPECT_NO_THROW(App::ExpressionParser::parse(this_obj(), "parsequant(1 mm)")) << "should parse simple quantity"; EXPECT_NO_THROW(App::ExpressionParser::parse(this_obj(), "parsequant(<<(1 + 2) m>>)")) << "should parse literal quantity"; EXPECT_NO_THROW(App::ExpressionParser::parse(this_obj(), "parsequant(str(1 m + 2 mm))")) << "should parse str-function quantity"; EXPECT_ANY_THROW(parse_quantity_text_as_quantity("parsequant(1 mm)")) << "should not treat parsequant-function as quantity"; EXPECT_EQ(parse_quantity_text_as_quantity("1 mm"), parse_quantity_text_as_quantity("1 mm")) << "equality sanity check"; EXPECT_NE(parse_quantity_text_as_quantity("1 mm"), parse_quantity_text_as_quantity("2 mm")) << "inequality sanity check"; std::array, 12> expression_vs_quantity_list = {{ // length { "1 mm", "1 mm" }, { "parsequant(1 mm)", "1 mm" }, { "parsequant(<<(1 + 2) m>>)", "3000 mm" }, { "parsequant(str(1 m + 2 mm))", "1002 mm"}, // angle { "10 deg", "10 deg" }, { "parsequant(10 deg)", "10 deg" }, { "parsequant(<<(10 + 20) deg>>)", "30 deg" }, { "parsequant(str(10 deg + 20 deg))", "30 deg" }, // mass { "10 g", "10 g" }, { "parsequant(10 g)", "10 g" }, { "parsequant(<<(10 + 20) kg>>)", "30000 g" }, { "parsequant(str(10 kg + 20010 g))", "30.01 kg" }, }}; for ( const auto& expression_vs_quantity_pair : expression_vs_quantity_list ) { auto expression_text = expression_vs_quantity_pair.first; auto quantity_text = expression_vs_quantity_pair.second; auto expression_result = parse_expression_text_as_quantity(expression_text); auto quantity_result = parse_quantity_text_as_quantity(quantity_text); EXPECT_EQ(expression_result, quantity_result) << "mismatch:" " expression_text='" + std::string(expression_text) + "'" " quantity_text='" + std::string(quantity_text) + "'" " expression_representation='" + expression_result.getUserString() + "'" " quantity_representation='" + quantity_result.getUserString() + "'" ; } } TEST_F(ExpressionParserTest, isTokenAConstant) { std::array constants {"pi", "e", "True", "False", "true", "false", "None"}; for (const auto & constant : constants) { EXPECT_TRUE(App::ExpressionParser::isTokenAConstant(constant)) << "\"" << constant << "\" did not evaluate as a constant"; } std::array notConstants {"PI", "E", "TRUE", "FALSE", "NONE", "none"}; for (const auto & nonConstant : notConstants) { EXPECT_FALSE(App::ExpressionParser::isTokenAConstant(nonConstant)) << "\"" << nonConstant << "\" evaluated as a constant"; } } // clang-format on