mirror of
https://github.com/l0ng-ai/openidea.git
synced 2026-11-08 17:02:17 +00:00
Run configuration detection only looked for pom.xml / build.gradle(.kts) at the opened root, so a Maven or Gradle build in a subfolder produced no configurations, and Java files fell back to a single-file `java X.java` launch without classpath or build. Detection now takes the root build, or else the shallowest build on each branch up to three levels down, with the subfolder in the config's cwd, id and name; a multi-module parent pom counts as one build. Spring Boot applications (@SpringBootApplication with main in a module that applies the Spring Boot plugin) get spring-boot configurations that build the module and run with its classpath. Gutter and file runs pick the build the file belongs to (also for Go modules), and only files outside any build run as single files; the main config lookup takes the nearest bootRun / run / spring-boot:run. On Windows, piped JVM (java, Maven, Gradle) and Python processes now write in the console encoding instead of the ANSI code page, so their output is no longer garbled. run_configs runs on a blocking thread. Fixes #149 Claude-Session: https://claude.ai/code/session_01Dq6jQQn8ZhRfkkyj3PRutA
519 lines
21 KiB
Rust
519 lines
21 KiB
Rust
use ide_run::{RunConfig, detect};
|
|
use std::fs;
|
|
use std::path::Path;
|
|
use tempfile::TempDir;
|
|
|
|
fn project(files: &[(&str, &str)]) -> TempDir {
|
|
let dir = tempfile::tempdir().unwrap();
|
|
for (path, content) in files {
|
|
let p = dir.path().join(path);
|
|
fs::create_dir_all(p.parent().unwrap()).unwrap();
|
|
fs::write(p, content).unwrap();
|
|
}
|
|
dir
|
|
}
|
|
|
|
fn find<'a>(configs: &'a [RunConfig], id: &str) -> &'a RunConfig {
|
|
configs
|
|
.iter()
|
|
.find(|c| c.id == id)
|
|
.unwrap_or_else(|| panic!("missing {id}; have {:?}", configs.iter().map(|c| &c.id).collect::<Vec<_>>()))
|
|
}
|
|
|
|
fn ids(configs: &[RunConfig]) -> Vec<&str> {
|
|
configs.iter().map(|c| c.id.as_str()).collect()
|
|
}
|
|
|
|
fn cmdline(c: &RunConfig) -> String {
|
|
std::iter::once(c.command.as_str()).chain(c.args.iter().map(String::as_str)).collect::<Vec<_>>().join(" ")
|
|
}
|
|
|
|
#[test]
|
|
fn empty_project_detects_nothing() {
|
|
let dir = tempfile::tempdir().unwrap();
|
|
assert!(detect(dir.path()).is_empty());
|
|
assert!(detect(Path::new("/definitely/not/here")).is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn npm_scripts_with_lockfile_package_manager() {
|
|
let dir = project(&[
|
|
("package.json", r#"{"scripts":{"dev":"vite","test":"vitest","build":"vite build","test:e2e":"playwright"}}"#),
|
|
("pnpm-lock.yaml", ""),
|
|
]);
|
|
let c = detect(dir.path());
|
|
assert_eq!(ids(&c), ["npm:build", "npm:dev", "npm:test", "npm:test:e2e"]);
|
|
let dev = find(&c, "npm:dev");
|
|
assert_eq!(cmdline(dev), "pnpm run dev");
|
|
assert!(dev.detected && !dev.is_test);
|
|
assert_eq!(dev.kind, "npm");
|
|
assert!(find(&c, "npm:test").is_test);
|
|
assert!(find(&c, "npm:test:e2e").is_test);
|
|
}
|
|
|
|
#[test]
|
|
fn package_manager_selection() {
|
|
for (lock, pm) in [("yarn.lock", "yarn"), ("bun.lockb", "bun"), ("bun.lock", "bun"), ("package-lock.json", "npm")] {
|
|
let dir = project(&[("package.json", r#"{"scripts":{"start":"node ."}}"#), (lock, "")]);
|
|
assert_eq!(find(&detect(dir.path()), "npm:start").command, pm, "{lock}");
|
|
}
|
|
let dir = project(&[("package.json", r#"{"packageManager":"[email protected]","scripts":{"start":"x"}}"#)]);
|
|
assert_eq!(find(&detect(dir.path()), "npm:start").command, "yarn");
|
|
}
|
|
|
|
#[test]
|
|
fn npm_workspace_packages() {
|
|
let dir = project(&[
|
|
("package.json", r#"{"workspaces":["packages/*"],"scripts":{"lint":"eslint"}}"#),
|
|
("packages/web/package.json", r#"{"name":"@acme/web","scripts":{"dev":"vite"}}"#),
|
|
("packages/api/package.json", r#"{"scripts":{"test":"jest"}}"#),
|
|
("packages/notapkg/readme.md", ""),
|
|
]);
|
|
let c = detect(dir.path());
|
|
let web = find(&c, "npm:@acme/web:dev");
|
|
assert_eq!(web.cwd.as_deref(), Some(Path::new("packages/web")));
|
|
assert_eq!(cmdline(web), "npm run dev");
|
|
let api = find(&c, "npm:packages/api:test");
|
|
assert!(api.is_test);
|
|
|
|
let dir = project(&[
|
|
("package.json", "{}"),
|
|
("pnpm-lock.yaml", ""),
|
|
("pnpm-workspace.yaml", "packages:\n - 'apps/*'\n - \"libs/core\"\n"),
|
|
("apps/site/package.json", r#"{"name":"site","scripts":{"build":"astro build"}}"#),
|
|
("libs/core/package.json", r#"{"name":"core","scripts":{"test":"vitest"}}"#),
|
|
]);
|
|
let c = detect(dir.path());
|
|
assert_eq!(cmdline(find(&c, "npm:site:build")), "pnpm run build");
|
|
assert!(find(&c, "npm:core:test").is_test);
|
|
}
|
|
|
|
#[test]
|
|
fn cargo_single_package() {
|
|
let dir =
|
|
project(&[("Cargo.toml", "[package]\nname = \"app\"\nversion = \"0.1.0\"\n"), ("src/main.rs", "fn main() {}")]);
|
|
let c = detect(dir.path());
|
|
assert_eq!(ids(&c), ["cargo:run:app", "cargo:test", "cargo:build"]);
|
|
let run = find(&c, "cargo:run:app");
|
|
assert_eq!(cmdline(run), "cargo run");
|
|
assert_eq!(run.debug_adapter.as_deref(), Some("codelldb"));
|
|
assert!(find(&c, "cargo:test").is_test);
|
|
assert_eq!(cmdline(find(&c, "cargo:test")), "cargo test");
|
|
}
|
|
|
|
#[test]
|
|
fn cargo_multiple_bins() {
|
|
let dir = project(&[
|
|
(
|
|
"Cargo.toml",
|
|
"[package]\nname = \"tool\"\nversion = \"0.1.0\"\n\n[[bin]]\nname = \"special\"\npath = \"tools/special.rs\"\n",
|
|
),
|
|
("src/main.rs", ""),
|
|
("src/bin/extra.rs", ""),
|
|
("src/bin/multi/main.rs", ""),
|
|
]);
|
|
let c = detect(dir.path());
|
|
assert_eq!(cmdline(find(&c, "cargo:run:tool")), "cargo run --bin tool");
|
|
assert_eq!(cmdline(find(&c, "cargo:run:special")), "cargo run --bin special");
|
|
assert_eq!(cmdline(find(&c, "cargo:run:extra")), "cargo run --bin extra");
|
|
assert_eq!(cmdline(find(&c, "cargo:run:multi")), "cargo run --bin multi");
|
|
}
|
|
|
|
#[test]
|
|
fn cargo_workspace() {
|
|
let dir = project(&[
|
|
("Cargo.toml", "[workspace]\nmembers = [\"crates/*\", \"cli\"]\nexclude = [\"crates/skip\"]\n"),
|
|
("crates/core/Cargo.toml", "[package]\nname = \"core\"\n"),
|
|
("crates/core/src/lib.rs", ""),
|
|
("crates/skip/Cargo.toml", "[package]\nname = \"skip\"\n"),
|
|
("crates/skip/src/main.rs", ""),
|
|
("cli/Cargo.toml", "[package]\nname = \"openidea-cli\"\n"),
|
|
("cli/src/main.rs", ""),
|
|
]);
|
|
let c = detect(dir.path());
|
|
assert_eq!(ids(&c), ["cargo:run:openidea-cli", "cargo:test", "cargo:build"]);
|
|
assert_eq!(cmdline(&c[0]), "cargo run -p openidea-cli --bin openidea-cli");
|
|
assert_eq!(cmdline(find(&c, "cargo:test")), "cargo test --workspace");
|
|
}
|
|
|
|
#[test]
|
|
fn gradle_projects() {
|
|
let dir = project(&[
|
|
("build.gradle.kts", "plugins {\n application\n id(\"org.springframework.boot\") version \"3.2.0\"\n}\n"),
|
|
("gradlew", "#!/bin/sh"),
|
|
]);
|
|
let c = detect(dir.path());
|
|
assert_eq!(ids(&c), ["gradle:build", "gradle:test", "gradle:run", "gradle:bootRun"]);
|
|
if cfg!(unix) {
|
|
assert_eq!(cmdline(find(&c, "gradle:bootRun")), "./gradlew bootRun");
|
|
}
|
|
assert!(find(&c, "gradle:test").is_test);
|
|
|
|
let dir = project(&[("build.gradle", "plugins { id 'java' }\n")]);
|
|
let c = detect(dir.path());
|
|
assert_eq!(ids(&c), ["gradle:build", "gradle:test"]);
|
|
assert_eq!(c[0].command, "gradle");
|
|
|
|
let dir = project(&[("build.gradle", "apply plugin: 'application'\n")]);
|
|
assert!(detect(dir.path()).iter().any(|c| c.id == "gradle:run"));
|
|
|
|
// Multi-module: Spring Boot applied in a subproject.
|
|
let dir = project(&[
|
|
("settings.gradle", "include 'app'"),
|
|
("build.gradle", ""),
|
|
("app/build.gradle", "plugins { id 'org.springframework.boot' version '3.2.0' }"),
|
|
]);
|
|
assert!(detect(dir.path()).iter().any(|c| c.id == "gradle:bootRun"));
|
|
}
|
|
|
|
#[test]
|
|
fn maven_projects() {
|
|
let pom = "<project><build><plugins><plugin><groupId>org.springframework.boot</groupId>\
|
|
<artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>";
|
|
let dir = project(&[("pom.xml", pom), ("mvnw", "#!/bin/sh")]);
|
|
let c = detect(dir.path());
|
|
assert_eq!(ids(&c), ["maven:package", "maven:test", "maven:spring-boot:run"]);
|
|
if cfg!(unix) {
|
|
assert_eq!(cmdline(find(&c, "maven:test")), "./mvnw test");
|
|
}
|
|
assert!(find(&c, "maven:test").is_test);
|
|
|
|
let dir = project(&[("pom.xml", "<project/>")]);
|
|
let c = detect(dir.path());
|
|
assert_eq!(ids(&c), ["maven:package", "maven:test"]);
|
|
assert_eq!(c[0].command, "mvn");
|
|
}
|
|
|
|
const BOOT_POM: &str = "<project><artifactId>app</artifactId><build><plugins><plugin>\
|
|
<artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>";
|
|
const PARENT_POM: &str = "<project><packaging>pom</packaging><modules><module>app</module></modules>\
|
|
<build><pluginManagement><plugins><plugin>\
|
|
<artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></pluginManagement>\
|
|
</build></project>";
|
|
const BOOT_MAIN: &str = "package org.dromara;\n\nimport org.springframework.boot.SpringApplication;\n\
|
|
import org.springframework.boot.autoconfigure.SpringBootApplication;\n\n\
|
|
/** Entry. */\n@SpringBootApplication\npublic class DromaraApplication {\n\
|
|
\x20 public static void main(String[] args) {\n\
|
|
\x20 SpringApplication.run(DromaraApplication.class, args);\n }\n}\n";
|
|
|
|
/// A folder holding a Maven back end and an npm front end, each its own repository
|
|
/// (RuoYi-Vue-Plus + plus-ui): the Maven build is detected where it is.
|
|
#[test]
|
|
fn builds_nested_in_a_folder_of_repositories() {
|
|
let dir = project(&[
|
|
("RuoYi-Vue-Plus/pom.xml", PARENT_POM),
|
|
("RuoYi-Vue-Plus/ruoyi-admin/pom.xml", BOOT_POM),
|
|
("RuoYi-Vue-Plus/ruoyi-admin/src/main/java/org/dromara/DromaraApplication.java", BOOT_MAIN),
|
|
("RuoYi-Vue-Plus/ruoyi-admin/src/main/java/org/dromara/web/Controller.java", "package org.dromara.web;"),
|
|
("RuoYi-Vue-Plus/ruoyi-common/pom.xml", "<project><packaging>pom</packaging></project>"),
|
|
("RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/pom.xml", "<project/>"),
|
|
("RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/A.java", "class A {}"),
|
|
("plus-ui/package.json", r#"{"scripts":{"dev":"vite"}}"#),
|
|
]);
|
|
let c = detect(dir.path());
|
|
let app = find(&c, "application:RuoYi-Vue-Plus/ruoyi-admin:org.dromara.DromaraApplication");
|
|
assert_eq!(app.name, "DromaraApplication");
|
|
assert_eq!(app.kind, "spring-boot");
|
|
assert_eq!(cmdline(app), "java -cp ${classpath} org.dromara.DromaraApplication");
|
|
assert_eq!(app.cwd.as_deref(), Some(Path::new("RuoYi-Vue-Plus/ruoyi-admin")));
|
|
assert_eq!(app.debug_adapter.as_deref(), Some("java-debug"));
|
|
let package = find(&c, "maven:package:RuoYi-Vue-Plus");
|
|
assert_eq!(package.name, "RuoYi-Vue-Plus: mvn package");
|
|
assert_eq!(cmdline(package), "mvn package");
|
|
assert_eq!(package.cwd.as_deref(), Some(Path::new("RuoYi-Vue-Plus")));
|
|
assert!(find(&c, "maven:test:RuoYi-Vue-Plus").is_test);
|
|
assert!(ids(&c).contains(&"maven:spring-boot:run:RuoYi-Vue-Plus"));
|
|
assert!(ids(&c).contains(&"npm:plus-ui:dev"));
|
|
// Modules of the nested build are not builds of their own; only one application.
|
|
assert!(!ids(&c).iter().any(|id| id.contains("ruoyi-common")));
|
|
assert_eq!(c.iter().filter(|c| c.kind == "spring-boot").count(), 1);
|
|
}
|
|
|
|
#[test]
|
|
fn nested_builds_of_every_kind_and_depth() {
|
|
let dir = project(&[
|
|
("services/api/settings.gradle.kts", "include(\"app\")"),
|
|
("services/api/gradlew", "#!/bin/sh"),
|
|
("services/api/app/build.gradle.kts", "plugins { id(\"org.springframework.boot\") version \"3.3.0\" }\n"),
|
|
(
|
|
"services/api/app/src/main/kotlin/com/acme/Api.kt",
|
|
"package com.acme\n\n@SpringBootApplication\nclass Api\n\nfun main(args: Array<String>) {\n runApplication<Api>(*args)\n}\n",
|
|
),
|
|
("billing/pom.xml", BOOT_POM),
|
|
("billing/mvnw", "#!/bin/sh"),
|
|
(
|
|
"billing/src/main/java/Billing.java",
|
|
"@SpringBootApplication\nclass Billing { public static void main(String[] a) {} }",
|
|
),
|
|
// Deeper than a build is looked for.
|
|
("a/b/c/d/pom.xml", "<project/>"),
|
|
// Build output and sources are not searched.
|
|
("web/node_modules/x/pom.xml", "<project/>"),
|
|
("tool/src/pom.xml", "<project/>"),
|
|
]);
|
|
let c = detect(dir.path());
|
|
let boot = find(&c, "gradle:bootRun:services/api");
|
|
assert_eq!(boot.name, "services/api: gradle bootRun");
|
|
assert_eq!(boot.cwd.as_deref(), Some(Path::new("services/api")));
|
|
if cfg!(unix) {
|
|
assert_eq!(cmdline(boot), "./gradlew bootRun");
|
|
assert_eq!(cmdline(find(&c, "maven:package:billing")), "./mvnw package");
|
|
}
|
|
assert!(find(&c, "gradle:test:services/api").is_test);
|
|
let api = find(&c, "application:services/api/app:com.acme.ApiKt");
|
|
assert_eq!(api.cwd.as_deref(), Some(Path::new("services/api/app")));
|
|
let billing = find(&c, "application:billing:Billing");
|
|
assert_eq!(billing.cwd.as_deref(), Some(Path::new("billing")));
|
|
let builds: Vec<&str> =
|
|
ids(&c).into_iter().filter(|id| id.starts_with("maven:package") || id.starts_with("gradle:build")).collect();
|
|
assert_eq!(builds, ["gradle:build:services/api", "maven:package:billing"]);
|
|
}
|
|
|
|
#[test]
|
|
fn a_root_build_disables_the_nested_scan() {
|
|
let dir = project(&[
|
|
("pom.xml", BOOT_POM),
|
|
(
|
|
"src/main/java/demo/App.java",
|
|
"package demo;\n@SpringBootApplication public class App {\n public static void main(String[] args) {}\n}\n",
|
|
),
|
|
("tools/gen/pom.xml", "<project/>"),
|
|
]);
|
|
let c = detect(dir.path());
|
|
assert!(ids(&c).contains(&"maven:package"));
|
|
assert!(!ids(&c).iter().any(|id| id.contains("tools")));
|
|
let app = find(&c, "application:demo.App");
|
|
assert_eq!(app.cwd, None);
|
|
}
|
|
|
|
#[test]
|
|
fn spring_boot_applications_need_the_plugin_and_a_main() {
|
|
let main =
|
|
"package demo;\n@SpringBootApplication\npublic class App {\n public static void main(String[] args) {}\n}\n";
|
|
// A library using Spring Boot without the plugin, a configuration class without `main`,
|
|
// the annotation in a comment, a parent POM.
|
|
let dir = project(&[
|
|
("lib/pom.xml", "<project><dependencies>spring-boot-starter</dependencies></project>"),
|
|
("lib/src/main/java/demo/App.java", main),
|
|
("svc/pom.xml", BOOT_POM),
|
|
("svc/src/main/java/demo/Config.java", "package demo;\n@SpringBootApplication\npublic class Config {}\n"),
|
|
(
|
|
"svc/src/main/java/demo/Old.java",
|
|
"package demo;\n// @SpringBootApplication\nclass Old { public static void main(String[] a) {} }\n",
|
|
),
|
|
("gr/build.gradle", "dependencies { implementation 'org.springframework.boot:spring-boot-starter' }\n"),
|
|
("gr/src/main/java/demo/App.java", main),
|
|
]);
|
|
let c = detect(dir.path());
|
|
assert!(!c.iter().any(|c| c.kind == "spring-boot"), "{:?}", ids(&c));
|
|
}
|
|
|
|
#[test]
|
|
fn go_projects() {
|
|
let dir = project(&[
|
|
("go.mod", "module example.com/x\n"),
|
|
("main.go", "// comment\npackage main\n\nfunc main() {}\n"),
|
|
("main_test.go", "package main\n"),
|
|
("cmd/server/main.go", "package main\n"),
|
|
("cmd/lib/lib.go", "package lib\n"),
|
|
]);
|
|
let c = detect(dir.path());
|
|
assert_eq!(ids(&c), ["go:run", "go:run:server", "go:test"]);
|
|
assert_eq!(cmdline(find(&c, "go:run:server")), "go run ./cmd/server");
|
|
assert_eq!(cmdline(find(&c, "go:test")), "go test ./...");
|
|
assert!(find(&c, "go:test").is_test);
|
|
assert_eq!(find(&c, "go:run").debug_adapter.as_deref(), Some("delve"));
|
|
|
|
let dir = project(&[("go.mod", "module x\n"), ("lib.go", "package x\n")]);
|
|
assert_eq!(ids(&detect(dir.path())), ["go:test"]);
|
|
}
|
|
|
|
#[test]
|
|
fn python_projects() {
|
|
let dir = project(&[
|
|
("pyproject.toml", "[tool.pytest.ini_options]\naddopts = \"-q\"\n"),
|
|
("uv.lock", ""),
|
|
("src/mypkg/__init__.py", ""),
|
|
("src/mypkg/__main__.py", ""),
|
|
("manage.py", ""),
|
|
]);
|
|
let c = detect(dir.path());
|
|
assert_eq!(ids(&c), ["django:runserver", "python:module:mypkg", "pytest"]);
|
|
let pytest = find(&c, "pytest");
|
|
assert_eq!(cmdline(pytest), "uv run pytest");
|
|
assert!(pytest.is_test);
|
|
assert_eq!(pytest.debug_adapter.as_deref(), Some("debugpy"));
|
|
assert_eq!(cmdline(find(&c, "python:module:mypkg")), "uv run python -m mypkg");
|
|
assert_eq!(cmdline(find(&c, "django:runserver")), "uv run python manage.py runserver");
|
|
|
|
let dir = project(&[("pytest.ini", "[pytest]\n"), ("poetry.lock", "")]);
|
|
assert_eq!(cmdline(find(&detect(dir.path()), "pytest")), "poetry run pytest");
|
|
|
|
let dir = project(&[("setup.cfg", "[tool:pytest]\ntestpaths = tests\n")]);
|
|
assert_eq!(cmdline(find(&detect(dir.path()), "pytest")), "pytest");
|
|
|
|
let dir = project(&[("requirements.txt", "requests\n"), ("app/__main__.py", "")]);
|
|
let c = detect(dir.path());
|
|
assert_eq!(ids(&c), ["python:module:app"]);
|
|
}
|
|
|
|
#[cfg(unix)]
|
|
#[test]
|
|
fn python_virtualenv() {
|
|
let dir = project(&[("tox.ini", ""), (".venv/bin/python", ""), (".venv/bin/pytest", ""), ("manage.py", "")]);
|
|
let c = detect(dir.path());
|
|
assert_eq!(cmdline(find(&c, "pytest")), ".venv/bin/pytest");
|
|
assert_eq!(cmdline(find(&c, "django:runserver")), ".venv/bin/python manage.py runserver");
|
|
}
|
|
|
|
#[cfg(unix)]
|
|
#[test]
|
|
fn bare_virtualenv_marks_a_python_project() {
|
|
// No packaging files: the venv alone says which interpreter / pytest to use.
|
|
let dir = project(&[(".venv/bin/python", ""), (".venv/bin/pytest", ""), ("worker/test_w.py", "")]);
|
|
assert_eq!(cmdline(find(&detect(dir.path()), "pytest")), ".venv/bin/pytest");
|
|
let dir = project(&[(".venv/bin/python", "")]);
|
|
assert!(detect(dir.path()).is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn makefile_targets() {
|
|
let makefile = "\
|
|
CC := gcc
|
|
VERSION = 1.0:beta
|
|
.PHONY: all test clean
|
|
export PATH := $(PATH)
|
|
|
|
all: build test
|
|
build: main.o
|
|
\tgcc -o app main.o
|
|
%.o: %.c
|
|
\t$(CC) -c $<
|
|
test::
|
|
\t./run-tests
|
|
clean lint: ; rm -rf out
|
|
$(OUT)/x: y
|
|
out/file: z
|
|
define TEMPLATE
|
|
inner: x
|
|
endef
|
|
# comment: no
|
|
";
|
|
let dir = project(&[("Makefile", makefile)]);
|
|
let c = detect(dir.path());
|
|
assert_eq!(ids(&c), ["make:all", "make:build", "make:test", "make:clean", "make:lint"]);
|
|
assert_eq!(cmdline(find(&c, "make:build")), "make build");
|
|
assert!(find(&c, "make:test").is_test);
|
|
}
|
|
|
|
#[test]
|
|
fn deno_tasks() {
|
|
let dir = project(&[(
|
|
"deno.jsonc",
|
|
"{\n // tasks\n \"tasks\": {\n \"dev\": \"deno run -A main.ts\", /* inline */\n \"test\": \"deno test\",\n },\n \"url\": \"https://x.dev//y\"\n}\n",
|
|
)]);
|
|
let c = detect(dir.path());
|
|
assert_eq!(ids(&c), ["deno:dev", "deno:test"]);
|
|
assert_eq!(cmdline(&c[0]), "deno task dev");
|
|
assert!(c[1].is_test);
|
|
}
|
|
|
|
#[test]
|
|
fn godot_projects_run_their_main_scene() {
|
|
let dir = project(&[("project.godot", "config_version=5\n"), ("demo/project.godot", "config_version=5\n")]);
|
|
let c = detect(dir.path());
|
|
let root = find(&c, "godot:project");
|
|
assert_eq!((root.name.as_str(), root.kind.as_str()), ("Godot Project", "godot"));
|
|
assert_eq!(cmdline(root), "godot --path .");
|
|
assert_eq!(root.cwd, None);
|
|
assert_eq!(root.debug_adapter.as_deref(), Some("godot"));
|
|
let demo = find(&c, "godot:project:demo");
|
|
assert_eq!(demo.name, "Godot Project (demo)");
|
|
assert_eq!(demo.cwd, Some(std::path::PathBuf::from("demo").into()));
|
|
assert!(detect(project(&[("main.gd", "extends Node\n")]).path()).is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn just_recipes() {
|
|
let justfile = "\
|
|
set shell := [\"bash\", \"-c\"]
|
|
alias b := build
|
|
version := \"1.0\"
|
|
export RUST_LOG := \"info\"
|
|
|
|
# Build it
|
|
build target='debug':
|
|
cargo build
|
|
@test *args: build
|
|
cargo test {{args}}
|
|
_hidden:
|
|
echo
|
|
[private]
|
|
secret:
|
|
echo
|
|
serve port=\"8080\": (build \"release\")
|
|
./serve
|
|
";
|
|
let dir = project(&[("justfile", justfile)]);
|
|
let c = detect(dir.path());
|
|
assert_eq!(ids(&c), ["just:build", "just:test", "just:serve"]);
|
|
assert_eq!(cmdline(&c[1]), "just test");
|
|
assert!(c[1].is_test);
|
|
}
|
|
|
|
#[test]
|
|
fn ordering_is_stable_across_ecosystems() {
|
|
let dir = project(&[
|
|
("Makefile", "all:\n"),
|
|
("package.json", r#"{"scripts":{"dev":"x"}}"#),
|
|
("Cargo.toml", "[package]\nname = \"a\"\n"),
|
|
("src/main.rs", ""),
|
|
("go.mod", "module a\n"),
|
|
]);
|
|
let first = ids(&detect(dir.path())).into_iter().map(str::to_owned).collect::<Vec<_>>();
|
|
assert_eq!(first, ["npm:dev", "cargo:run:a", "cargo:test", "cargo:build", "go:test", "make:all"]);
|
|
for _ in 0..3 {
|
|
assert_eq!(detect(dir.path()).iter().map(|c| c.id.clone()).collect::<Vec<_>>(), first);
|
|
}
|
|
assert!(detect(dir.path()).iter().all(|c| c.detected));
|
|
}
|
|
|
|
#[test]
|
|
fn nested_packages_in_polyglot_repo() {
|
|
let dir = project(&[
|
|
("Makefile", "all:\n\techo\n"),
|
|
("web/package.json", r#"{"scripts":{"dev":"vite","test":"vitest"}}"#),
|
|
("web/pnpm-lock.yaml", ""),
|
|
("api/go.mod", "module x\n"),
|
|
("api/main.go", "package main\nfunc main() {}\n"),
|
|
("node_modules/x/package.json", r#"{"scripts":{"no":"x"}}"#),
|
|
]);
|
|
let c = detect(dir.path());
|
|
let dev = find(&c, "npm:web:dev");
|
|
assert_eq!(cmdline(dev), "pnpm run dev");
|
|
assert_eq!(dev.cwd.as_deref(), Some(Path::new("web")));
|
|
assert_eq!(dev.name, "web: pnpm run dev");
|
|
assert!(find(&c, "npm:web:test").is_test);
|
|
let run = find(&c, "go:run:api");
|
|
assert_eq!(run.cwd.as_deref(), Some(Path::new("api")));
|
|
assert_eq!(run.name, "api: go run .");
|
|
let test = find(&c, "go:test:api");
|
|
assert!(test.is_test);
|
|
assert_eq!(cmdline(test), "go test ./...");
|
|
assert!(!ids(&c).iter().any(|id| id.contains("no")));
|
|
}
|
|
|
|
#[test]
|
|
fn root_package_disables_nested_scan() {
|
|
let dir = project(&[
|
|
("package.json", r#"{"scripts":{"build":"tsc"}}"#),
|
|
("tools/package.json", r#"{"scripts":{"gen":"x"}}"#),
|
|
("go.mod", "module x\n"),
|
|
("svc/go.mod", "module y\n"),
|
|
]);
|
|
let c = detect(dir.path());
|
|
assert!(!ids(&c).contains(&"npm:tools:gen"));
|
|
assert!(ids(&c).contains(&"go:test"));
|
|
assert!(!ids(&c).contains(&"go:test:svc"));
|
|
}
|