microsoft/qdk

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
fedimser/det-qre

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

415lines · modecode

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