| // 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 <memory> | 
 | #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" | 
 | #include "src/lib/fidl_codec/wire_types.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(); | 
 |   Protocol* protocol = nullptr; | 
 |   library->GetProtocolByName("fuchsia.io/Directory", &protocol); | 
 |   ASSERT_NE(protocol, nullptr); | 
 |   ProtocolMethod* method = protocol->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"; | 
 |   ProtocolMethod 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 |