1 //===- unittest/Support/YAMLParserTest ------------------------------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "llvm/ADT/SmallString.h" 11 #include "llvm/ADT/Twine.h" 12 #include "llvm/Support/Casting.h" 13 #include "llvm/Support/MemoryBuffer.h" 14 #include "llvm/Support/SourceMgr.h" 15 #include "llvm/Support/YAMLParser.h" 16 #include "gtest/gtest.h" 17 18 namespace llvm { 19 20 static void SuppressDiagnosticsOutput(const SMDiagnostic &, void *) { 21 // Prevent SourceMgr from writing errors to stderr 22 // to reduce noise in unit test runs. 23 } 24 25 // Assumes Ctx is an SMDiagnostic where Diag can be stored. 26 static void CollectDiagnosticsOutput(const SMDiagnostic &Diag, void *Ctx) { 27 SMDiagnostic* DiagOut = static_cast<SMDiagnostic*>(Ctx); 28 *DiagOut = Diag; 29 } 30 31 // Checks that the given input gives a parse error. Makes sure that an error 32 // text is available and the parse fails. 33 static void ExpectParseError(StringRef Message, StringRef Input) { 34 SourceMgr SM; 35 yaml::Stream Stream(Input, SM); 36 SM.setDiagHandler(SuppressDiagnosticsOutput); 37 EXPECT_FALSE(Stream.validate()) << Message << ": " << Input; 38 EXPECT_TRUE(Stream.failed()) << Message << ": " << Input; 39 } 40 41 // Checks that the given input can be parsed without error. 42 static void ExpectParseSuccess(StringRef Message, StringRef Input) { 43 SourceMgr SM; 44 yaml::Stream Stream(Input, SM); 45 EXPECT_TRUE(Stream.validate()) << Message << ": " << Input; 46 } 47 48 TEST(YAMLParser, ParsesEmptyArray) { 49 ExpectParseSuccess("Empty array", "[]"); 50 } 51 52 TEST(YAMLParser, FailsIfNotClosingArray) { 53 ExpectParseError("Not closing array", "["); 54 ExpectParseError("Not closing array", " [ "); 55 ExpectParseError("Not closing array", " [x"); 56 } 57 58 TEST(YAMLParser, ParsesEmptyArrayWithWhitespace) { 59 ExpectParseSuccess("Array with spaces", " [ ] "); 60 ExpectParseSuccess("All whitespaces", "\t\r\n[\t\n \t\r ]\t\r \n\n"); 61 } 62 63 TEST(YAMLParser, ParsesEmptyObject) { 64 ExpectParseSuccess("Empty object", "[{}]"); 65 } 66 67 TEST(YAMLParser, ParsesObject) { 68 ExpectParseSuccess("Object with an entry", "[{\"a\":\"/b\"}]"); 69 } 70 71 TEST(YAMLParser, ParsesMultipleKeyValuePairsInObject) { 72 ExpectParseSuccess("Multiple key, value pairs", 73 "[{\"a\":\"/b\",\"c\":\"d\",\"e\":\"f\"}]"); 74 } 75 76 TEST(YAMLParser, FailsIfNotClosingObject) { 77 ExpectParseError("Missing close on empty", "[{]"); 78 ExpectParseError("Missing close after pair", "[{\"a\":\"b\"]"); 79 } 80 81 TEST(YAMLParser, FailsIfMissingColon) { 82 ExpectParseError("Missing colon between key and value", "[{\"a\"\"/b\"}]"); 83 ExpectParseError("Missing colon between key and value", "[{\"a\" \"b\"}]"); 84 } 85 86 TEST(YAMLParser, FailsOnMissingQuote) { 87 ExpectParseError("Missing open quote", "[{a\":\"b\"}]"); 88 ExpectParseError("Missing closing quote", "[{\"a\":\"b}]"); 89 } 90 91 TEST(YAMLParser, ParsesEscapedQuotes) { 92 ExpectParseSuccess("Parses escaped string in key and value", 93 "[{\"a\":\"\\\"b\\\" \\\" \\\"\"}]"); 94 } 95 96 TEST(YAMLParser, ParsesEmptyString) { 97 ExpectParseSuccess("Parses empty string in value", "[{\"a\":\"\"}]"); 98 } 99 100 TEST(YAMLParser, ParsesMultipleObjects) { 101 ExpectParseSuccess( 102 "Multiple objects in array", 103 "[" 104 " { \"a\" : \"b\" }," 105 " { \"a\" : \"b\" }," 106 " { \"a\" : \"b\" }" 107 "]"); 108 } 109 110 TEST(YAMLParser, FailsOnMissingComma) { 111 ExpectParseError( 112 "Missing comma", 113 "[" 114 " { \"a\" : \"b\" }" 115 " { \"a\" : \"b\" }" 116 "]"); 117 } 118 119 TEST(YAMLParser, ParsesSpacesInBetweenTokens) { 120 ExpectParseSuccess( 121 "Various whitespace between tokens", 122 " \t \n\n \r [ \t \n\n \r" 123 " \t \n\n \r { \t \n\n \r\"a\"\t \n\n \r :" 124 " \t \n\n \r \"b\"\t \n\n \r } \t \n\n \r,\t \n\n \r" 125 " \t \n\n \r { \t \n\n \r\"a\"\t \n\n \r :" 126 " \t \n\n \r \"b\"\t \n\n \r } \t \n\n \r]\t \n\n \r"); 127 } 128 129 TEST(YAMLParser, ParsesArrayOfArrays) { 130 ExpectParseSuccess("Array of arrays", "[[]]"); 131 } 132 133 TEST(YAMLParser, ParsesBlockLiteralScalars) { 134 ExpectParseSuccess("Block literal scalar", "test: |\n Hello\n World\n"); 135 ExpectParseSuccess("Block literal scalar EOF", "test: |\n Hello\n World"); 136 ExpectParseSuccess("Empty block literal scalar header EOF", "test: | "); 137 ExpectParseSuccess("Empty block literal scalar", "test: |\ntest2: 20"); 138 ExpectParseSuccess("Empty block literal scalar 2", "- | \n \n\n \n- 42"); 139 ExpectParseSuccess("Block literal scalar in sequence", 140 "- |\n Testing\n Out\n\n- 22"); 141 ExpectParseSuccess("Block literal scalar in document", 142 "--- |\n Document\n..."); 143 ExpectParseSuccess("Empty non indented lines still count", 144 "- |\n First line\n \n\n Another line\n\n- 2"); 145 ExpectParseSuccess("Comment in block literal scalar header", 146 "test: | # Comment \n No Comment\ntest 2: | # Void"); 147 ExpectParseSuccess("Chomping indicators in block literal scalar header", 148 "test: |- \n Hello\n\ntest 2: |+ \n\n World\n\n\n"); 149 ExpectParseSuccess("Indent indicators in block literal scalar header", 150 "test: |1 \n \n Hello \n World\n"); 151 ExpectParseSuccess("Chomping and indent indicators in block literals", 152 "test: |-1\n Hello\ntest 2: |9+\n World"); 153 ExpectParseSuccess("Trailing comments in block literals", 154 "test: |\n Content\n # Trailing\n #Comment\ntest 2: 3"); 155 ExpectParseError("Invalid block scalar header", "test: | failure"); 156 ExpectParseError("Invalid line indentation", "test: |\n First line\n Error"); 157 ExpectParseError("Long leading space line", "test: |\n \n Test\n"); 158 } 159 160 TEST(YAMLParser, NullTerminatedBlockScalars) { 161 SourceMgr SM; 162 yaml::Stream Stream("test: |\n Hello\n World\n", SM); 163 yaml::Document &Doc = *Stream.begin(); 164 yaml::MappingNode *Map = cast<yaml::MappingNode>(Doc.getRoot()); 165 StringRef Value = 166 cast<yaml::BlockScalarNode>(Map->begin()->getValue())->getValue(); 167 168 EXPECT_EQ(Value, "Hello\nWorld\n"); 169 EXPECT_EQ(Value.data()[Value.size()], '\0'); 170 } 171 172 TEST(YAMLParser, HandlesEndOfFileGracefully) { 173 ExpectParseError("In string starting with EOF", "[\""); 174 ExpectParseError("In string hitting EOF", "[\" "); 175 ExpectParseError("In string escaping EOF", "[\" \\"); 176 ExpectParseError("In array starting with EOF", "["); 177 ExpectParseError("In array element starting with EOF", "[[], "); 178 ExpectParseError("In array hitting EOF", "[[] "); 179 ExpectParseError("In array hitting EOF", "[[]"); 180 ExpectParseError("In object hitting EOF", "{\"\""); 181 } 182 183 TEST(YAMLParser, HandlesNullValuesInKeyValueNodesGracefully) { 184 ExpectParseError("KeyValueNode with null value", "test: '"); 185 } 186 187 // Checks that the given string can be parsed into an identical string inside 188 // of an array. 189 static void ExpectCanParseString(StringRef String) { 190 std::string StringInArray = (llvm::Twine("[\"") + String + "\"]").str(); 191 SourceMgr SM; 192 yaml::Stream Stream(StringInArray, SM); 193 yaml::SequenceNode *ParsedSequence 194 = dyn_cast<yaml::SequenceNode>(Stream.begin()->getRoot()); 195 StringRef ParsedString 196 = dyn_cast<yaml::ScalarNode>( 197 static_cast<yaml::Node*>(ParsedSequence->begin()))->getRawValue(); 198 ParsedString = ParsedString.substr(1, ParsedString.size() - 2); 199 EXPECT_EQ(String, ParsedString.str()); 200 } 201 202 // Checks that parsing the given string inside an array fails. 203 static void ExpectCannotParseString(StringRef String) { 204 std::string StringInArray = (llvm::Twine("[\"") + String + "\"]").str(); 205 ExpectParseError((Twine("When parsing string \"") + String + "\"").str(), 206 StringInArray); 207 } 208 209 TEST(YAMLParser, ParsesStrings) { 210 ExpectCanParseString(""); 211 ExpectCannotParseString("\\"); 212 ExpectCannotParseString("\""); 213 ExpectCanParseString(" "); 214 ExpectCanParseString("\\ "); 215 ExpectCanParseString("\\\""); 216 ExpectCannotParseString("\"\\"); 217 ExpectCannotParseString(" \\"); 218 ExpectCanParseString("\\\\"); 219 ExpectCannotParseString("\\\\\\"); 220 ExpectCanParseString("\\\\\\\\"); 221 ExpectCanParseString("\\\" "); 222 ExpectCannotParseString("\\\\\" "); 223 ExpectCanParseString("\\\\\\\" "); 224 ExpectCanParseString(" \\\\ \\\" \\\\\\\" "); 225 } 226 227 TEST(YAMLParser, WorksWithIteratorAlgorithms) { 228 SourceMgr SM; 229 yaml::Stream Stream("[\"1\", \"2\", \"3\", \"4\", \"5\", \"6\"]", SM); 230 yaml::SequenceNode *Array 231 = dyn_cast<yaml::SequenceNode>(Stream.begin()->getRoot()); 232 EXPECT_EQ(6, std::distance(Array->begin(), Array->end())); 233 } 234 235 TEST(YAMLParser, DefaultDiagnosticFilename) { 236 SourceMgr SM; 237 238 SMDiagnostic GeneratedDiag; 239 SM.setDiagHandler(CollectDiagnosticsOutput, &GeneratedDiag); 240 241 // When we construct a YAML stream over an unnamed string, 242 // the filename is hard-coded as "YAML". 243 yaml::Stream UnnamedStream("[]", SM); 244 UnnamedStream.printError(UnnamedStream.begin()->getRoot(), "Hello, World!"); 245 EXPECT_EQ("YAML", GeneratedDiag.getFilename()); 246 } 247 248 TEST(YAMLParser, DiagnosticFilenameFromBufferID) { 249 SourceMgr SM; 250 251 SMDiagnostic GeneratedDiag; 252 SM.setDiagHandler(CollectDiagnosticsOutput, &GeneratedDiag); 253 254 // When we construct a YAML stream over a named buffer, 255 // we get its ID as filename in diagnostics. 256 std::unique_ptr<MemoryBuffer> Buffer = 257 MemoryBuffer::getMemBuffer("[]", "buffername.yaml"); 258 yaml::Stream Stream(Buffer->getMemBufferRef(), SM); 259 Stream.printError(Stream.begin()->getRoot(), "Hello, World!"); 260 EXPECT_EQ("buffername.yaml", GeneratedDiag.getFilename()); 261 } 262 263 } // end namespace llvm 264