• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2024-2025 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #ifndef ES2PANDA_TEST_UTILS_AST_VERIFIER_TEST_H
17 #define ES2PANDA_TEST_UTILS_AST_VERIFIER_TEST_H
18 
19 #include "ast_verifier/ASTVerifier.h"
20 #include "panda_executable_path_getter.h"
21 
22 #include <gtest/gtest.h>
23 
24 namespace ir_alias = ark::es2panda::ir;
25 namespace verifier_alias = ark::es2panda::compiler::ast_verifier;
26 
27 class LSPAPITests;
28 
29 namespace test::utils {
30 
31 // CC-OFFNXT(G.PRE.02) macro to improve readability
32 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
33 #define CONTEXT(...) if (ContextHolder<ContextFromStringExtractor> h {this, __VA_ARGS__}; true)
34 
35 // CC-OFFNXT(G.PRE.02) macro to improve readability
36 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
37 #define CONTEXT_FROM_FILE(...) if (ContextHolder<ContextFromFileExtractor> h {this, __VA_ARGS__}; true)
38 
39 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
40 class AstVerifierTest : public testing::Test, public verifier_alias::InvariantsRegistry {
41 public:
42     AstVerifierTest();
43     NO_COPY_SEMANTIC(AstVerifierTest);
44     NO_MOVE_SEMANTIC(AstVerifierTest);
45     ~AstVerifierTest() override;
46 
Allocator()47     ark::ArenaAllocator *Allocator() const
48     {
49         return allocator_;
50     }
51 
GetChecker()52     auto *GetChecker()
53     {
54         return reinterpret_cast<ark::es2panda::public_lib::Context *>(ctx_)->checker->AsETSChecker();
55     }
56 
GetAst()57     auto *GetAst()
58     {
59         return reinterpret_cast<ir_alias::AstNode *>(impl_->ProgramAst(ctx_, impl_->ContextProgram(ctx_)));
60     }
61 
GetCfg()62     auto *GetCfg()
63     {
64         return reinterpret_cast<ark::es2panda::parser::Program *>(impl_->ContextProgram(ctx_))->GetCFG();
65     }
66 
GetImpl()67     auto *GetImpl()
68     {
69         return impl_;
70     }
71 
GetContext()72     auto *GetContext()
73     {
74         return ctx_;
75     }
76 
AstNodeForEach(void (* func)(es2panda_AstNode *,void *),void * arg)77     void AstNodeForEach(void (*func)(es2panda_AstNode *, void *), void *arg)
78     {
79         impl_->AstNodeForEach(reinterpret_cast<es2panda_AstNode *>(GetAst()), func, arg);
80     }
81 
ContextErrorMessage()82     auto ContextErrorMessage()
83     {
84         return std::string(impl_->ContextErrorMessage(ctx_));
85     }
86 
87     struct ContextFromStringExtractor {
88         ContextFromStringExtractor(AstVerifierTest *fixture, char const *source, char const *fileName = "dummy.ets")
89         {
90             ASSERT(fixture->ctx_ == nullptr);
91             fixture->ctx_ = fixture->impl_->CreateContextFromString(fixture->cfg_, source, fileName);
92         }
93     };
94 
95     struct ContextFromFileExtractor {
ContextFromFileExtractorContextFromFileExtractor96         ContextFromFileExtractor(AstVerifierTest *fixture, char const *fileName)
97         {
98             ASSERT(fixture->ctx_ == nullptr);
99             fixture->ctx_ = fixture->impl_->CreateContextFromFile(fixture->cfg_, fileName);
100         }
101     };
102 
103     template <typename ContextExtractor>
104     class ContextHolder : private ContextExtractor {
105     public:
106         NO_COPY_SEMANTIC(ContextHolder);
107         NO_MOVE_SEMANTIC(ContextHolder);
108 
109         template <typename... ExtractorArgs>
ContextHolder(AstVerifierTest * fixture,es2panda_ContextState state,ExtractorArgs &&...args)110         ContextHolder(AstVerifierTest *fixture, es2panda_ContextState state, ExtractorArgs &&...args)
111             : ContextHolder(fixture, state, state, std::forward<ExtractorArgs>(args)...)
112         {
113         }
114 
115         template <typename... ExtractorArgs>
ContextHolder(AstVerifierTest * fixture,es2panda_ContextState target,es2panda_ContextState expected,ExtractorArgs &&...args)116         ContextHolder(AstVerifierTest *fixture, es2panda_ContextState target, es2panda_ContextState expected,
117                       ExtractorArgs &&...args)
118             : ContextExtractor(fixture, std::forward<ExtractorArgs>(args)...), fixture_ {fixture}
119         {
120             ASSERT(target != ES2PANDA_STATE_ERROR);
121             fixture_->impl_->ProceedToState(fixture_->ctx_, target);
122             EXPECT_EQ(fixture_->impl_->ContextState(fixture_->ctx_), expected)
123                 << fixture_->impl_->ContextErrorMessage(fixture_->ctx_);
124             ;
125         }
126 
~ContextHolder()127         ~ContextHolder()
128         {
129             ASSERT(fixture_->ctx_ != nullptr);
130             fixture_->impl_->DestroyContext(fixture_->ctx_);
131             fixture_->ctx_ = nullptr;
132         }
133 
134     private:
135         AstVerifierTest *fixture_ {};
136     };
137 
138     class ExpectVerifierMessage {
139     public:
140         ExpectVerifierMessage() = default;
141         // NOLINTNEXTLINE(*-explicit-constructor)
ExpectVerifierMessage(std::initializer_list<std::string> l)142         explicit ExpectVerifierMessage(std::initializer_list<std::string> l) : l_(l) {}
143 
CheckMessages(const verifier_alias::Messages & messages)144         bool CheckMessages(const verifier_alias::Messages &messages)
145         {
146             if (messages.size() != l_.size()) {
147                 std::cerr << "Expected " << l_.size() << " messages, got " << messages.size() << std::endl;
148                 if (!messages.empty()) {
149                     std::cerr << "Got messages:" << std::endl;
150                 }
151                 for (const auto &msg : messages) {
152                     std::cerr << " " << msg.ToString() << std::endl;
153                 }
154                 return false;
155             }
156             auto res = true;
157             // NOLINTNEXTLINE(modernize-loop-convert)
158             for (size_t i = 0; i < messages.size(); i++) {
159                 if (messages[i].ToString().find(l_.begin()[i]) == std::string::npos) {
160                     std::cerr << "Expected '" << l_.begin()[i] << "', got '" << messages[i].ToString() << "'"
161                               << std::endl;
162                     res = false;
163                 }
164             }
165             return res;
166         }
167 
168     private:
169         std::initializer_list<std::string> l_;
170     };
171 
172     template <typename Invariant>
173     [[nodiscard]] auto Verify(ExpectVerifierMessage expected = {})
174     {
175         ASSERT(ctx_ != nullptr);
176         auto *inv = Get<Invariant>();
177         inv->Init();
178 
179         std::function<void(const ir_alias::AstNode *)> aux {};
180         aux = [inv, &aux](const ir_alias::AstNode *child) -> void {
181             // Required invariants need to be manually called in tests:
182             std::apply([child](auto &...requiredInv) { (((void)(requiredInv)(child)), ...); }, inv->GetRequired());
183             const auto [_, action] = (*inv)(child);
184             if (action == verifier_alias::CheckAction::SKIP_SUBTREE) {
185                 return;
186             }
187             child->Iterate(aux);
188         };
189         aux(GetAst());
190         return expected.CheckMessages(inv->ViewMessages());
191     }
192 
193     template <typename Invariant>
194     [[nodiscard]] auto VerifyNode(const ir_alias::AstNode *ast, ExpectVerifierMessage expected = {})
195     {
196         auto *inv = Get<Invariant>();
197         inv->Init();
198         // Required invariants need to be manually called in tests:
199         std::apply([ast](auto &...requiredInv) { (((void)(requiredInv)(ast)), ...); }, inv->GetRequired());
200         (void)(*inv)(ast);
201         return expected.CheckMessages(inv->ViewMessages());
202     }
203 
204 private:
205     es2panda_Impl const *impl_ {};
206     es2panda_Config *cfg_ {};
207     es2panda_Context *ctx_ {};
208     ark::ArenaAllocator *allocator_ {};
209 
210     friend class ::LSPAPITests;
211 };
212 
213 }  // namespace test::utils
214 
215 #endif  // ES2PANDA_TEST_UTILS_AST_VERIFIER_TEST_H
216