@@ -36,7 +36,6 @@ use crate::{
36
36
config:: { Config , RustfmtConfig , WorkspaceSymbolConfig } ,
37
37
diagnostics:: convert_diagnostic,
38
38
global_state:: { FetchWorkspaceRequest , GlobalState , GlobalStateSnapshot } ,
39
- hack_recover_crate_name,
40
39
line_index:: LineEndings ,
41
40
lsp:: {
42
41
ext:: {
@@ -194,74 +193,88 @@ pub(crate) fn handle_view_item_tree(
194
193
Ok ( res)
195
194
}
196
195
197
- // cargo test requires the real package name which might contain hyphens but
198
- // the test identifier passed to this function is the namespace form where hyphens
199
- // are replaced with underscores so we have to reverse this and find the real package name
200
- fn find_package_name ( namespace_root : & str , cargo : & CargoWorkspace ) -> Option < String > {
196
+ // cargo test requires:
197
+ // - the package name - the root of the test identifier supplied to this handler can be
198
+ // a package or a target inside a package.
199
+ // - the target name - if the test identifier is a target, it's needed in addition to the
200
+ // package name to run the right test
201
+ // - real names - the test identifier uses the namespace form where hyphens are replaced with
202
+ // underscores. cargo test requires the real name.
203
+ // - the target kind e.g. bin or lib
204
+ fn find_test_target ( namespace_root : & str , cargo : & CargoWorkspace ) -> Option < TestTarget > {
201
205
cargo. packages ( ) . find_map ( |p| {
202
206
let package_name = & cargo[ p] . name ;
203
- if package_name. replace ( '-' , "_" ) == namespace_root {
204
- Some ( package_name. clone ( ) )
205
- } else {
206
- None
207
+ for target in cargo[ p] . targets . iter ( ) {
208
+ let target_name = & cargo[ * target] . name ;
209
+ if target_name. replace ( '-' , "_" ) == namespace_root {
210
+ return Some ( TestTarget {
211
+ package : package_name. clone ( ) ,
212
+ target : target_name. clone ( ) ,
213
+ kind : cargo[ * target] . kind ,
214
+ } ) ;
215
+ }
207
216
}
217
+ None
208
218
} )
209
219
}
210
220
221
+ fn get_all_targets ( cargo : & CargoWorkspace ) -> Vec < TestTarget > {
222
+ cargo
223
+ . packages ( )
224
+ . flat_map ( |p| {
225
+ let package_name = & cargo[ p] . name ;
226
+ cargo[ p] . targets . iter ( ) . map ( |target| {
227
+ let target_name = & cargo[ * target] . name ;
228
+ TestTarget {
229
+ package : package_name. clone ( ) ,
230
+ target : target_name. clone ( ) ,
231
+ kind : cargo[ * target] . kind ,
232
+ }
233
+ } )
234
+ } )
235
+ . collect ( )
236
+ }
237
+
211
238
pub ( crate ) fn handle_run_test (
212
239
state : & mut GlobalState ,
213
240
params : lsp_ext:: RunTestParams ,
214
241
) -> anyhow:: Result < ( ) > {
215
242
if let Some ( _session) = state. test_run_session . take ( ) {
216
243
state. send_notification :: < lsp_ext:: EndRunTest > ( ( ) ) ;
217
244
}
218
- // We detect the lowest common ancestor of all included tests, and
219
- // run it. We ignore excluded tests for now, the client will handle
220
- // it for us.
221
- let lca = match params. include {
222
- Some ( tests) => tests
223
- . into_iter ( )
224
- . reduce ( |x, y| {
225
- let mut common_prefix = "" . to_owned ( ) ;
226
- for ( xc, yc) in x. chars ( ) . zip ( y. chars ( ) ) {
227
- if xc != yc {
228
- break ;
229
- }
230
- common_prefix. push ( xc) ;
231
- }
232
- common_prefix
233
- } )
234
- . unwrap_or_default ( ) ,
235
- None => "" . to_owned ( ) ,
236
- } ;
237
- let ( namespace_root, test_path) = if lca. is_empty ( ) {
238
- ( None , None )
239
- } else if let Some ( ( namespace_root, path) ) = lca. split_once ( "::" ) {
240
- ( Some ( namespace_root) , Some ( path) )
241
- } else {
242
- ( Some ( lca. as_str ( ) ) , None )
243
- } ;
245
+
244
246
let mut handles = vec ! [ ] ;
245
247
for ws in & * state. workspaces {
246
248
if let ProjectWorkspaceKind :: Cargo { cargo, .. } = & ws. kind {
247
- let test_target = if let Some ( namespace_root) = namespace_root {
248
- if let Some ( package_name) = find_package_name ( namespace_root, cargo) {
249
- TestTarget :: Package ( package_name)
250
- } else {
251
- TestTarget :: Workspace
252
- }
253
- } else {
254
- TestTarget :: Workspace
249
+ let tests = match params. include {
250
+ Some ( ref include) => include
251
+ . iter ( )
252
+ . filter_map ( |test| {
253
+ let ( root, remainder) = match test. split_once ( "::" ) {
254
+ Some ( ( root, remainder) ) => ( root. to_owned ( ) , Some ( remainder) ) ,
255
+ None => ( test. clone ( ) , None ) ,
256
+ } ;
257
+ if let Some ( target) = find_test_target ( & root, cargo) {
258
+ Some ( ( target, remainder) )
259
+ } else {
260
+ tracing:: error!( "Test target not found for: {test}" ) ;
261
+ None
262
+ }
263
+ } )
264
+ . collect_vec ( ) ,
265
+ None => get_all_targets ( cargo) . into_iter ( ) . map ( |target| ( target, None ) ) . collect ( ) ,
255
266
} ;
256
267
257
- let handle = CargoTestHandle :: new (
258
- test_path,
259
- state. config . cargo_test_options ( None ) ,
260
- cargo. workspace_root ( ) ,
261
- test_target,
262
- state. test_run_sender . clone ( ) ,
263
- ) ?;
264
- handles. push ( handle) ;
268
+ for ( target, path) in tests {
269
+ let handle = CargoTestHandle :: new (
270
+ path,
271
+ state. config . cargo_test_options ( None ) ,
272
+ cargo. workspace_root ( ) ,
273
+ target,
274
+ state. test_run_sender . clone ( ) ,
275
+ ) ?;
276
+ handles. push ( handle) ;
277
+ }
265
278
}
266
279
}
267
280
// Each process send finished signal twice, once for stdout and once for stderr
@@ -285,9 +298,7 @@ pub(crate) fn handle_discover_test(
285
298
}
286
299
None => ( snap. analysis . discover_test_roots ( ) ?, None ) ,
287
300
} ;
288
- for t in & tests {
289
- hack_recover_crate_name:: insert_name ( t. id . clone ( ) ) ;
290
- }
301
+
291
302
Ok ( lsp_ext:: DiscoverTestResults {
292
303
tests : tests
293
304
. into_iter ( )
0 commit comments