• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1namespace ts.projectSystem {
2    describe("unittests:: tsserver:: cancellationToken", () => {
3        // Disable sourcemap support for the duration of the test, as sourcemapping the errors generated during this test is slow and not something we care to test
4        let oldPrepare: AnyFunction;
5        before(() => {
6            oldPrepare = (Error as any).prepareStackTrace;
7            delete (Error as any).prepareStackTrace;
8        });
9
10        after(() => {
11            (Error as any).prepareStackTrace = oldPrepare;
12        });
13
14        it("is attached to request", () => {
15            const f1 = {
16                path: "/a/b/app.ts",
17                content: "let xyz = 1;"
18            };
19            const host = createServerHost([f1]);
20            let expectedRequestId: number;
21            const cancellationToken: server.ServerCancellationToken = {
22                isCancellationRequested: () => false,
23                setRequest: requestId => {
24                    if (expectedRequestId === undefined) {
25                        assert.isTrue(false, "unexpected call");
26                    }
27                    assert.equal(requestId, expectedRequestId);
28                },
29                resetRequest: noop
30            };
31
32            const session = createSession(host, { cancellationToken });
33
34            expectedRequestId = session.getNextSeq();
35            session.executeCommandSeq(<server.protocol.OpenRequest>{
36                command: "open",
37                arguments: { file: f1.path }
38            });
39
40            expectedRequestId = session.getNextSeq();
41            session.executeCommandSeq(<server.protocol.GeterrRequest>{
42                command: "geterr",
43                arguments: { files: [f1.path] }
44            });
45
46            expectedRequestId = session.getNextSeq();
47            session.executeCommandSeq(<server.protocol.OccurrencesRequest>{
48                command: "occurrences",
49                arguments: { file: f1.path, line: 1, offset: 6 }
50            });
51
52            expectedRequestId = 2;
53            host.runQueuedImmediateCallbacks();
54            expectedRequestId = 2;
55            host.runQueuedImmediateCallbacks();
56        });
57
58        it("Geterr is cancellable", () => {
59            const f1 = {
60                path: "/a/app.ts",
61                content: "let x = 1"
62            };
63            const config = {
64                path: "/a/tsconfig.json",
65                content: JSON.stringify({
66                    compilerOptions: {}
67                })
68            };
69
70            const cancellationToken = new TestServerCancellationToken();
71            const host = createServerHost([f1, config]);
72            const session = createSession(host, {
73                canUseEvents: true,
74                eventHandler: noop,
75                cancellationToken
76            });
77            {
78                session.executeCommandSeq(<protocol.OpenRequest>{
79                    command: "open",
80                    arguments: { file: f1.path }
81                });
82                // send geterr for missing file
83                session.executeCommandSeq(<protocol.GeterrRequest>{
84                    command: "geterr",
85                    arguments: { files: ["/a/missing"] }
86                });
87                // Queued files
88                assert.equal(host.getOutput().length, 0, "expected 0 message");
89                host.checkTimeoutQueueLengthAndRun(1);
90                // Completed event since file is missing
91                assert.equal(host.getOutput().length, 1, "expect 1 message");
92                verifyRequestCompleted(session.getSeq(), 0);
93            }
94            {
95                const getErrId = session.getNextSeq();
96                // send geterr for a valid file
97                session.executeCommandSeq(<protocol.GeterrRequest>{
98                    command: "geterr",
99                    arguments: { files: [f1.path] }
100                });
101
102                assert.equal(host.getOutput().length, 0, "expect 0 messages");
103
104                // run new request
105                session.executeCommandSeq(<protocol.ProjectInfoRequest>{
106                    command: "projectInfo",
107                    arguments: { file: f1.path }
108                });
109                session.clearMessages();
110
111                // cancel previously issued Geterr
112                cancellationToken.setRequestToCancel(getErrId);
113                host.runQueuedTimeoutCallbacks();
114
115                assert.equal(host.getOutput().length, 1, "expect 1 message");
116                verifyRequestCompleted(getErrId, 0);
117
118                cancellationToken.resetToken();
119            }
120            {
121                const getErrId = session.getNextSeq();
122                session.executeCommandSeq(<protocol.GeterrRequest>{
123                    command: "geterr",
124                    arguments: { files: [f1.path] }
125                });
126                assert.equal(host.getOutput().length, 0, "expect 0 messages");
127
128                // run first step
129                host.runQueuedTimeoutCallbacks();
130                assert.equal(host.getOutput().length, 1, "expect 1 message");
131                const e1 = <protocol.Event>getMessage(0);
132                assert.equal(e1.event, "syntaxDiag");
133                session.clearMessages();
134
135                cancellationToken.setRequestToCancel(getErrId);
136                host.runQueuedImmediateCallbacks();
137                assert.equal(host.getOutput().length, 1, "expect 1 message");
138                verifyRequestCompleted(getErrId, 0);
139
140                cancellationToken.resetToken();
141            }
142            {
143                const getErrId = session.getNextSeq();
144                session.executeCommandSeq(<protocol.GeterrRequest>{
145                    command: "geterr",
146                    arguments: { files: [f1.path] }
147                });
148                assert.equal(host.getOutput().length, 0, "expect 0 messages");
149
150                // run first step
151                host.runQueuedTimeoutCallbacks();
152                assert.equal(host.getOutput().length, 1, "expect 1 message");
153                const e1 = <protocol.Event>getMessage(0);
154                assert.equal(e1.event, "syntaxDiag");
155                session.clearMessages();
156
157                // the semanticDiag message
158                host.runQueuedImmediateCallbacks();
159                assert.equal(host.getOutput().length, 1);
160                const e2 = <protocol.Event>getMessage(0);
161                assert.equal(e2.event, "semanticDiag");
162                session.clearMessages();
163
164                host.runQueuedImmediateCallbacks(1);
165                assert.equal(host.getOutput().length, 2);
166                const e3 = <protocol.Event>getMessage(0);
167                assert.equal(e3.event, "suggestionDiag");
168                verifyRequestCompleted(getErrId, 1);
169
170                cancellationToken.resetToken();
171            }
172            {
173                const getErr1 = session.getNextSeq();
174                session.executeCommandSeq(<protocol.GeterrRequest>{
175                    command: "geterr",
176                    arguments: { files: [f1.path] }
177                });
178                assert.equal(host.getOutput().length, 0, "expect 0 messages");
179                // run first step
180                host.runQueuedTimeoutCallbacks();
181                assert.equal(host.getOutput().length, 1, "expect 1 message");
182                const e1 = <protocol.Event>getMessage(0);
183                assert.equal(e1.event, "syntaxDiag");
184                session.clearMessages();
185
186                session.executeCommandSeq(<protocol.GeterrRequest>{
187                    command: "geterr",
188                    arguments: { files: [f1.path] }
189                });
190                // make sure that getErr1 is completed
191                verifyRequestCompleted(getErr1, 0);
192            }
193
194            function verifyRequestCompleted(expectedSeq: number, n: number) {
195                const event = <protocol.RequestCompletedEvent>getMessage(n);
196                assert.equal(event.event, "requestCompleted");
197                assert.equal(event.body.request_seq, expectedSeq, "expectedSeq");
198                session.clearMessages();
199            }
200
201            function getMessage(n: number) {
202                return JSON.parse(server.extractMessage(host.getOutput()[n]));
203            }
204        });
205
206        it("Lower priority tasks are cancellable", () => {
207            const f1 = {
208                path: "/a/app.ts",
209                content: `{ let x = 1; } var foo = "foo"; var bar = "bar"; var fooBar = "fooBar";`
210            };
211            const config = {
212                path: "/a/tsconfig.json",
213                content: JSON.stringify({
214                    compilerOptions: {}
215                })
216            };
217            const cancellationToken = new TestServerCancellationToken(/*cancelAfterRequest*/ 3);
218            const host = createServerHost([f1, config]);
219            const session = createSession(host, {
220                canUseEvents: true,
221                eventHandler: noop,
222                cancellationToken,
223                throttleWaitMilliseconds: 0
224            });
225            {
226                session.executeCommandSeq(<protocol.OpenRequest>{
227                    command: "open",
228                    arguments: { file: f1.path }
229                });
230
231                // send navbar request (normal priority)
232                session.executeCommandSeq(<protocol.NavBarRequest>{
233                    command: "navbar",
234                    arguments: { file: f1.path }
235                });
236
237                // ensure the nav bar request can be canceled
238                verifyExecuteCommandSeqIsCancellable(<protocol.NavBarRequest>{
239                    command: "navbar",
240                    arguments: { file: f1.path }
241                });
242
243                // send outlining spans request (normal priority)
244                session.executeCommandSeq(<protocol.OutliningSpansRequestFull>{
245                    command: "outliningSpans",
246                    arguments: { file: f1.path }
247                });
248
249                // ensure the outlining spans request can be canceled
250                verifyExecuteCommandSeqIsCancellable(<protocol.OutliningSpansRequestFull>{
251                    command: "outliningSpans",
252                    arguments: { file: f1.path }
253                });
254            }
255
256            function verifyExecuteCommandSeqIsCancellable<T extends server.protocol.Request>(request: Partial<T>) {
257                // Set the next request to be cancellable
258                // The cancellation token will cancel the request the third time
259                // isCancellationRequested() is called.
260                cancellationToken.setRequestToCancel(session.getNextSeq());
261                let operationCanceledExceptionThrown = false;
262
263                try {
264                    session.executeCommandSeq(request);
265                }
266                catch (e) {
267                    assert(e instanceof OperationCanceledException);
268                    operationCanceledExceptionThrown = true;
269                }
270                assert(operationCanceledExceptionThrown, "Operation Canceled Exception not thrown for request: " + JSON.stringify(request));
271            }
272        });
273    });
274}
275