| // Copyright 2020 The Fuchsia Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "src/lib/fidl_codec/semantic_parser.h" | |
| #include <iostream> | |
| #include <sstream> | |
| #include <string> | |
| #include <vector> | |
| #include <gtest/gtest.h> | |
| #include "src/lib/fidl_codec/library_loader.h" | |
| #include "src/lib/fidl_codec/list_test_data.h" | |
| #include "src/lib/fidl_codec/semantic_parser_test.h" | |
| namespace fidl_codec { | |
| namespace semantic { | |
| // Checks the semantic parser. | |
| // Checks that we detects errors. | |
| // Checks that we do a good recovery on errors (only a few tests display more than one error). | |
| SemanticParserTest::SemanticParserTest() { | |
| fidl_codec_test::SdkExamples sdk_examples; | |
| // Loads all the files in sdk/core.fidl_json.txt. | |
| for (const auto& element : sdk_examples.map()) { | |
| library_loader_.AddContent(element.second, &err_); | |
| } | |
| } | |
| void SemanticParserTest::SetUp() {} | |
| TEST_F(SemanticParserTest, GlobalExample) { | |
| // Checks that Directory::Open exists in fuchsia.io. | |
| Library* library = library_loader_.GetLibraryFromName("fuchsia.io"); | |
| ASSERT_NE(library, nullptr); | |
| library->DecodeTypes(); | |
| Interface* interface = nullptr; | |
| library->GetInterfaceByName("fuchsia.io/Directory", &interface); | |
| ASSERT_NE(interface, nullptr); | |
| InterfaceMethod* method = interface->GetMethodByName("Open"); | |
| ASSERT_NE(method, nullptr); | |
| // Checks that we currently don't have any semantic for Open. | |
| ASSERT_EQ(method->semantic(), nullptr); | |
| std::string text = | |
| "library fuchsia.io {\n" | |
| " Directory::Open {\n" | |
| " request.object = handle / request.path;\n" | |
| " }\n" | |
| "}\n" | |
| "library fuchsia.sys {\n" | |
| " Launcher::CreateComponent {\n" | |
| " request.controller = HandleDescription('server-control', request.launch_info.url);\n" | |
| " }\n" | |
| "}\n"; | |
| ParserErrors parser_errors; | |
| SemanticParser parser(&library_loader_, text, &parser_errors); | |
| parser.ParseSemantic(); | |
| // Checks that we now have the right semantic. | |
| ASSERT_NE(method->semantic(), nullptr); | |
| std::stringstream ss; | |
| method->semantic()->Dump(ss); | |
| std::string result = ss.str(); | |
| ASSERT_EQ(result, "request.object = handle / request.path\n"); | |
| } | |
| TEST_F(SemanticParserTest, CheckAssignments) { | |
| std::string text = | |
| "request.object = handle / request.path;\n" | |
| "request.foo = handle;\n" | |
| "request.bar = handle / request.other_path;\n" | |
| "request.bar2 = handle : 'cloned';\n"; | |
| ParserErrors parser_errors; | |
| SemanticParser parser(&library_loader_, text, &parser_errors); | |
| MethodSemantic semantic; | |
| while (!parser.IsEof()) { | |
| parser.ParseAssignment(&semantic); | |
| } | |
| std::stringstream ss; | |
| semantic.Dump(ss); | |
| std::string result = ss.str(); | |
| ASSERT_EQ(result, | |
| "request.object = handle / request.path\n" | |
| "request.foo = handle\n" | |
| "request.bar = handle / request.other_path\n" | |
| "request.bar2 = handle : 'cloned'\n"); | |
| } | |
| TEST_F(SemanticParserTest, CheckDisplay) { | |
| std::string text = | |
| " input_field: request.path;\n" | |
| " result: request.object;\n" | |
| " input_field: request.data.size ' bytes';\n" | |
| " input_field: 'buffer of ' request.data.size ' bytes';\n" | |
| " input_field: 'size = ' request.data.size;\n" | |
| "}\n"; | |
| InterfaceMethod method; | |
| ParserErrors parser_errors; | |
| SemanticParser parser(&library_loader_, text, &parser_errors); | |
| while (!parser.IsEof()) { | |
| parser.ParseMethod(&method); | |
| } | |
| ASSERT_NE(method.short_display(), nullptr); | |
| std::stringstream ss; | |
| method.short_display()->Dump(ss); | |
| std::string result = ss.str(); | |
| ASSERT_EQ(result, | |
| "input_field: request.path;\n" | |
| "input_field: request.data.size \" bytes\";\n" | |
| "input_field: \"buffer of \" request.data.size \" bytes\";\n" | |
| "input_field: \"size = \" request.data.size;\n" | |
| "result: request.object;\n"); | |
| } | |
| TEST_F(SemanticParserTest, EmptyText) { | |
| std::string text = ""; | |
| std::stringstream error_stream; | |
| ParserErrors parser_errors(error_stream); | |
| SemanticParser parser(&library_loader_, text, &parser_errors); | |
| MethodSemantic semantic; | |
| parser.ParseSemantic(); | |
| std::string result = error_stream.str(); | |
| ASSERT_EQ(result, ""); | |
| } | |
| TEST_F(SemanticParserTest, LibraryExpected) { | |
| std::string text = | |
| "xxx fuchsia.io {\n" | |
| " Directory::Open {\n" | |
| " request.object = handle / request.path;\n" | |
| " }\n" | |
| "}\n"; | |
| std::stringstream error_stream; | |
| ParserErrors parser_errors(error_stream); | |
| SemanticParser parser(&library_loader_, text, &parser_errors); | |
| parser.ParseSemantic(); | |
| std::string result = error_stream.str(); | |
| ASSERT_EQ(result, | |
| "xxx fuchsia.io {\n" | |
| "^\n" | |
| "1:1: Keyword 'library' expected.\n"); | |
| } | |
| TEST_F(SemanticParserTest, LibraryNameExpected) { | |
| std::string text = | |
| "library {\n" | |
| " Directory::Open {\n" | |
| " request.object = handle / request.path;\n" | |
| " }\n" | |
| "}\n"; | |
| std::stringstream error_stream; | |
| ParserErrors parser_errors(error_stream); | |
| SemanticParser parser(&library_loader_, text, &parser_errors); | |
| parser.ParseSemantic(); | |
| std::string result = error_stream.str(); | |
| ASSERT_EQ(result, | |
| "library {\n" | |
| " ^\n" | |
| "1:9: Library name expected.\n"); | |
| } | |
| TEST_F(SemanticParserTest, LibraryNotFound) { | |
| std::string text = | |
| "library fuchsia.xxx {\n" | |
| " Directory::Open {\n" | |
| " request.object = handle / request.path;\n" | |
| " }\n" | |
| "}\n"; | |
| std::stringstream error_stream; | |
| ParserErrors parser_errors(error_stream); | |
| SemanticParser parser(&library_loader_, text, &parser_errors); | |
| parser.ParseSemantic(); | |
| std::string result = error_stream.str(); | |
| ASSERT_EQ(result, | |
| "library fuchsia.xxx {\n" | |
| " ^\n" | |
| "1:9: Library fuchsia.xxx not found.\n"); | |
| } | |
| TEST_F(SemanticParserTest, MissingLeftBrace1) { | |
| std::string text = | |
| "library fuchsia.io\n" | |
| " Directory::Open {\n" | |
| " request.object = handle / request.path;\n" | |
| " }\n" | |
| "}\n"; | |
| std::stringstream error_stream; | |
| ParserErrors parser_errors(error_stream); | |
| SemanticParser parser(&library_loader_, text, &parser_errors); | |
| parser.ParseSemantic(); | |
| std::string result = error_stream.str(); | |
| ASSERT_EQ(result, | |
| " Directory::Open {\n" | |
| " ^\n" | |
| "2:3: Symbol '{' expected.\n"); | |
| } | |
| TEST_F(SemanticParserTest, ProtocolNameExpected) { | |
| std::string text = | |
| "library fuchsia.io {\n" | |
| " ::Open {\n" | |
| " request.object = handle / request.path;\n" | |
| " }\n" | |
| "}\n"; | |
| std::stringstream error_stream; | |
| ParserErrors parser_errors(error_stream); | |
| SemanticParser parser(&library_loader_, text, &parser_errors); | |
| parser.ParseSemantic(); | |
| std::string result = error_stream.str(); | |
| ASSERT_EQ(result, | |
| " ::Open {\n" | |
| " ^\n" | |
| "2:3: Protocol name expected.\n"); | |
| } | |
| TEST_F(SemanticParserTest, ProtocolNotFound) { | |
| std::string text = | |
| "library fuchsia.io {\n" | |
| " Xxx::Open {\n" | |
| " request.object = handle / request.path;\n" | |
| " }\n" | |
| "}\n"; | |
| std::stringstream error_stream; | |
| ParserErrors parser_errors(error_stream); | |
| SemanticParser parser(&library_loader_, text, &parser_errors); | |
| parser.ParseSemantic(); | |
| std::string result = error_stream.str(); | |
| ASSERT_EQ(result, | |
| " Xxx::Open {\n" | |
| " ^\n" | |
| "2:3: Protocol Xxx not found in library fuchsia.io\n"); | |
| } | |
| TEST_F(SemanticParserTest, DoubleColonExpected) { | |
| std::string text = | |
| "library fuchsia.io {\n" | |
| " Directory Open {\n" | |
| " request.object = handle / request.path;\n" | |
| " }\n" | |
| "}\n"; | |
| std::stringstream error_stream; | |
| ParserErrors parser_errors(error_stream); | |
| SemanticParser parser(&library_loader_, text, &parser_errors); | |
| parser.ParseSemantic(); | |
| std::string result = error_stream.str(); | |
| ASSERT_EQ(result, | |
| " Directory Open {\n" | |
| " ^\n" | |
| "2:13: Symbol '::' expected.\n"); | |
| } | |
| TEST_F(SemanticParserTest, MethodNameExpected) { | |
| std::string text = | |
| "library fuchsia.io {\n" | |
| " Directory:: {\n" | |
| " request.object = handle / request.path;\n" | |
| " }\n" | |
| "}\n"; | |
| std::stringstream error_stream; | |
| ParserErrors parser_errors(error_stream); | |
| SemanticParser parser(&library_loader_, text, &parser_errors); | |
| parser.ParseSemantic(); | |
| std::string result = error_stream.str(); | |
| ASSERT_EQ(result, | |
| " Directory:: {\n" | |
| " ^\n" | |
| "2:15: Method name expected.\n"); | |
| } | |
| TEST_F(SemanticParserTest, MethodNotFound) { | |
| std::string text = | |
| "library fuchsia.io {\n" | |
| " Directory::Xxx {\n" | |
| " request.object = handle / request.path;\n" | |
| " }\n" | |
| "}\n"; | |
| std::stringstream error_stream; | |
| ParserErrors parser_errors(error_stream); | |
| SemanticParser parser(&library_loader_, text, &parser_errors); | |
| parser.ParseSemantic(); | |
| std::string result = error_stream.str(); | |
| ASSERT_EQ(result, | |
| " Directory::Xxx {\n" | |
| " ^\n" | |
| "2:14: Method Xxx not found in protocol fuchsia.io/Directory\n"); | |
| } | |
| TEST_F(SemanticParserTest, MissingLeftBrace2) { | |
| std::string text = | |
| "library fuchsia.io {\n" | |
| " Directory::Open\n" | |
| " request.object = handle / request.path;\n" | |
| " }\n" | |
| "}\n"; | |
| std::stringstream error_stream; | |
| ParserErrors parser_errors(error_stream); | |
| SemanticParser parser(&library_loader_, text, &parser_errors); | |
| parser.ParseSemantic(); | |
| std::string result = error_stream.str(); | |
| ASSERT_EQ(result, | |
| " request.object = handle / request.path;\n" | |
| " ^\n" | |
| "3:5: Symbol '{' expected.\n" | |
| "}\n" | |
| "^\n" | |
| "5:1: Keyword 'library' expected.\n"); | |
| } | |
| TEST_F(SemanticParserTest, InputFieldColonExpected) { | |
| std::string text = | |
| "library fuchsia.io {\n" | |
| " Directory::Open {\n" | |
| " input_field request.path;\n" | |
| " result: request.object;\n" | |
| " }\n" | |
| "}\n"; | |
| std::stringstream error_stream; | |
| ParserErrors parser_errors(error_stream); | |
| SemanticParser parser(&library_loader_, text, &parser_errors); | |
| parser.ParseSemantic(); | |
| std::string result = error_stream.str(); | |
| ASSERT_EQ(result, | |
| " input_field request.path;\n" | |
| " ^\n" | |
| "3:17: Symbol ':' expected.\n"); | |
| } | |
| TEST_F(SemanticParserTest, ResultColonExpected) { | |
| std::string text = | |
| "library fuchsia.io {\n" | |
| " Directory::Open {\n" | |
| " input_field: request.path;\n" | |
| " result request.object;\n" | |
| " }\n" | |
| "}\n"; | |
| std::stringstream error_stream; | |
| ParserErrors parser_errors(error_stream); | |
| SemanticParser parser(&library_loader_, text, &parser_errors); | |
| parser.ParseSemantic(); | |
| std::string result = error_stream.str(); | |
| ASSERT_EQ(result, | |
| " result request.object;\n" | |
| " ^\n" | |
| "4:12: Symbol ':' expected.\n"); | |
| } | |
| TEST_F(SemanticParserTest, InputFieldSemiColonExpected) { | |
| std::string text = | |
| "library fuchsia.io {\n" | |
| " Directory::Open {\n" | |
| " input_field: request.path\n" | |
| " result: request.object;\n" | |
| " }\n" | |
| "}\n"; | |
| std::stringstream error_stream; | |
| ParserErrors parser_errors(error_stream); | |
| SemanticParser parser(&library_loader_, text, &parser_errors); | |
| parser.ParseSemantic(); | |
| std::string result = error_stream.str(); | |
| ASSERT_EQ(result, | |
| " result: request.object;\n" | |
| " ^\n" | |
| "4:5: Symbol ';' expected.\n"); | |
| } | |
| TEST_F(SemanticParserTest, ResultSemiColonExpected) { | |
| std::string text = | |
| "library fuchsia.io {\n" | |
| " Directory::Open {\n" | |
| " input_field: request.path;\n" | |
| " result: request.object\n" | |
| " }\n" | |
| "}\n"; | |
| std::stringstream error_stream; | |
| ParserErrors parser_errors(error_stream); | |
| SemanticParser parser(&library_loader_, text, &parser_errors); | |
| parser.ParseSemantic(); | |
| std::string result = error_stream.str(); | |
| ASSERT_EQ(result, | |
| " }\n" | |
| " ^\n" | |
| "5:3: Symbol ';' expected.\n"); | |
| } | |
| TEST_F(SemanticParserTest, AssignmentExpected) { | |
| std::string text = | |
| "library fuchsia.io {\n" | |
| " Directory::Open {\n" | |
| " = handle / request.path;\n" | |
| " }\n" | |
| "}\n"; | |
| std::stringstream error_stream; | |
| ParserErrors parser_errors(error_stream); | |
| SemanticParser parser(&library_loader_, text, &parser_errors); | |
| parser.ParseSemantic(); | |
| std::string result = error_stream.str(); | |
| ASSERT_EQ(result, | |
| " = handle / request.path;\n" | |
| " ^\n" | |
| "3:5: Assignment expected.\n"); | |
| } | |
| TEST_F(SemanticParserTest, FieldNameExpected) { | |
| std::string text = | |
| "library fuchsia.io {\n" | |
| " Directory::Open {\n" | |
| " request. = handle / request.path;\n" | |
| " }\n" | |
| "}\n"; | |
| std::stringstream error_stream; | |
| ParserErrors parser_errors(error_stream); | |
| SemanticParser parser(&library_loader_, text, &parser_errors); | |
| parser.ParseSemantic(); | |
| std::string result = error_stream.str(); | |
| ASSERT_EQ(result, | |
| " request. = handle / request.path;\n" | |
| " ^\n" | |
| "3:14: Field name expected.\n"); | |
| } | |
| TEST_F(SemanticParserTest, EqualExpected) { | |
| std::string text = | |
| "library fuchsia.io {\n" | |
| " Directory::Open {\n" | |
| " request.object handle / request.path;\n" | |
| " }\n" | |
| "}\n"; | |
| std::stringstream error_stream; | |
| ParserErrors parser_errors(error_stream); | |
| SemanticParser parser(&library_loader_, text, &parser_errors); | |
| parser.ParseSemantic(); | |
| std::string result = error_stream.str(); | |
| ASSERT_EQ(result, | |
| " request.object handle / request.path;\n" | |
| " ^\n" | |
| "3:20: Symbol '=' expected.\n"); | |
| } | |
| TEST_F(SemanticParserTest, ExpressionExpected1) { | |
| std::string text = | |
| "library fuchsia.io {\n" | |
| " Directory::Open {\n" | |
| " request.object =;\n" | |
| " }\n" | |
| "}\n"; | |
| std::stringstream error_stream; | |
| ParserErrors parser_errors(error_stream); | |
| SemanticParser parser(&library_loader_, text, &parser_errors); | |
| parser.ParseSemantic(); | |
| std::string result = error_stream.str(); | |
| ASSERT_EQ(result, | |
| " request.object =;\n" | |
| " ^\n" | |
| "3:21: Expression expected.\n"); | |
| } | |
| TEST_F(SemanticParserTest, ExpressionExpected2) { | |
| std::string text = | |
| "library fuchsia.io {\n" | |
| " Directory::Open {\n" | |
| " request.object = handle /;\n" | |
| " }\n" | |
| "}\n"; | |
| std::stringstream error_stream; | |
| ParserErrors parser_errors(error_stream); | |
| SemanticParser parser(&library_loader_, text, &parser_errors); | |
| parser.ParseSemantic(); | |
| std::string result = error_stream.str(); | |
| ASSERT_EQ(result, | |
| " request.object = handle /;\n" | |
| " ^\n" | |
| "3:30: Expression expected.\n"); | |
| } | |
| TEST_F(SemanticParserTest, ExpressionExpected3) { | |
| std::string text = | |
| "library fuchsia.io {\n" | |
| " Directory::Open {\n" | |
| " request.object = xxx;\n" | |
| " }\n" | |
| "}\n"; | |
| std::stringstream error_stream; | |
| ParserErrors parser_errors(error_stream); | |
| SemanticParser parser(&library_loader_, text, &parser_errors); | |
| parser.ParseSemantic(); | |
| std::string result = error_stream.str(); | |
| ASSERT_EQ(result, | |
| " request.object = xxx;\n" | |
| " ^\n" | |
| "3:22: Expression expected.\n"); | |
| } | |
| TEST_F(SemanticParserTest, SemicolonExpected) { | |
| std::string text = | |
| "library fuchsia.io {\n" | |
| " Directory::Open {\n" | |
| " request.object = handle / request.path\n" | |
| " }\n" | |
| "}\n"; | |
| std::stringstream error_stream; | |
| ParserErrors parser_errors(error_stream); | |
| SemanticParser parser(&library_loader_, text, &parser_errors); | |
| parser.ParseSemantic(); | |
| std::string result = error_stream.str(); | |
| ASSERT_EQ(result, | |
| " }\n" | |
| " ^\n" | |
| "4:3: Symbol ';' expected.\n"); | |
| } | |
| TEST_F(SemanticParserTest, HandleDescriptionTypo) { | |
| std::string text = | |
| "library fuchsia.sys {\n" | |
| " Launcher::CreateComponent {\n" | |
| " request.controller = HandleDescriptions('server-control', request.launch_info.url);\n" | |
| " }\n" | |
| "}\n"; | |
| std::stringstream error_stream; | |
| ParserErrors parser_errors(error_stream); | |
| SemanticParser parser(&library_loader_, text, &parser_errors); | |
| parser.ParseSemantic(); | |
| std::string result = error_stream.str(); | |
| ASSERT_EQ( | |
| result, | |
| " request.controller = HandleDescriptions('server-control', request.launch_info.url);\n" | |
| " ^\n" | |
| "3:25: Expression expected.\n"); | |
| } | |
| TEST_F(SemanticParserTest, UnterminatedString) { | |
| std::string text = | |
| "library fuchsia.sys {\n" | |
| " Launcher::CreateComponent {\n" | |
| " request.controller = HandleDescription('server-control, request.launch_info.url);\n" | |
| " }\n" | |
| "}\n"; | |
| std::stringstream error_stream; | |
| ParserErrors parser_errors(error_stream); | |
| SemanticParser parser(&library_loader_, text, &parser_errors); | |
| parser.ParseSemantic(); | |
| std::string result = error_stream.str(); | |
| ASSERT_EQ(result, | |
| " request.controller = HandleDescription('server-control, request.launch_info.url);\n" | |
| " ^\n3:43: Unterminated string.\n" | |
| " request.controller = HandleDescription('server-control, request.launch_info.url);\n" | |
| " ^\n3:44: Symbol ',' expected.\n"); | |
| } | |
| TEST_F(SemanticParserTest, LeftParenthesisExpected) { | |
| std::string text = | |
| "library fuchsia.sys {\n" | |
| " Launcher::CreateComponent {\n" | |
| " request.controller = HandleDescription 'server-control', request.launch_info.url);\n" | |
| " }\n" | |
| "}\n"; | |
| std::stringstream error_stream; | |
| ParserErrors parser_errors(error_stream); | |
| SemanticParser parser(&library_loader_, text, &parser_errors); | |
| parser.ParseSemantic(); | |
| std::string result = error_stream.str(); | |
| ASSERT_EQ( | |
| result, | |
| " request.controller = HandleDescription 'server-control', request.launch_info.url);\n" | |
| " ^\n" | |
| "3:43: Symbol '(' expected.\n"); | |
| } | |
| TEST_F(SemanticParserTest, CommaExpected) { | |
| std::string text = | |
| "library fuchsia.sys {\n" | |
| " Launcher::CreateComponent {\n" | |
| " request.controller = HandleDescription('server-control' request.launch_info.url);\n" | |
| " }\n" | |
| "}\n"; | |
| std::stringstream error_stream; | |
| ParserErrors parser_errors(error_stream); | |
| SemanticParser parser(&library_loader_, text, &parser_errors); | |
| parser.ParseSemantic(); | |
| std::string result = error_stream.str(); | |
| ASSERT_EQ(result, | |
| " request.controller = HandleDescription('server-control' request.launch_info.url);\n" | |
| " ^\n" | |
| "3:60: Symbol ',' expected.\n"); | |
| } | |
| TEST_F(SemanticParserTest, RightParenthesisExpected) { | |
| std::string text = | |
| "library fuchsia.sys {\n" | |
| " Launcher::CreateComponent {\n" | |
| " request.controller = HandleDescription('server-control', request.launch_info.url;\n" | |
| " }\n" | |
| "}\n"; | |
| std::stringstream error_stream; | |
| ParserErrors parser_errors(error_stream); | |
| SemanticParser parser(&library_loader_, text, &parser_errors); | |
| parser.ParseSemantic(); | |
| std::string result = error_stream.str(); | |
| ASSERT_EQ(result, | |
| " request.controller = HandleDescription('server-control', request.launch_info.url;\n" | |
| " ^\n" | |
| "3:84: Symbol ')' expected.\n"); | |
| } | |
| } // namespace semantic | |
| } // namespace fidl_codec |