-
Notifications
You must be signed in to change notification settings - Fork 97
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Update test callable discovery to work within the language service #2095
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not done looking at all the files, posting all my comments for today.
wasm/src/language_service.rs
Outdated
|
||
#[wasm_bindgen] | ||
extern "C" { | ||
#[wasm_bindgen(typescript_type = "(callables: [string, ILocation][]) => void")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use the ITestCallable
type you already defined in the other wasm file, instead of the [string, ILocation]
tuple. Tuples are clunky in TS.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Finished looking. I really like the overall approach. Also I assume integration tests are coming up in a different PR.
decl.name.span, | ||
)); | ||
} | ||
if decl.input.ty != qsc_hir::ty::Ty::UNIT { | ||
self.errors | ||
.push(TestAttributeError::CallableHasParameters(decl.name.span)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of just the name, could you use the whole signature's span, so that the squiggles appear below the problematic code and not the name?
And now that I wrote that... I realize there may not be an easy way to get that span from the AST. I think I hit this issue before. Hmm...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As it was brought up in the team chat, I think it'd be useful to have a test case where a @Config()
attribute causes a test callable to be excluded from the compilation. get_test_callables
shouldn't return that callable.
// TODO(sezna) verify encoding | ||
crate::qsc_utils::into_location( | ||
qsc::line_column::Encoding::Utf16, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For consistency, this should be the same position_encoding
that the LanguageService
was initialized with. You can add it as a parameter to CompilationStateUpdater::new
and just keep it in this struct.
// Trigger a document update for the second test file. | ||
updater | ||
.update_document( | ||
"parent/src/test2.qs", | ||
1, | ||
"@Test() function Test2() : Unit {}", | ||
) | ||
.await; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It'd be interesting to test if the tests in test2.qs
file get discovered WITHOUT triggering an update for test2.qs
.
In theory, just poking test1.qs
should cause the whole project to be loaded, which in turn will raise a notification about all the tests.
If you remove this line, I think you'll see in the below expect!
that all the tests are still discovered. (Currently, the expect!
output contains TWO notifications with the same contents, because the compilation is updated twice. After you remove the second update_document
, the expect!
output should contain ONE notification, which will still show all the tests).
I suggest just removing the second update. It'll make the test case both shorter and more "interesting".
@@ -37,15 +37,25 @@ import { registerQSharpNotebookCellUpdateHandlers } from "./notebook.js"; | |||
import { createReferenceProvider } from "./references.js"; | |||
import { createRenameProvider } from "./rename.js"; | |||
import { createSignatureHelpProvider } from "./signature.js"; | |||
|
|||
export async function activateLanguageService(extensionUri: vscode.Uri) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW I had intentionally made this function take extensionUri
instead of the whole context
, since we never actually use anything but the extensionUri
. I prefer the previous version.
const compilerWorkerScriptPath = vscode.Uri.joinPath( | ||
context.extensionUri, | ||
"./out/compilerWorker.js", | ||
).toString(); | ||
worker = getCompilerWorker(compilerWorkerScriptPath); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd love it if you pulled this into a common utility in vscode
, since these two lines, including the hardcoded path, are repeated in 4-5 places now.
@Test() | ||
operation MeasureSignedIntTests() : Unit { | ||
let testCases = [ | ||
("0b0001 == 1", 4, (qs) => X(qs[0]), (qs) => MeasureSignedInteger(qs, 6), 11), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't actually pass for me .
false, | ||
); | ||
|
||
const testMetadata = new WeakMap<vscode.TestItem, number>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting, I don't think I've ever seen a WeakMap
used in practice! Is the idea that the test controller owns these items, and not this map?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's from the docs. Search on weakmap
on https://code.visualstudio.com/api/extension-guides/testing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This idea came from this page: https://code.visualstudio.com/api/extension-guides/testing (search for weakmap) -- to be honest, I'm not 100% sure why they recommend a WeakMap
versus a regular key/value store here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to be honest, I'm not 100% sure why they recommend a WeakMap versus a regular key/value store here
Because a regular key/value store would hold a strong reference to the TestItem that is the key you put in it, so it would never get garbage collected (unless you manually scrubbed the map and removed items which no longer exist in the controller, which you don't in this code currently. You delete no longer needed items in line 182, but you don't remove them from the map, so they will remain held in memory by that reference if not using a WeakMap).
From the MDN docs: an object's presence as a key in a WeakMap does not prevent the object from being garbage collected. Once an object used as a key has been collected, its corresponding values in any WeakMap become candidates for garbage collection as well
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In addition to the tests here I'd love it if you added a samples under samples/
showing how to use this new attribute.
#[derive(Debug)] | ||
pub struct TestCallables { | ||
pub callables: Vec<(String, Location)>, | ||
pub version: Option<u32>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused field (and rightly so!)
@sezna hitting this odd bug where you can't navigate to the test case after it fails. |
This PR rewrites #2059 to trigger test discovery from within the language server, as opposed to externally in an entirely separate context. Please read the description in that PR for more detail.
It also adds tests for LS state in both the Rust-based LS tests and the JS-based
basics.js
npm API tests.