OLD | NEW |
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, 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.analysis.updateContent; | 5 library test.analysis.updateContent; |
6 | 6 |
7 import 'package:analysis_server/src/constants.dart'; | 7 import 'package:analysis_server/src/constants.dart'; |
8 import 'package:analysis_server/src/protocol.dart'; | 8 import 'package:analysis_server/src/protocol.dart'; |
9 import 'package:analysis_server/src/services/index/index.dart'; | 9 import 'package:analysis_server/src/services/index/index.dart'; |
10 import 'package:analyzer/src/generated/ast.dart'; | 10 import 'package:analyzer/src/generated/ast.dart'; |
11 import 'package:typed_mock/typed_mock.dart'; | 11 import 'package:typed_mock/typed_mock.dart'; |
12 import 'package:unittest/unittest.dart'; | 12 import 'package:unittest/unittest.dart'; |
13 | 13 |
14 import '../analysis_abstract.dart'; | 14 import '../analysis_abstract.dart'; |
15 import '../reflective_tests.dart'; | 15 import '../reflective_tests.dart'; |
16 | 16 |
17 | |
18 main() { | 17 main() { |
19 groupSep = ' | '; | 18 groupSep = ' | '; |
20 runReflectiveTests(UpdateContentTest); | 19 runReflectiveTests(UpdateContentTest); |
21 } | 20 } |
22 | 21 |
23 | |
24 compilationUnitMatcher(String file) { | 22 compilationUnitMatcher(String file) { |
25 return new _ArgumentMatcher_CompilationUnit(file); | 23 return new _ArgumentMatcher_CompilationUnit(file); |
26 } | 24 } |
27 | 25 |
28 | |
29 @reflectiveTest | 26 @reflectiveTest |
30 class UpdateContentTest extends AbstractAnalysisTest { | 27 class UpdateContentTest extends AbstractAnalysisTest { |
31 Map<String, List<AnalysisError>> filesErrors = {}; | 28 Map<String, List<AnalysisError>> filesErrors = {}; |
32 int serverErrorCount = 0; | 29 int serverErrorCount = 0; |
33 int navigationCount = 0; | 30 int navigationCount = 0; |
34 | 31 |
35 Index createIndex() { | 32 Index createIndex() { |
36 return new _MockIndex(); | 33 return new _MockIndex(); |
37 } | 34 } |
38 | 35 |
39 @override | 36 @override |
40 void processNotification(Notification notification) { | 37 void processNotification(Notification notification) { |
41 if (notification.event == ANALYSIS_ERRORS) { | 38 if (notification.event == ANALYSIS_ERRORS) { |
42 var decoded = new AnalysisErrorsParams.fromNotification(notification); | 39 var decoded = new AnalysisErrorsParams.fromNotification(notification); |
43 filesErrors[decoded.file] = decoded.errors; | 40 filesErrors[decoded.file] = decoded.errors; |
44 } | 41 } |
45 if (notification.event == ANALYSIS_NAVIGATION) { | 42 if (notification.event == ANALYSIS_NAVIGATION) { |
46 navigationCount++; | 43 navigationCount++; |
47 } | 44 } |
48 if (notification.event == SERVER_ERROR) { | 45 if (notification.event == SERVER_ERROR) { |
49 serverErrorCount++; | 46 serverErrorCount++; |
50 } | 47 } |
51 } | 48 } |
52 | 49 |
53 test_discardNotifications_onSourceChange() async { | 50 test_discardNotifications_onSourceChange() async { |
54 createProject(); | 51 createProject(); |
55 addTestFile(''); | 52 addTestFile(''); |
56 await server.onAnalysisComplete; | 53 await server.onAnalysisComplete; |
57 server.setAnalysisSubscriptions({ | 54 server.setAnalysisSubscriptions( |
58 AnalysisService.NAVIGATION: [testFile].toSet() | 55 {AnalysisService.NAVIGATION: [testFile].toSet()}); |
59 }); | |
60 // update file, analyze, but don't sent notifications | 56 // update file, analyze, but don't sent notifications |
61 navigationCount = 0; | 57 navigationCount = 0; |
62 server.updateContent('1', { | 58 server.updateContent('1', {testFile: new AddContentOverlay('foo() {}')}); |
63 testFile: new AddContentOverlay('foo() {}') | |
64 }); | |
65 server.test_performAllAnalysisOperations(); | 59 server.test_performAllAnalysisOperations(); |
66 expect(serverErrorCount, 0); | 60 expect(serverErrorCount, 0); |
67 expect(navigationCount, 0); | 61 expect(navigationCount, 0); |
68 // replace the file contents, | 62 // replace the file contents, |
69 // should discard any pending notification operations | 63 // should discard any pending notification operations |
70 server.updateContent('2', { | 64 server.updateContent('2', {testFile: new AddContentOverlay('bar() {}')}); |
71 testFile: new AddContentOverlay('bar() {}') | |
72 }); | |
73 await server.onAnalysisComplete; | 65 await server.onAnalysisComplete; |
74 expect(serverErrorCount, 0); | 66 expect(serverErrorCount, 0); |
75 expect(navigationCount, 1); | 67 expect(navigationCount, 1); |
76 } | 68 } |
77 | 69 |
78 test_illegal_ChangeContentOverlay() { | 70 test_illegal_ChangeContentOverlay() { |
79 // It should be illegal to send a ChangeContentOverlay for a file that | 71 // It should be illegal to send a ChangeContentOverlay for a file that |
80 // doesn't have an overlay yet. | 72 // doesn't have an overlay yet. |
81 createProject(); | 73 createProject(); |
82 addTestFile('library foo;'); | 74 addTestFile('library foo;'); |
83 String id = 'myId'; | 75 String id = 'myId'; |
84 try { | 76 try { |
85 server.updateContent(id, { | 77 server.updateContent(id, { |
86 testFile: new ChangeContentOverlay([new SourceEdit(8, 3, 'bar')]) | 78 testFile: new ChangeContentOverlay([new SourceEdit(8, 3, 'bar')]) |
87 }); | 79 }); |
88 fail('Expected an exception to be thrown'); | 80 fail('Expected an exception to be thrown'); |
89 } on RequestFailure catch (e) { | 81 } on RequestFailure catch (e) { |
90 expect(e.response.id, id); | 82 expect(e.response.id, id); |
91 expect(e.response.error.code, RequestErrorCode.INVALID_OVERLAY_CHANGE); | 83 expect(e.response.error.code, RequestErrorCode.INVALID_OVERLAY_CHANGE); |
92 } | 84 } |
93 } | 85 } |
94 | 86 |
95 test_indexUnitAfterNopChange() async { | 87 test_indexUnitAfterNopChange() async { |
96 var testUnitMatcher = compilationUnitMatcher(testFile) as dynamic; | 88 var testUnitMatcher = compilationUnitMatcher(testFile) as dynamic; |
97 createProject(); | 89 createProject(); |
98 addTestFile('main() { print(1); }'); | 90 addTestFile('main() { print(1); }'); |
99 await server.onAnalysisComplete; | 91 await server.onAnalysisComplete; |
100 verify(server.index.indexUnit(anyObject, testUnitMatcher)).times(1); | 92 verify(server.index.indexUnit(anyObject, testUnitMatcher)).times(1); |
101 // add an overlay | 93 // add an overlay |
102 server.updateContent('1', { | 94 server.updateContent( |
103 testFile: new AddContentOverlay('main() { print(2); }') | 95 '1', {testFile: new AddContentOverlay('main() { print(2); }')}); |
104 }); | |
105 // Perform the next single operation: analysis. | 96 // Perform the next single operation: analysis. |
106 // It will schedule an indexing operation. | 97 // It will schedule an indexing operation. |
107 await server.test_onOperationPerformed; | 98 await server.test_onOperationPerformed; |
108 // Update the file and remove an overlay. | 99 // Update the file and remove an overlay. |
109 resourceProvider.updateFile(testFile, 'main() { print(2); }'); | 100 resourceProvider.updateFile(testFile, 'main() { print(2); }'); |
110 server.updateContent('2', { | 101 server.updateContent('2', {testFile: new RemoveContentOverlay()}); |
111 testFile: new RemoveContentOverlay() | |
112 }); | |
113 // Validate that at the end the unit was indexed. | 102 // Validate that at the end the unit was indexed. |
114 await server.onAnalysisComplete; | 103 await server.onAnalysisComplete; |
115 verify(server.index.indexUnit(anyObject, testUnitMatcher)).times(2); | 104 verify(server.index.indexUnit(anyObject, testUnitMatcher)).times(2); |
116 } | 105 } |
117 | 106 |
118 test_multiple_contexts() { | 107 test_multiple_contexts() { |
119 String fooPath = '/project1/foo.dart'; | 108 String fooPath = '/project1/foo.dart'; |
120 resourceProvider.newFile(fooPath, ''' | 109 resourceProvider.newFile(fooPath, ''' |
121 library foo; | 110 library foo; |
122 import '../project2/baz.dart'; | 111 import '../project2/baz.dart'; |
123 main() { f(); }'''); | 112 main() { f(); }'''); |
124 String barPath = '/project2/bar.dart'; | 113 String barPath = '/project2/bar.dart'; |
125 resourceProvider.newFile(barPath, ''' | 114 resourceProvider.newFile(barPath, ''' |
126 library bar; | 115 library bar; |
127 import 'baz.dart'; | 116 import 'baz.dart'; |
128 main() { f(); }'''); | 117 main() { f(); }'''); |
129 String bazPath = '/project2/baz.dart'; | 118 String bazPath = '/project2/baz.dart'; |
130 resourceProvider.newFile(bazPath, ''' | 119 resourceProvider.newFile(bazPath, ''' |
131 library baz; | 120 library baz; |
132 f(int i) {} | 121 f(int i) {} |
133 '''); | 122 '''); |
134 Request request = new AnalysisSetAnalysisRootsParams( | 123 Request request = new AnalysisSetAnalysisRootsParams( |
135 ['/project1', '/project2'], | 124 ['/project1', '/project2'], []).toRequest('0'); |
136 []).toRequest('0'); | |
137 handleSuccessfulRequest(request); | 125 handleSuccessfulRequest(request); |
138 return waitForTasksFinished().then((_) { | 126 return waitForTasksFinished().then((_) { |
139 // Files foo.dart and bar.dart should both have errors, since they both | 127 // Files foo.dart and bar.dart should both have errors, since they both |
140 // call f() with the wrong number of arguments. | 128 // call f() with the wrong number of arguments. |
141 expect(filesErrors[fooPath], hasLength(1)); | 129 expect(filesErrors[fooPath], hasLength(1)); |
142 expect(filesErrors[barPath], hasLength(1)); | 130 expect(filesErrors[barPath], hasLength(1)); |
143 // Overlay the content of baz.dart to eliminate the errors. | 131 // Overlay the content of baz.dart to eliminate the errors. |
144 server.updateContent('1', { | 132 server.updateContent('1', { |
145 bazPath: new AddContentOverlay(''' | 133 bazPath: new AddContentOverlay(''' |
146 library baz; | 134 library baz; |
147 f() {} | 135 f() {} |
148 ''') | 136 ''') |
149 }); | 137 }); |
150 return waitForTasksFinished(); | 138 return waitForTasksFinished(); |
151 }).then((_) { | 139 }).then((_) { |
152 // The overlay should have been propagated to both contexts, causing both | 140 // The overlay should have been propagated to both contexts, causing both |
153 // foo.dart and bar.dart to be reanalyzed and found to be free of errors. | 141 // foo.dart and bar.dart to be reanalyzed and found to be free of errors. |
154 expect(filesErrors[fooPath], isEmpty); | 142 expect(filesErrors[fooPath], isEmpty); |
155 expect(filesErrors[barPath], isEmpty); | 143 expect(filesErrors[barPath], isEmpty); |
156 }); | 144 }); |
157 } | 145 } |
158 | 146 |
159 test_sendNoticesAfterNopChange() async { | 147 test_sendNoticesAfterNopChange() async { |
160 createProject(); | 148 createProject(); |
161 addTestFile(''); | 149 addTestFile(''); |
162 await server.onAnalysisComplete; | 150 await server.onAnalysisComplete; |
163 // add an overlay | 151 // add an overlay |
164 server.updateContent('1', { | 152 server.updateContent( |
165 testFile: new AddContentOverlay('main() {} main() {}') | 153 '1', {testFile: new AddContentOverlay('main() {} main() {}')}); |
166 }); | |
167 await server.onAnalysisComplete; | 154 await server.onAnalysisComplete; |
168 // clear errors and make a no-op change | 155 // clear errors and make a no-op change |
169 filesErrors.clear(); | 156 filesErrors.clear(); |
170 server.updateContent('2', { | 157 server.updateContent('2', { |
171 testFile: new ChangeContentOverlay([new SourceEdit(0, 4, 'main')]) | 158 testFile: new ChangeContentOverlay([new SourceEdit(0, 4, 'main')]) |
172 }); | 159 }); |
173 await server.onAnalysisComplete; | 160 await server.onAnalysisComplete; |
174 // errors should have been resent | 161 // errors should have been resent |
175 expect(filesErrors, isNotEmpty); | 162 expect(filesErrors, isNotEmpty); |
176 } | 163 } |
177 | 164 |
178 test_sendNoticesAfterNopChange_flushedUnit() async { | 165 test_sendNoticesAfterNopChange_flushedUnit() async { |
179 createProject(); | 166 createProject(); |
180 addTestFile(''); | 167 addTestFile(''); |
181 await server.onAnalysisComplete; | 168 await server.onAnalysisComplete; |
182 // add an overlay | 169 // add an overlay |
183 server.updateContent('1', { | 170 server.updateContent( |
184 testFile: new AddContentOverlay('main() {} main() {}') | 171 '1', {testFile: new AddContentOverlay('main() {} main() {}')}); |
185 }); | |
186 await server.onAnalysisComplete; | 172 await server.onAnalysisComplete; |
187 // clear errors and make a no-op change | 173 // clear errors and make a no-op change |
188 filesErrors.clear(); | 174 filesErrors.clear(); |
189 server.test_flushResolvedUnit(testFile); | 175 server.test_flushResolvedUnit(testFile); |
190 server.updateContent('2', { | 176 server.updateContent('2', { |
191 testFile: new ChangeContentOverlay([new SourceEdit(0, 4, 'main')]) | 177 testFile: new ChangeContentOverlay([new SourceEdit(0, 4, 'main')]) |
192 }); | 178 }); |
193 await server.onAnalysisComplete; | 179 await server.onAnalysisComplete; |
194 // errors should have been resent | 180 // errors should have been resent |
195 expect(filesErrors, isNotEmpty); | 181 expect(filesErrors, isNotEmpty); |
196 } | 182 } |
197 } | 183 } |
198 | 184 |
199 | |
200 class _ArgumentMatcher_CompilationUnit extends ArgumentMatcher { | 185 class _ArgumentMatcher_CompilationUnit extends ArgumentMatcher { |
201 final String file; | 186 final String file; |
202 | 187 |
203 _ArgumentMatcher_CompilationUnit(this.file); | 188 _ArgumentMatcher_CompilationUnit(this.file); |
204 | 189 |
205 @override | 190 @override |
206 bool matches(arg) { | 191 bool matches(arg) { |
207 return arg is CompilationUnit && arg.element.source.fullName == file; | 192 return arg is CompilationUnit && arg.element.source.fullName == file; |
208 } | 193 } |
209 } | 194 } |
210 | 195 |
211 | |
212 class _MockIndex extends TypedMock implements Index { | 196 class _MockIndex extends TypedMock implements Index { |
213 noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); | 197 noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); |
214 } | 198 } |
OLD | NEW |