| OLD | NEW |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library test.domain.analysis; | 5 library test.domain.analysis; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 | 8 |
| 9 import 'package:analyzer/file_system/memory_file_system.dart'; | |
| 10 import 'package:analysis_server/src/analysis_server.dart'; | 9 import 'package:analysis_server/src/analysis_server.dart'; |
| 11 import 'package:analysis_server/src/computer/element.dart'; | 10 import 'package:analysis_server/src/computer/element.dart'; |
| 12 import 'package:analysis_server/src/constants.dart'; | 11 import 'package:analysis_server/src/constants.dart'; |
| 13 import 'package:analysis_server/src/domain_analysis.dart'; | 12 import 'package:analysis_server/src/domain_analysis.dart'; |
| 14 import 'package:analysis_server/src/protocol.dart'; | 13 import 'package:analysis_server/src/protocol.dart'; |
| 14 import 'package:analysis_testing/mock_sdk.dart'; |
| 15 import 'package:analysis_testing/reflective_tests.dart'; |
| 16 import 'package:analyzer/file_system/memory_file_system.dart'; |
| 15 import 'package:analyzer/src/generated/engine.dart'; | 17 import 'package:analyzer/src/generated/engine.dart'; |
| 16 import 'package:path/path.dart'; | 18 import 'package:path/path.dart'; |
| 17 import 'package:unittest/unittest.dart'; | 19 import 'package:unittest/unittest.dart'; |
| 18 | 20 |
| 21 import 'analysis_abstract.dart'; |
| 19 import 'mocks.dart'; | 22 import 'mocks.dart'; |
| 20 import 'analysis_abstract.dart'; | 23 |
| 21 import 'reflective_tests.dart'; | 24 |
| 25 AnalysisError jsonToAnalysisError(Map<String, Object> json) { |
| 26 Map<String, Object> jsonLocation = json[LOCATION]; |
| 27 Location location = new Location(jsonLocation[FILE], _getSafeInt(jsonLocation, |
| 28 OFFSET, -1), _getSafeInt(jsonLocation, LENGTH, -1), _getSafeInt(jsonLocati
on, |
| 29 START_LINE, -1), _getSafeInt(jsonLocation, START_COLUMN, -1)); |
| 30 return new AnalysisError(json[ERROR_CODE], json[SEVERITY], json[TYPE], locatio
n, |
| 31 json['message'], json['correction']); |
| 32 } |
| 22 | 33 |
| 23 | 34 |
| 24 main() { | 35 main() { |
| 25 groupSep = ' | '; | 36 groupSep = ' | '; |
| 26 | 37 |
| 27 runReflectiveTests(AnalysisDomainTest); | 38 runReflectiveTests(AnalysisDomainTest); |
| 28 | 39 |
| 29 MockServerChannel serverChannel; | 40 MockServerChannel serverChannel; |
| 30 MemoryResourceProvider resourceProvider; | 41 MemoryResourceProvider resourceProvider; |
| 31 AnalysisServer server; | 42 AnalysisServer server; |
| (...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 150 request.setParameter(REMOVED, ['/dart/sdk-1.2']); | 161 request.setParameter(REMOVED, ['/dart/sdk-1.2']); |
| 151 request.setParameter(DEFAULT, '/dart/sdk-1.4'); | 162 request.setParameter(DEFAULT, '/dart/sdk-1.4'); |
| 152 var response = handler.handleRequest(request); | 163 var response = handler.handleRequest(request); |
| 153 // TODO(scheglov) implement | 164 // TODO(scheglov) implement |
| 154 expect(response, isNull); | 165 expect(response, isNull); |
| 155 }); | 166 }); |
| 156 }); | 167 }); |
| 157 } | 168 } |
| 158 | 169 |
| 159 | 170 |
| 160 class AnalysisError { | 171 testNotificationErrors() { |
| 161 final String errorCode; | 172 AnalysisTestHelper helper; |
| 162 final String severity; | |
| 163 final String type; | |
| 164 final Location location; | |
| 165 final String message; | |
| 166 final String correction; | |
| 167 AnalysisError(this.errorCode, this.severity, this.type, this.location, | |
| 168 this.message, this.correction); | |
| 169 | 173 |
| 170 @override | 174 setUp(() { |
| 171 String toString() { | 175 helper = new AnalysisTestHelper(); |
| 172 return 'AnalysisError(location=$location message=$message); ' | 176 }); |
| 173 'errorCode=$errorCode; severity=$separator type=$type'; | 177 |
| 174 } | 178 test('ParserErrorCode', () { |
| 179 helper.createSingleFileProject('library lib'); |
| 180 return helper.waitForOperationsFinished().then((_) { |
| 181 List<AnalysisError> errors = helper.getTestErrors(); |
| 182 expect(errors, hasLength(1)); |
| 183 AnalysisError error = errors[0]; |
| 184 expect(error.location.file, '/project/bin/test.dart'); |
| 185 expect(error.location.offset, isPositive); |
| 186 expect(error.location.length, isNonNegative); |
| 187 expect(error.errorCode, 'ParserErrorCode.EXPECTED_TOKEN'); |
| 188 expect(error.severity, 'ERROR'); |
| 189 expect(error.type, 'SYNTACTIC_ERROR'); |
| 190 expect(error.message, isNotNull); |
| 191 }); |
| 192 }); |
| 193 |
| 194 test('StaticWarningCode', () { |
| 195 helper.createSingleFileProject(['main() {', ' print(unknown);', '}']); |
| 196 return helper.waitForOperationsFinished().then((_) { |
| 197 List<AnalysisError> errors = helper.getTestErrors(); |
| 198 expect(errors, hasLength(1)); |
| 199 AnalysisError error = errors[0]; |
| 200 expect(error.errorCode, 'StaticWarningCode.UNDEFINED_IDENTIFIER'); |
| 201 }); |
| 202 }); |
| 175 } | 203 } |
| 176 | 204 |
| 177 | 205 |
| 178 AnalysisError jsonToAnalysisError(Map<String, Object> json) { | 206 testUpdateContent() { |
| 179 Map<String, Object> jsonLocation = json[LOCATION]; | 207 test('full content', () { |
| 180 Location location = new Location(jsonLocation[FILE], _getSafeInt(jsonLocation, | 208 AnalysisTestHelper helper = new AnalysisTestHelper(); |
| 181 OFFSET, -1), _getSafeInt(jsonLocation, LENGTH, -1), _getSafeInt(jsonLocati
on, | 209 helper.createSingleFileProject('// empty'); |
| 182 START_LINE, -1), _getSafeInt(jsonLocation, START_COLUMN, -1)); | 210 return helper.waitForOperationsFinished().then((_) { |
| 183 return new AnalysisError(json[ERROR_CODE], json[SEVERITY], json[TYPE], locatio
n, | 211 // no errors initially |
| 184 json['message'], json['correction']); | 212 List<AnalysisError> errors = helper.getTestErrors(); |
| 213 expect(errors, isEmpty); |
| 214 // update code |
| 215 helper.sendContentChange({ |
| 216 CONTENT: 'library lib' |
| 217 }); |
| 218 // wait, there is an error |
| 219 return helper.waitForOperationsFinished().then((_) { |
| 220 List<AnalysisError> errors = helper.getTestErrors(); |
| 221 expect(errors, hasLength(1)); |
| 222 }); |
| 223 }); |
| 224 }); |
| 225 |
| 226 test('incremental', () { |
| 227 AnalysisTestHelper helper = new AnalysisTestHelper(); |
| 228 helper.createSingleFileProject('library A;'); |
| 229 return helper.waitForOperationsFinished().then((_) { |
| 230 // no errors initially |
| 231 List<AnalysisError> errors = helper.getTestErrors(); |
| 232 expect(errors, isEmpty); |
| 233 // update code |
| 234 helper.sendContentChange({ |
| 235 CONTENT: 'library lib', |
| 236 OFFSET: 'library '.length, |
| 237 OLD_LENGTH: 'A;'.length, |
| 238 NEW_LENGTH: 'lib'.length, |
| 239 }); |
| 240 // wait, there is an error |
| 241 return helper.waitForOperationsFinished().then((_) { |
| 242 List<AnalysisError> errors = helper.getTestErrors(); |
| 243 expect(errors, hasLength(1)); |
| 244 }); |
| 245 }); |
| 246 }); |
| 247 |
| 248 test('change on disk, normal', () { |
| 249 AnalysisTestHelper helper = new AnalysisTestHelper(); |
| 250 helper.createSingleFileProject('library A;'); |
| 251 return helper.waitForOperationsFinished().then((_) { |
| 252 // There should be no errors |
| 253 expect(helper.getTestErrors(), hasLength(0)); |
| 254 // Change file on disk, adding a syntax error. |
| 255 helper.resourceProvider.modifyFile(helper.testFile, 'library lib'); |
| 256 // There should be errors now. |
| 257 return pumpEventQueue().then((_) { |
| 258 return helper.waitForOperationsFinished().then((_) { |
| 259 expect(helper.getTestErrors(), hasLength(1)); |
| 260 }); |
| 261 }); |
| 262 }); |
| 263 }); |
| 264 |
| 265 test('change on disk, during override', () { |
| 266 AnalysisTestHelper helper = new AnalysisTestHelper(); |
| 267 helper.createSingleFileProject('library A;'); |
| 268 return helper.waitForOperationsFinished().then((_) { |
| 269 // update code |
| 270 helper.sendContentChange({ |
| 271 CONTENT: 'library B;' |
| 272 }); |
| 273 // There should be no errors |
| 274 return helper.waitForOperationsFinished().then((_) { |
| 275 expect(helper.getTestErrors(), hasLength(0)); |
| 276 // Change file on disk, adding a syntax error. |
| 277 helper.resourceProvider.modifyFile(helper.testFile, 'library lib'); |
| 278 // There should still be no errors (file should not have been reread). |
| 279 return helper.waitForOperationsFinished().then((_) { |
| 280 expect(helper.getTestErrors(), hasLength(0)); |
| 281 // Send a content change with a null content param--file should be |
| 282 // reread from disk. |
| 283 helper.sendContentChange({ |
| 284 CONTENT: null |
| 285 }); |
| 286 // There should be errors now. |
| 287 return helper.waitForOperationsFinished().then((_) { |
| 288 expect(helper.getTestErrors(), hasLength(1)); |
| 289 }); |
| 290 }); |
| 291 }); |
| 292 }); |
| 293 }); |
| 185 } | 294 } |
| 186 | 295 |
| 187 | 296 |
| 297 void test_setSubscriptions() { |
| 298 test('before analysis', () { |
| 299 AnalysisTestHelper helper = new AnalysisTestHelper(); |
| 300 // subscribe |
| 301 helper.addAnalysisSubscriptionHighlights(helper.testFile); |
| 302 // create project |
| 303 helper.createSingleFileProject('int V = 42;'); |
| 304 // wait, there are highlight regions |
| 305 helper.waitForOperationsFinished().then((_) { |
| 306 var highlights = helper.getHighlights(helper.testFile); |
| 307 expect(highlights, isNot(isEmpty)); |
| 308 }); |
| 309 }); |
| 310 |
| 311 test('after analysis', () { |
| 312 AnalysisTestHelper helper = new AnalysisTestHelper(); |
| 313 // create project |
| 314 helper.createSingleFileProject('int V = 42;'); |
| 315 // wait, no regions initially |
| 316 return helper.waitForOperationsFinished().then((_) { |
| 317 var highlights = helper.getHighlights(helper.testFile); |
| 318 expect(highlights, isEmpty); |
| 319 // subscribe |
| 320 helper.addAnalysisSubscriptionHighlights(helper.testFile); |
| 321 // wait, has regions |
| 322 return helper.waitForOperationsFinished().then((_) { |
| 323 var highlights = helper.getHighlights(helper.testFile); |
| 324 expect(highlights, isNot(isEmpty)); |
| 325 }); |
| 326 }); |
| 327 }); |
| 328 } |
| 329 |
| 330 |
| 188 int _getSafeInt(Map<String, Object> json, String key, int defaultValue) { | 331 int _getSafeInt(Map<String, Object> json, String key, int defaultValue) { |
| 189 Object value = json[key]; | 332 Object value = json[key]; |
| 190 if (value is int) { | 333 if (value is int) { |
| 191 return value; | 334 return value; |
| 192 } | 335 } |
| 193 return defaultValue; | 336 return defaultValue; |
| 194 } | 337 } |
| 195 | 338 |
| 196 | 339 |
| 340 @ReflectiveTestCase() |
| 341 class AnalysisDomainTest extends AbstractAnalysisTest { |
| 342 Map<String, List<AnalysisError>> filesErrors = {}; |
| 343 |
| 344 void processNotification(Notification notification) { |
| 345 if (notification.event == ANALYSIS_ERRORS) { |
| 346 String file = notification.getParameter(FILE); |
| 347 List<Map<String, Object>> errorMaps = notification.getParameter(ERRORS); |
| 348 filesErrors[file] = errorMaps.map(jsonToAnalysisError).toList(); |
| 349 } |
| 350 } |
| 351 |
| 352 test_packageMapDependencies() { |
| 353 // Prepare a source file that has errors because it refers to an unknown |
| 354 // package. |
| 355 String pkgFile = '/packages/pkgA/libA.dart'; |
| 356 resourceProvider.newFile(pkgFile, ''' |
| 357 library lib_a; |
| 358 class A {} |
| 359 '''); |
| 360 addTestFile(''' |
| 361 import 'package:pkgA/libA.dart'; |
| 362 f(A a) { |
| 363 } |
| 364 '''); |
| 365 String pkgDependency = posix.join(projectPath, 'package_dep'); |
| 366 resourceProvider.newFile(pkgDependency, 'contents'); |
| 367 packageMapProvider.dependencies.add(pkgDependency); |
| 368 // Create project and wait for analysis |
| 369 createProject(); |
| 370 return waitForTasksFinished().then((_) { |
| 371 expect(filesErrors[testFile], isNot(isEmpty)); |
| 372 // Add the package to the package map and tickle the package dependency. |
| 373 packageMapProvider.packageMap = { |
| 374 'pkgA': [resourceProvider.getResource('/packages/pkgA')] |
| 375 }; |
| 376 resourceProvider.modifyFile(pkgDependency, 'new contents'); |
| 377 // Let the server time to notice the file has changed, then let |
| 378 // analysis omplete. There should now be no error. |
| 379 return pumpEventQueue().then((_) => waitForTasksFinished()).then((_) { |
| 380 expect(filesErrors[testFile], isEmpty); |
| 381 }); |
| 382 }); |
| 383 } |
| 384 |
| 385 test_setRoots_packages() { |
| 386 // prepare package |
| 387 String pkgFile = '/packages/pkgA/libA.dart'; |
| 388 resourceProvider.newFile(pkgFile, ''' |
| 389 library lib_a; |
| 390 class A {} |
| 391 '''); |
| 392 packageMapProvider.packageMap['pkgA'] = [resourceProvider.getResource( |
| 393 '/packages/pkgA')]; |
| 394 addTestFile(''' |
| 395 import 'package:pkgA/libA.dart'; |
| 396 main(A a) { |
| 397 } |
| 398 '''); |
| 399 // create project and wait for analysis |
| 400 createProject(); |
| 401 return waitForTasksFinished().then((_) { |
| 402 // if 'package:pkgA/libA.dart' was resolved, then there are no errors |
| 403 expect(filesErrors[testFile], isEmpty); |
| 404 // packages file also was resolved |
| 405 expect(filesErrors[pkgFile], isEmpty); |
| 406 }); |
| 407 } |
| 408 } |
| 409 |
| 410 class AnalysisError { |
| 411 final String errorCode; |
| 412 final String severity; |
| 413 final String type; |
| 414 final Location location; |
| 415 final String message; |
| 416 final String correction; |
| 417 AnalysisError(this.errorCode, this.severity, this.type, this.location, |
| 418 this.message, this.correction); |
| 419 |
| 420 @override |
| 421 String toString() { |
| 422 return 'AnalysisError(location=$location message=$message); ' |
| 423 'errorCode=$errorCode; severity=$separator type=$type'; |
| 424 } |
| 425 } |
| 426 |
| 427 |
| 197 /** | 428 /** |
| 198 * A helper to test 'analysis.*' requests. | 429 * A helper to test 'analysis.*' requests. |
| 199 */ | 430 */ |
| 200 class AnalysisTestHelper { | 431 class AnalysisTestHelper { |
| 201 MockServerChannel serverChannel; | 432 MockServerChannel serverChannel; |
| 202 MemoryResourceProvider resourceProvider; | 433 MemoryResourceProvider resourceProvider; |
| 203 AnalysisServer server; | 434 AnalysisServer server; |
| 204 AnalysisDomainHandler handler; | 435 AnalysisDomainHandler handler; |
| 205 | 436 |
| 206 Map<String, List<String>> analysisSubscriptions = {}; | 437 Map<String, List<String>> analysisSubscriptions = {}; |
| (...skipping 25 matching lines...) Expand all Loading... |
| 232 String file = notification.getParameter(FILE); | 463 String file = notification.getParameter(FILE); |
| 233 filesHighlights[file] = notification.getParameter(REGIONS); | 464 filesHighlights[file] = notification.getParameter(REGIONS); |
| 234 } | 465 } |
| 235 if (notification.event == ANALYSIS_NAVIGATION) { | 466 if (notification.event == ANALYSIS_NAVIGATION) { |
| 236 String file = notification.getParameter(FILE); | 467 String file = notification.getParameter(FILE); |
| 237 filesNavigation[file] = notification.getParameter(REGIONS); | 468 filesNavigation[file] = notification.getParameter(REGIONS); |
| 238 } | 469 } |
| 239 }); | 470 }); |
| 240 } | 471 } |
| 241 | 472 |
| 242 void addAnalysisSubscriptionHighlights(String file) { | |
| 243 addAnalysisSubscription(AnalysisService.HIGHLIGHTS, file); | |
| 244 } | |
| 245 | |
| 246 void addAnalysisSubscriptionNavigation(String file) { | |
| 247 addAnalysisSubscription(AnalysisService.NAVIGATION, file); | |
| 248 } | |
| 249 | |
| 250 void addAnalysisSubscription(AnalysisService service, String file) { | 473 void addAnalysisSubscription(AnalysisService service, String file) { |
| 251 // add file to subscription | 474 // add file to subscription |
| 252 var files = analysisSubscriptions[service.name]; | 475 var files = analysisSubscriptions[service.name]; |
| 253 if (files == null) { | 476 if (files == null) { |
| 254 files = <String>[]; | 477 files = <String>[]; |
| 255 analysisSubscriptions[service.name] = files; | 478 analysisSubscriptions[service.name] = files; |
| 256 } | 479 } |
| 257 files.add(file); | 480 files.add(file); |
| 258 // set subscriptions | 481 // set subscriptions |
| 259 Request request = new Request('0', ANALYSIS_SET_SUBSCRIPTIONS); | 482 Request request = new Request('0', ANALYSIS_SET_SUBSCRIPTIONS); |
| 260 request.setParameter(SUBSCRIPTIONS, analysisSubscriptions); | 483 request.setParameter(SUBSCRIPTIONS, analysisSubscriptions); |
| 261 handleSuccessfulRequest(request); | 484 handleSuccessfulRequest(request); |
| 262 } | 485 } |
| 263 | 486 |
| 264 /** | 487 void addAnalysisSubscriptionHighlights(String file) { |
| 265 * Returns a [Future] that completes when this this helper finished all its | 488 addAnalysisSubscription(AnalysisService.HIGHLIGHTS, file); |
| 266 * scheduled tasks. | 489 } |
| 267 */ | 490 |
| 268 Future waitForOperationsFinished() { | 491 void addAnalysisSubscriptionNavigation(String file) { |
| 269 return waitForServerOperationsPerformed(server); | 492 addAnalysisSubscription(AnalysisService.NAVIGATION, file); |
| 270 } | 493 } |
| 271 | 494 |
| 272 /** | 495 /** |
| 496 * Creates an empty project `/project`. |
| 497 */ |
| 498 void createEmptyProject() { |
| 499 resourceProvider.newFolder('/project'); |
| 500 Request request = new Request('0', ANALYSIS_SET_ANALYSIS_ROOTS); |
| 501 request.setParameter(INCLUDED, ['/project']); |
| 502 request.setParameter(EXCLUDED, []); |
| 503 handleSuccessfulRequest(request); |
| 504 } |
| 505 |
| 506 /** |
| 507 * Creates a project with a single Dart file `/project/bin/test.dart` with |
| 508 * the given [code]. |
| 509 */ |
| 510 void createSingleFileProject(code) { |
| 511 this.testCode = _getCodeString(code); |
| 512 resourceProvider.newFolder('/project'); |
| 513 resourceProvider.newFile(testFile, testCode); |
| 514 Request request = new Request('0', ANALYSIS_SET_ANALYSIS_ROOTS); |
| 515 request.setParameter(INCLUDED, ['/project']); |
| 516 request.setParameter(EXCLUDED, []); |
| 517 handleSuccessfulRequest(request); |
| 518 } |
| 519 |
| 520 /** |
| 273 * Returns the offset of [search] in [testCode]. | 521 * Returns the offset of [search] in [testCode]. |
| 274 * Fails if not found. | 522 * Fails if not found. |
| 275 */ | 523 */ |
| 276 int findOffset(String search) { | 524 int findOffset(String search) { |
| 277 int offset = testCode.indexOf(search); | 525 int offset = testCode.indexOf(search); |
| 278 expect(offset, isNot(-1)); | 526 expect(offset, isNot(-1)); |
| 279 return offset; | 527 return offset; |
| 280 } | 528 } |
| 281 | 529 |
| 282 /** | 530 /** |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 333 | 581 |
| 334 /** | 582 /** |
| 335 * Returns navigation information recorded for the given [testFile]. | 583 * Returns navigation information recorded for the given [testFile]. |
| 336 * May be empty, but not `null`. | 584 * May be empty, but not `null`. |
| 337 */ | 585 */ |
| 338 List<Map<String, Object>> getTestNavigation() { | 586 List<Map<String, Object>> getTestNavigation() { |
| 339 return getNavigation(testFile); | 587 return getNavigation(testFile); |
| 340 } | 588 } |
| 341 | 589 |
| 342 /** | 590 /** |
| 343 * Creates an empty project `/project`. | 591 * Validates that the given [request] is handled successfully. |
| 344 */ | 592 */ |
| 345 void createEmptyProject() { | 593 void handleSuccessfulRequest(Request request) { |
| 346 resourceProvider.newFolder('/project'); | 594 Response response = handler.handleRequest(request); |
| 347 Request request = new Request('0', ANALYSIS_SET_ANALYSIS_ROOTS); | 595 expect(response, isResponseSuccess('0')); |
| 348 request.setParameter(INCLUDED, ['/project']); | 596 } |
| 349 request.setParameter(EXCLUDED, []); | 597 |
| 598 /** |
| 599 * Send an `updateContent` request for [testFile]. |
| 600 */ |
| 601 void sendContentChange(Map contentChange) { |
| 602 Request request = new Request('0', ANALYSIS_UPDATE_CONTENT); |
| 603 request.setParameter('files', { |
| 604 testFile: contentChange |
| 605 }); |
| 350 handleSuccessfulRequest(request); | 606 handleSuccessfulRequest(request); |
| 351 } | 607 } |
| 352 | 608 |
| 353 /** | |
| 354 * Creates a project with a single Dart file `/project/bin/test.dart` with | |
| 355 * the given [code]. | |
| 356 */ | |
| 357 void createSingleFileProject(code) { | |
| 358 this.testCode = _getCodeString(code); | |
| 359 resourceProvider.newFolder('/project'); | |
| 360 resourceProvider.newFile(testFile, testCode); | |
| 361 Request request = new Request('0', ANALYSIS_SET_ANALYSIS_ROOTS); | |
| 362 request.setParameter(INCLUDED, ['/project']); | |
| 363 request.setParameter(EXCLUDED, []); | |
| 364 handleSuccessfulRequest(request); | |
| 365 } | |
| 366 | |
| 367 String setFileContent(String path, String content) { | 609 String setFileContent(String path, String content) { |
| 368 resourceProvider.newFile(path, content); | 610 resourceProvider.newFile(path, content); |
| 369 return path; | 611 return path; |
| 370 } | 612 } |
| 371 | 613 |
| 372 /** | 614 /** |
| 373 * Validates that the given [request] is handled successfully. | |
| 374 */ | |
| 375 void handleSuccessfulRequest(Request request) { | |
| 376 Response response = handler.handleRequest(request); | |
| 377 expect(response, isResponseSuccess('0')); | |
| 378 } | |
| 379 | |
| 380 /** | |
| 381 * Stops the associated server. | 615 * Stops the associated server. |
| 382 */ | 616 */ |
| 383 void stopServer() { | 617 void stopServer() { |
| 384 server.done(); | 618 server.done(); |
| 385 } | 619 } |
| 386 | 620 |
| 387 /** | 621 /** |
| 388 * Send an `updateContent` request for [testFile]. | 622 * Returns a [Future] that completes when this this helper finished all its |
| 623 * scheduled tasks. |
| 389 */ | 624 */ |
| 390 void sendContentChange(Map contentChange) { | 625 Future waitForOperationsFinished() { |
| 391 Request request = new Request('0', ANALYSIS_UPDATE_CONTENT); | 626 return waitForServerOperationsPerformed(server); |
| 392 request.setParameter('files', { | |
| 393 testFile: contentChange | |
| 394 }); | |
| 395 handleSuccessfulRequest(request); | |
| 396 } | 627 } |
| 397 | 628 |
| 398 static String _getCodeString(code) { | 629 static String _getCodeString(code) { |
| 399 if (code is List<String>) { | 630 if (code is List<String>) { |
| 400 code = code.join('\n'); | 631 code = code.join('\n'); |
| 401 } | 632 } |
| 402 return code as String; | 633 return code as String; |
| 403 } | 634 } |
| 404 } | 635 } |
| 405 | |
| 406 | |
| 407 testNotificationErrors() { | |
| 408 AnalysisTestHelper helper; | |
| 409 | |
| 410 setUp(() { | |
| 411 helper = new AnalysisTestHelper(); | |
| 412 }); | |
| 413 | |
| 414 test('ParserErrorCode', () { | |
| 415 helper.createSingleFileProject('library lib'); | |
| 416 return helper.waitForOperationsFinished().then((_) { | |
| 417 List<AnalysisError> errors = helper.getTestErrors(); | |
| 418 expect(errors, hasLength(1)); | |
| 419 AnalysisError error = errors[0]; | |
| 420 expect(error.location.file, '/project/bin/test.dart'); | |
| 421 expect(error.location.offset, isPositive); | |
| 422 expect(error.location.length, isNonNegative); | |
| 423 expect(error.errorCode, 'ParserErrorCode.EXPECTED_TOKEN'); | |
| 424 expect(error.severity, 'ERROR'); | |
| 425 expect(error.type, 'SYNTACTIC_ERROR'); | |
| 426 expect(error.message, isNotNull); | |
| 427 }); | |
| 428 }); | |
| 429 | |
| 430 test('StaticWarningCode', () { | |
| 431 helper.createSingleFileProject(['main() {', ' print(unknown);', '}']); | |
| 432 return helper.waitForOperationsFinished().then((_) { | |
| 433 List<AnalysisError> errors = helper.getTestErrors(); | |
| 434 expect(errors, hasLength(1)); | |
| 435 AnalysisError error = errors[0]; | |
| 436 expect(error.errorCode, 'StaticWarningCode.UNDEFINED_IDENTIFIER'); | |
| 437 }); | |
| 438 }); | |
| 439 } | |
| 440 | |
| 441 | |
| 442 testUpdateContent() { | |
| 443 test('full content', () { | |
| 444 AnalysisTestHelper helper = new AnalysisTestHelper(); | |
| 445 helper.createSingleFileProject('// empty'); | |
| 446 return helper.waitForOperationsFinished().then((_) { | |
| 447 // no errors initially | |
| 448 List<AnalysisError> errors = helper.getTestErrors(); | |
| 449 expect(errors, isEmpty); | |
| 450 // update code | |
| 451 helper.sendContentChange({ | |
| 452 CONTENT: 'library lib' | |
| 453 }); | |
| 454 // wait, there is an error | |
| 455 return helper.waitForOperationsFinished().then((_) { | |
| 456 List<AnalysisError> errors = helper.getTestErrors(); | |
| 457 expect(errors, hasLength(1)); | |
| 458 }); | |
| 459 }); | |
| 460 }); | |
| 461 | |
| 462 test('incremental', () { | |
| 463 AnalysisTestHelper helper = new AnalysisTestHelper(); | |
| 464 helper.createSingleFileProject('library A;'); | |
| 465 return helper.waitForOperationsFinished().then((_) { | |
| 466 // no errors initially | |
| 467 List<AnalysisError> errors = helper.getTestErrors(); | |
| 468 expect(errors, isEmpty); | |
| 469 // update code | |
| 470 helper.sendContentChange({ | |
| 471 CONTENT: 'library lib', | |
| 472 OFFSET: 'library '.length, | |
| 473 OLD_LENGTH: 'A;'.length, | |
| 474 NEW_LENGTH: 'lib'.length, | |
| 475 }); | |
| 476 // wait, there is an error | |
| 477 return helper.waitForOperationsFinished().then((_) { | |
| 478 List<AnalysisError> errors = helper.getTestErrors(); | |
| 479 expect(errors, hasLength(1)); | |
| 480 }); | |
| 481 }); | |
| 482 }); | |
| 483 | |
| 484 test('change on disk, normal', () { | |
| 485 AnalysisTestHelper helper = new AnalysisTestHelper(); | |
| 486 helper.createSingleFileProject('library A;'); | |
| 487 return helper.waitForOperationsFinished().then((_) { | |
| 488 // There should be no errors | |
| 489 expect(helper.getTestErrors(), hasLength(0)); | |
| 490 // Change file on disk, adding a syntax error. | |
| 491 helper.resourceProvider.modifyFile(helper.testFile, 'library lib'); | |
| 492 // There should be errors now. | |
| 493 return pumpEventQueue().then((_) { | |
| 494 return helper.waitForOperationsFinished().then((_) { | |
| 495 expect(helper.getTestErrors(), hasLength(1)); | |
| 496 }); | |
| 497 }); | |
| 498 }); | |
| 499 }); | |
| 500 | |
| 501 test('change on disk, during override', () { | |
| 502 AnalysisTestHelper helper = new AnalysisTestHelper(); | |
| 503 helper.createSingleFileProject('library A;'); | |
| 504 return helper.waitForOperationsFinished().then((_) { | |
| 505 // update code | |
| 506 helper.sendContentChange({ | |
| 507 CONTENT: 'library B;' | |
| 508 }); | |
| 509 // There should be no errors | |
| 510 return helper.waitForOperationsFinished().then((_) { | |
| 511 expect(helper.getTestErrors(), hasLength(0)); | |
| 512 // Change file on disk, adding a syntax error. | |
| 513 helper.resourceProvider.modifyFile(helper.testFile, 'library lib'); | |
| 514 // There should still be no errors (file should not have been reread). | |
| 515 return helper.waitForOperationsFinished().then((_) { | |
| 516 expect(helper.getTestErrors(), hasLength(0)); | |
| 517 // Send a content change with a null content param--file should be | |
| 518 // reread from disk. | |
| 519 helper.sendContentChange({ | |
| 520 CONTENT: null | |
| 521 }); | |
| 522 // There should be errors now. | |
| 523 return helper.waitForOperationsFinished().then((_) { | |
| 524 expect(helper.getTestErrors(), hasLength(1)); | |
| 525 }); | |
| 526 }); | |
| 527 }); | |
| 528 }); | |
| 529 }); | |
| 530 } | |
| 531 | |
| 532 void test_setSubscriptions() { | |
| 533 test('before analysis', () { | |
| 534 AnalysisTestHelper helper = new AnalysisTestHelper(); | |
| 535 // subscribe | |
| 536 helper.addAnalysisSubscriptionHighlights(helper.testFile); | |
| 537 // create project | |
| 538 helper.createSingleFileProject('int V = 42;'); | |
| 539 // wait, there are highlight regions | |
| 540 helper.waitForOperationsFinished().then((_) { | |
| 541 var highlights = helper.getHighlights(helper.testFile); | |
| 542 expect(highlights, isNot(isEmpty)); | |
| 543 }); | |
| 544 }); | |
| 545 | |
| 546 test('after analysis', () { | |
| 547 AnalysisTestHelper helper = new AnalysisTestHelper(); | |
| 548 // create project | |
| 549 helper.createSingleFileProject('int V = 42;'); | |
| 550 // wait, no regions initially | |
| 551 return helper.waitForOperationsFinished().then((_) { | |
| 552 var highlights = helper.getHighlights(helper.testFile); | |
| 553 expect(highlights, isEmpty); | |
| 554 // subscribe | |
| 555 helper.addAnalysisSubscriptionHighlights(helper.testFile); | |
| 556 // wait, has regions | |
| 557 return helper.waitForOperationsFinished().then((_) { | |
| 558 var highlights = helper.getHighlights(helper.testFile); | |
| 559 expect(highlights, isNot(isEmpty)); | |
| 560 }); | |
| 561 }); | |
| 562 }); | |
| 563 } | |
| 564 | |
| 565 | |
| 566 @ReflectiveTestCase() | |
| 567 class AnalysisDomainTest extends AbstractAnalysisTest { | |
| 568 Map<String, List<AnalysisError>> filesErrors = {}; | |
| 569 | |
| 570 void processNotification(Notification notification) { | |
| 571 if (notification.event == ANALYSIS_ERRORS) { | |
| 572 String file = notification.getParameter(FILE); | |
| 573 List<Map<String, Object>> errorMaps = notification.getParameter(ERRORS); | |
| 574 filesErrors[file] = errorMaps.map(jsonToAnalysisError).toList(); | |
| 575 } | |
| 576 } | |
| 577 | |
| 578 test_setRoots_packages() { | |
| 579 // prepare package | |
| 580 String pkgFile = '/packages/pkgA/libA.dart'; | |
| 581 resourceProvider.newFile(pkgFile, ''' | |
| 582 library lib_a; | |
| 583 class A {} | |
| 584 '''); | |
| 585 packageMapProvider.packageMap['pkgA'] = [resourceProvider.getResource( | |
| 586 '/packages/pkgA')]; | |
| 587 addTestFile(''' | |
| 588 import 'package:pkgA/libA.dart'; | |
| 589 main(A a) { | |
| 590 } | |
| 591 '''); | |
| 592 // create project and wait for analysis | |
| 593 createProject(); | |
| 594 return waitForTasksFinished().then((_) { | |
| 595 // if 'package:pkgA/libA.dart' was resolved, then there are no errors | |
| 596 expect(filesErrors[testFile], isEmpty); | |
| 597 // packages file also was resolved | |
| 598 expect(filesErrors[pkgFile], isEmpty); | |
| 599 }); | |
| 600 } | |
| 601 | |
| 602 test_packageMapDependencies() { | |
| 603 // Prepare a source file that has errors because it refers to an unknown | |
| 604 // package. | |
| 605 String pkgFile = '/packages/pkgA/libA.dart'; | |
| 606 resourceProvider.newFile(pkgFile, ''' | |
| 607 library lib_a; | |
| 608 class A {} | |
| 609 '''); | |
| 610 addTestFile(''' | |
| 611 import 'package:pkgA/libA.dart'; | |
| 612 f(A a) { | |
| 613 } | |
| 614 '''); | |
| 615 String pkgDependency = posix.join(projectPath, 'package_dep'); | |
| 616 resourceProvider.newFile(pkgDependency, 'contents'); | |
| 617 packageMapProvider.dependencies.add(pkgDependency); | |
| 618 // Create project and wait for analysis | |
| 619 createProject(); | |
| 620 return waitForTasksFinished().then((_) { | |
| 621 expect(filesErrors[testFile], isNot(isEmpty)); | |
| 622 // Add the package to the package map and tickle the package dependency. | |
| 623 packageMapProvider.packageMap = { | |
| 624 'pkgA': [resourceProvider.getResource('/packages/pkgA')] | |
| 625 }; | |
| 626 resourceProvider.modifyFile(pkgDependency, 'new contents'); | |
| 627 // Let the server time to notice the file has changed, then let | |
| 628 // analysis omplete. There should now be no error. | |
| 629 return pumpEventQueue().then((_) => waitForTasksFinished()).then((_) { | |
| 630 expect(filesErrors[testFile], isEmpty); | |
| 631 }); | |
| 632 }); | |
| 633 } | |
| 634 } | |
| OLD | NEW |