1 module parsertests; 2 3 import std.format : format; 4 import std.experimental.allocator; 5 import std.experimental.allocator.mallocator : Mallocator; 6 import std.stdio; 7 8 import lexer; 9 import parser; 10 11 private struct TestCase { 12 int id; 13 string str; 14 } 15 16 unittest { 17 TestCase[] tests; 18 tests ~= TestCase(0, 19 ` 20 mutation updateUser($userId: String! $name: String!) { 21 updateUser(id: $userId name: $name) { 22 name 23 } 24 } 25 `); 26 27 tests ~= TestCase(1,` 28 query inlineFragmentNoType($expandedInfo: Boolean) { 29 user(handle: "zuck") { 30 id 31 name 32 ... @include(if: $expandedInfo) { 33 firstName 34 lastName 35 birthday 36 } 37 } 38 } 39 `); 40 41 tests ~= TestCase(2, ` 42 query HeroForEpisode($ep: Episode!) { 43 hero(episode: $ep) { 44 name 45 ... on Droid { 46 primaryFunction 47 } 48 ... on Human { 49 height 50 } 51 } 52 } 53 `); 54 55 tests ~= TestCase(3, ` 56 mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) { 57 createReview(episode: $ep, review: $review) { 58 stars 59 commentary 60 } 61 } 62 `); 63 64 tests ~= TestCase(4, ` 65 query HeroNameAndFriends($episode: Episode = "JEDI") { 66 hero(episode: $episode) { 67 name 68 friends { 69 name 70 } 71 } 72 }`); 73 74 tests ~= TestCase(5, ` 75 query hero { 76 name 77 # Queries can have comments! 78 79 # Queries can have comments Another! 80 friends { 81 name 82 } 83 }`); 84 85 tests ~= TestCase(6, ` 86 enum DogCommand { SIT, DOWN, HEEL } 87 88 type Dog implements Pet { 89 name: String! 90 nickname: String 91 barkVolume: Int 92 doesKnowCommand(dogCommand: DogCommand!): Boolean! 93 isHousetrained(atOtherHomes: Boolean): Boolean! 94 owner: Human 95 } 96 97 interface Sentient { 98 name: String! 99 } 100 101 interface Pet { 102 name: String! 103 } 104 105 type Alien implements Sentient { 106 name: String! 107 homePlanet: String 108 } 109 110 type Human implements Sentient { 111 name: String! 112 } 113 114 enum CatCommand { JUMP } 115 116 type Cat implements Pet { 117 name: String! 118 nickname: String 119 doesKnowCommand(catCommand: CatCommand!): Boolean! 120 meowVolume: Int 121 } 122 123 union CatOrDog = Cat | Dog 124 union DogOrHuman = Dog | Human 125 union HumanOrAlien = Human | Alien 126 127 type QueryRoot { 128 dog: Dog 129 } 130 `); 131 132 tests ~= TestCase(7, ` 133 union SearchResult = Photo | Person 134 union SearchResult = Photo Person 135 136 type Person { 137 name: String 138 age: Int 139 } 140 141 type Photo { 142 height: Int 143 width: Int 144 } 145 146 type SearchQuery { 147 firstSearchResult: SearchResult 148 }`); 149 150 tests ~= TestCase(8, ` 151 interface NamedEntity { 152 name: String 153 } 154 155 type Person implements NamedEntity { 156 name: String 157 age: Int 158 } 159 160 type Business implements NamedEntity { 161 name: String 162 employeeCount: Int 163 }`); 164 165 tests ~= TestCase(9, `type Person { 166 name: String 167 age: Int 168 picture: Url 169 }`); 170 171 tests ~= TestCase(10, ` 172 query myQuery($someTest: Boolean) { 173 experimentalField @skip(if: $someTest) 174 }`); 175 176 tests ~= TestCase(11, ` 177 subscription sub { 178 newMessage { 179 body 180 sender 181 } 182 } 183 184 fragment newMessageFields on Message { 185 body 186 sender 187 } 188 189 subscription sub { 190 newMessage { 191 ... newMessageFields 192 } 193 194 }`); 195 196 tests ~= TestCase(11, ` 197 query HeroNameAndFriends($episode: Episode) { 198 hero(episode: $episode) { 199 name, 200 friends { 201 name 202 } 203 } 204 }`); 205 206 tests ~= TestCase(12, ` 207 query name { 208 leftComparison: hero(episode: $EMPIRE) { 209 ...comparisonFields 210 } 211 rightComparison: hero(episode: $JEDI) { 212 ...comparisonFields 213 } 214 } 215 fragment comparisonFields on Character { 216 name 217 appearsIn 218 friends { 219 name 220 } 221 } 222 223 `); 224 225 tests ~= TestCase(13, ` 226 query name{ 227 builds(first: 54) { 228 all: number 229 } 230 } 231 `); 232 233 tests ~= TestCase(14, ` 234 query foo { 235 viewer { 236 user { 237 name 238 builds(first: 1) { 239 edges { 240 node { 241 number 242 branch 243 message 244 } 245 } 246 } 247 } 248 } 249 } 250 `); 251 252 tests ~= TestCase(15, ` 253 query foo { 254 name 255 builds(first: 1) { 256 abc 257 } 258 }`); 259 260 tests ~= TestCase(16, ` 261 query h { 262 name 263 builds 264 } 265 `); 266 267 tests ~= TestCase(17, ` 268 query human($id: H, $limit: lim, $gender: Gen) { 269 name 270 height 271 friends { 272 id 273 gender 274 income 275 } 276 }` ); 277 278 tests ~= TestCase(18, ` 279 query human($id: Var) { 280 name 281 height 282 }`); 283 284 tests ~= TestCase(19, ` 285 query human() { 286 name 287 height 288 }`); 289 290 tests ~= TestCase(20, ` 291 query name { 292 foo 293 }`); 294 295 /*tests ~= TestCase(21, `{ 296 query n { 297 __type(name: "User") { 298 name 299 fields { 300 name 301 type { 302 name 303 } 304 } 305 } 306 } 307 }`);*/ 308 309 tests ~= TestCase(21, `{ 310 user(id: 1) { 311 friends { 312 name 313 } 314 } 315 }`); 316 317 tests ~= TestCase(22, `query IntrospectionQueryTypeQuery { 318 __schema { 319 queryType { 320 fields { 321 name 322 args { 323 name 324 description 325 type { 326 name 327 kind 328 ofType { 329 name 330 kind 331 } 332 } 333 defaultValue 334 } 335 } 336 } 337 } 338 }`); 339 340 tests ~= TestCase(23, `query IntrospectionDroidFieldsQuery { 341 __type(name: "Droid") { 342 name 343 fields { 344 name 345 type { 346 name 347 kind 348 } 349 } 350 } 351 }`); 352 353 tests ~= TestCase(24, ` 354 query IntrospectionCharacterKindQuery { 355 __type(name: "Character") { 356 name 357 kind 358 } 359 }`); 360 361 362 tests ~= TestCase(25, `query IntrospectionDroidKindQuery { 363 __type(name: "Droid") { 364 name 365 kind 366 } 367 }`); 368 369 tests ~= TestCase(26, `query IntrospectionDroidTypeQuery { 370 __type(name: "Droid") { 371 name 372 } 373 }`); 374 375 tests ~= TestCase(27, `query IntrospectionQueryTypeQuery { 376 __schema { 377 queryType { 378 name 379 } 380 } 381 }`); 382 383 tests ~= TestCase(28, `query IntrospectionTypeQuery { 384 __schema { 385 types { 386 name 387 } 388 } 389 }`); 390 391 tests ~= TestCase(29, `query IntrospectionDroidDescriptionQuery { 392 __type(name: "Droid") { 393 name 394 description 395 } 396 }`); 397 398 foreach(test; tests) { 399 auto l = Lexer(test.str); 400 auto p = Parser(l); 401 try { 402 auto d = p.parseDocument(); 403 } catch(Throwable e) { 404 writeln(e.toString()); 405 while(e.next) { 406 writeln(e.next.toString()); 407 e = e.next; 408 } 409 assert(false, format("Test %d", test.id)); 410 } 411 assert(p.lex.empty, format("%d", test.id)); 412 } 413 }