microsoft/qdk

Public

mirrored from https://github.com/microsoft/qdkAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v1.25.1

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

source/language_service/src/code_action/wrapper_refactor/tests.rs

439lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4use crate::test_utils::compile_notebook_with_markers;
5use crate::{code_action, test_utils::compile_project_with_markers_no_cursor};
6use expect_test::expect;
7use qsc::line_column::{Encoding, Position, Range};
8
9fn get_wrapper_text(source: &str, op_name: &str) -> String {
10 let (compilation, _targets) =
11 compile_project_with_markers_no_cursor(&[("<source>", source)], false);
12 let newline_count = u32::try_from(source.matches('\n').count()).expect("count fits");
13 let end = if newline_count == 0 {
14 Position {
15 line: 0,
16 column: u32::try_from(source.len()).expect("len fits"),
17 }
18 } else {
19 Position {
20 line: newline_count,
21 column: 0,
22 }
23 };
24 let range = Range {
25 start: Position { line: 0, column: 0 },
26 end,
27 };
28 let actions = code_action::get_code_actions(&compilation, "<source>", range, Encoding::Utf8);
29 let action = actions
30 .iter()
31 .find(|a| a.title == format!("Generate wrapper with default arguments for {op_name}"))
32 .unwrap_or_else(|| {
33 panic!(
34 "Expected wrapper action for {op_name}. Available: {:?}",
35 actions.iter().map(|a| &a.title).collect::<Vec<_>>()
36 )
37 });
38 // --- Range validation ---
39 let edit = action.edit.as_ref().expect("expected edit");
40 assert_eq!(edit.changes.len(), 1, "Expected a single file change");
41 let (file, edits) = &edit.changes[0];
42 assert_eq!(file, "<source>", "Unexpected file in edit change");
43 assert_eq!(edits.len(), 1, "Expected exactly one text edit");
44 let text_edit = &edits[0];
45 let edit_range = text_edit.range;
46 // The wrapper insertion should be a zero-length insertion immediately before the operation declaration.
47 assert_eq!(
48 edit_range.start, edit_range.end,
49 "Wrapper edit should be an insertion (zero-length range)"
50 );
51 if let Some(op_start_byte) = source.find(&format!("operation {op_name}")) {
52 // Compute expected (line, column) for op_start_byte.
53 let mut line: usize = 0;
54 let mut col: usize = 0;
55 let mut counted: usize = 0;
56 for part in source.split_inclusive('\n') {
57 let part_len = part.len();
58 if counted + part_len > op_start_byte {
59 // op starts in this line
60 let line_start_index = op_start_byte - counted;
61 col = part[..line_start_index].chars().count();
62 break;
63 }
64 counted += part_len;
65 line += 1;
66 }
67 assert_eq!(
68 edit_range.start.line as usize, line,
69 "Edit start line mismatch (expected {line}, got {})",
70 edit_range.start.line
71 );
72 assert_eq!(
73 edit_range.start.column as usize, col,
74 "Edit start column mismatch (expected {col}, got {})",
75 edit_range.start.column
76 );
77 } else {
78 panic!("Could not locate operation {op_name} in source to validate range");
79 }
80 text_edit.new_text.clone()
81}
82
83#[test]
84fn basic_wrapper() {
85 // Wrap in a namespace since most language features (including item collection) assume a namespace context.
86 let wrapper_text = get_wrapper_text(
87 "namespace Test { operation Op(a : Int, b : Bool) : Unit { } }",
88 "Op",
89 );
90 expect![[r#"
91 operation OpWithDefaults() : Unit {
92 // TODO: Fill out the values for the parameters
93 let a = 0;
94 let b = false;
95
96 // Call original operation
97 Op(a, b);
98 }
99
100 "#]]
101 .assert_eq(&wrapper_text);
102}
103
104#[test]
105fn indentation_nested() {
106 let source =
107 "namespace Test {\n // Some preceding code\n operation Ind(a : Int) : Unit { }\n}";
108 let wrapper_text = get_wrapper_text(source, "Ind");
109 assert!(wrapper_text.starts_with("operation IndWithDefaults()"));
110 assert!(wrapper_text.contains(" // Call original operation"));
111}
112
113#[test]
114fn indentation_tabs() {
115 let source = "namespace Test {\n\toperation Tabbed(a : Int) : Unit { }\n}";
116 let wrapper_text = get_wrapper_text(source, "Tabbed");
117 assert!(wrapper_text.starts_with("operation TabbedWithDefaults()"));
118 assert!(wrapper_text.contains("\t\t// Call original operation"));
119}
120
121#[test]
122fn default_qubit() {
123 let wrapper = get_wrapper_text("namespace Test { operation Q(q : Qubit) : Unit { } }", "Q");
124 expect![[r#"
125 operation QWithDefaults() : Unit {
126 // TODO: Fill out the values for the parameters
127 use q = Qubit();
128
129 // Call original operation
130 Q(q);
131 }
132
133 "#]]
134 .assert_eq(&wrapper);
135}
136
137#[test]
138fn default_qubit_array() {
139 let wrapper = get_wrapper_text(
140 "namespace Test { operation QA(qs : Qubit[]) : Unit { } }",
141 "QA",
142 );
143 expect![[r#"
144 operation QAWithDefaults() : Unit {
145 // TODO: Fill out the values for the parameters
146 use qs = Qubit[1];
147
148 // Call original operation
149 QA(qs);
150 }
151
152 "#]]
153 .assert_eq(&wrapper);
154}
155
156#[test]
157fn default_primitives() {
158 let wrapper = get_wrapper_text(
159 "namespace Test { operation Prims(a : Int, b : Bool, c : Double, d : Result, e : Pauli, f : BigInt, g : String) : Unit { } }",
160 "Prims",
161 );
162 expect![[r#"
163 operation PrimsWithDefaults() : Unit {
164 // TODO: Fill out the values for the parameters
165 let a = 0;
166 let b = false;
167 let c = 0.0;
168 let d = Zero;
169 let e = PauliI;
170 let f = 0L;
171 let g = "";
172
173 // Call original operation
174 Prims(a, b, c, d, e, f, g);
175 }
176
177 "#]]
178 .assert_eq(&wrapper);
179}
180
181#[test]
182fn default_udt() {
183 let wrapper = get_wrapper_text(
184 "namespace Test { newtype MyT = Int; operation UsesUdt(x : MyT) : Unit { } }",
185 "UsesUdt",
186 );
187 expect![[r#"
188 operation UsesUdtWithDefaults() : Unit {
189 // TODO: Fill out the values for the parameters
190 // TODO: provide value for x (UDT MyT)
191
192 // Call original operation
193 UsesUdt(_);
194 }
195
196 "#]]
197 .assert_eq(&wrapper);
198}
199
200#[test]
201fn default_generic() {
202 let wrapper = get_wrapper_text(
203 "namespace Test { operation Generic<'T>(x : 'T) : Unit { } }",
204 "Generic",
205 );
206 expect![[r#"
207 operation GenericWithDefaults() : Unit {
208 // TODO: Fill out the values for the parameters
209 // TODO: provide value for x (Generic parameter 'T)
210
211 // Call original operation
212 Generic(_);
213 }
214
215 "#]]
216 .assert_eq(&wrapper);
217}
218
219#[test]
220fn default_array_int() {
221 let wrapper = get_wrapper_text(
222 "namespace Test { operation Arr(arr : Int[]) : Unit { } }",
223 "Arr",
224 );
225 expect![[r#"
226 operation ArrWithDefaults() : Unit {
227 // TODO: Fill out the values for the parameters
228 let arr = [];
229
230 // Call original operation
231 Arr(arr);
232 }
233
234 "#]]
235 .assert_eq(&wrapper);
236}
237
238#[test]
239fn default_tuple_destructured() {
240 let wrapper = get_wrapper_text(
241 "namespace Test { operation Tup(param : (Int, Bool, (Double, Qubit))) : Unit { } }",
242 "Tup",
243 );
244 expect![[r#"
245 operation TupWithDefaults() : Unit {
246 // TODO: Fill out the values for the parameters
247 use param_q0 = Qubit();
248 let param = (0, false, (0.0, param_q0));
249
250 // Call original operation
251 Tup(param);
252 }
253
254 "#]]
255 .assert_eq(&wrapper);
256}
257
258#[test]
259fn default_tuple_bound() {
260 let wrapper = get_wrapper_text(
261 "namespace Test { operation Tup2(t : (Qubit, Int, (Bool, Qubit[]))) : Unit { } }",
262 "Tup2",
263 );
264 expect![[r#"
265 operation Tup2WithDefaults() : Unit {
266 // TODO: Fill out the values for the parameters
267 use t_q0 = Qubit();
268 use t_qs0 = Qubit[1];
269 let t = (t_q0, 0, (false, t_qs0));
270
271 // Call original operation
272 Tup2(t);
273 }
274
275 "#]]
276 .assert_eq(&wrapper);
277}
278
279#[test]
280fn qubit_tuple_counter_persistence() {
281 let wrapper = get_wrapper_text(
282 "namespace Test { operation Deep(t : (Qubit, (Qubit, Qubit), Qubit, (Qubit, Qubit), (Qubit, (Qubit, Qubit)))) : Unit { } }",
283 "Deep",
284 );
285 expect![[r#"
286 operation DeepWithDefaults() : Unit {
287 // TODO: Fill out the values for the parameters
288 use t_q0 = Qubit();
289 use t_q1 = Qubit();
290 use t_q2 = Qubit();
291 use t_q3 = Qubit();
292 use t_q4 = Qubit();
293 use t_q5 = Qubit();
294 use t_q6 = Qubit();
295 use t_q7 = Qubit();
296 use t_q8 = Qubit();
297 let t = (t_q0, (t_q1, t_q2), t_q3, (t_q4, t_q5), (t_q6, (t_q7, t_q8)));
298
299 // Call original operation
300 Deep(t);
301 }
302
303 "#]]
304 .assert_eq(&wrapper);
305}
306
307#[test]
308fn qubit_and_array_counters() {
309 let wrapper = get_wrapper_text(
310 "namespace Test { operation Mix(t : (Qubit, Qubit[], (Qubit, Qubit[], Qubit[]), Qubit, Qubit[])) : Unit { } }",
311 "Mix",
312 );
313 expect![[r#"
314 operation MixWithDefaults() : Unit {
315 // TODO: Fill out the values for the parameters
316 use t_q0 = Qubit();
317 use t_qs0 = Qubit[1];
318 use t_q1 = Qubit();
319 use t_qs1 = Qubit[1];
320 use t_qs2 = Qubit[1];
321 use t_q2 = Qubit();
322 use t_qs3 = Qubit[1];
323 let t = (t_q0, t_qs0, (t_q1, t_qs1, t_qs2), t_q2, t_qs3);
324
325 // Call original operation
326 Mix(t);
327 }
328
329 "#]]
330 .assert_eq(&wrapper);
331}
332
333#[test]
334fn tuple_todo_positioning() {
335 let wrapper = get_wrapper_text(
336 "namespace Test { newtype MyT = Int; operation Mixed<'T>(t : (Qubit, MyT, 'T)) : Unit { } }",
337 "Mixed",
338 );
339 expect![[r#"
340 operation MixedWithDefaults() : Unit {
341 // TODO: Fill out the values for the parameters
342 use t_q0 = Qubit();
343 // TODO: provide value for tuple component of t (UDT MyT)
344 // TODO: provide value for tuple component of t (Generic parameter 'T)
345 let t = (t_q0, _, _);
346
347 // Call original operation
348 Mixed(t);
349 }
350
351 "#]]
352 .assert_eq(&wrapper);
353}
354
355#[test]
356fn default_single_element_tuple() {
357 let wrapper = get_wrapper_text(
358 "namespace Test { operation Single(t : (Double,)) : Unit { } }",
359 "Single",
360 );
361 expect![[r#"
362 operation SingleWithDefaults() : Unit {
363 // TODO: Fill out the values for the parameters
364 let t = (0.0,);
365
366 // Call original operation
367 Single(t);
368 }
369
370 "#]]
371 .assert_eq(&wrapper);
372}
373
374#[test]
375fn no_code_action_for_lambdas_() {
376 let source = "namespace Test { operation Named(x : Int) : Unit { let l = (y) => { x + y }; let e = (y) => { x + y }; l(2); } }";
377 let (compilation, _targets) =
378 compile_project_with_markers_no_cursor(&[("<source>", source)], false);
379 let range = Range {
380 start: Position { line: 0, column: 0 },
381 end: Position {
382 line: 0,
383 column: u32::try_from(source.len()).expect("len fits"),
384 },
385 };
386 let actions = code_action::get_code_actions(&compilation, "<source>", range, Encoding::Utf8);
387 let titles = actions
388 .iter()
389 .filter_map(|a| {
390 if a.title.contains("Generate wrapper") {
391 Some(a.title.clone())
392 } else {
393 None
394 }
395 })
396 .collect::<Vec<_>>()
397 .join("\n");
398 expect!["Generate wrapper with default arguments for Named"].assert_eq(&titles);
399}
400
401#[test]
402fn preserves_crlf_newlines() {
403 // Source with Windows CRLF newlines. We embed them explicitly.
404 let source = "namespace Test {\r\n operation Op(a : Int) : Unit { }\r\n}";
405 let wrapper = get_wrapper_text(source, "Op");
406 // Ensure the wrapper uses CRLF consistently (no lone \n occurrences)
407 assert!(
408 wrapper.matches("\r\n").count() > 2,
409 "Expected multiple CRLF sequences in wrapper text"
410 );
411 assert!(
412 !wrapper.contains('\n') || wrapper.contains("\r\n"),
413 "Found bare LF without CR"
414 );
415}
416
417#[test]
418fn notebook_cell_wrapper_action() {
419 let source = "operation Op(a : Int, b : Bool) : Unit {↘ }";
420 let cells = [("cell1", source)];
421 let (compilation, cell_uri, _, _) = compile_notebook_with_markers(&cells);
422 let range = Range {
423 start: Position { line: 0, column: 0 },
424 end: Position {
425 line: 0,
426 column: u32::try_from(source.len()).expect("len fits"),
427 },
428 };
429 let actions = code_action::get_code_actions(&compilation, &cell_uri, range, Encoding::Utf8);
430 let wrapper = actions
431 .iter()
432 .find(|a| a.title == "Generate wrapper with default arguments for Op")
433 .unwrap_or_else(|| panic!("Expected wrapper action in notebook cell. Got: {actions:?}"));
434 let edit = wrapper.edit.as_ref().expect("expected edit");
435 assert_eq!(edit.changes.len(), 1, "Expected single file change");
436 let (edit_file, edits) = &edit.changes[0];
437 assert_eq!(edit_file, &cell_uri, "Edit applied to wrong notebook cell");
438 assert_eq!(edits.len(), 1, "Expected single text edit");
439}
440