Cross-platform tool for generating clean, testable FTC robot projects without editing the SDK installation. Features: - Standalone project generation with proper separation from SDK - Per-project SDK configuration via .weevil.toml - Local unit testing support (no robot required) - Cross-platform build/deploy scripts (Linux/macOS/Windows) - Project upgrade system preserving user code - Configuration management commands - Comprehensive test suite (11 passing tests) - Zero-warning builds Architecture: - Pure Rust implementation with embedded Gradle wrapper - Projects use deployToSDK task to copy code to FTC SDK TeamCode - Git-ready projects with automatic initialization - USB and WiFi deployment with auto-detection Commands: - weevil new <name> - Create new project - weevil upgrade <path> - Update project infrastructure - weevil config <path> - View/modify project configuration - weevil sdk status/install/update - Manage SDKs Addresses the core problem: FTC's SDK structure forces students to edit framework internals instead of separating concerns like industry standard practices. Weevil enables proper software engineering workflows for robotics education.
185 lines
6.5 KiB
Rust
185 lines
6.5 KiB
Rust
// File: tests/integration/project_lifecycle_tests.rs
|
|
// Integration tests - full project lifecycle
|
|
|
|
use tempfile::TempDir;
|
|
use std::path::PathBuf;
|
|
use std::fs;
|
|
use std::process::Command;
|
|
use include_dir::{include_dir, Dir};
|
|
|
|
// Embed test fixtures
|
|
static MOCK_SDK: Dir = include_dir!("$CARGO_MANIFEST_DIR/tests/fixtures/mock-ftc-sdk");
|
|
|
|
#[test]
|
|
fn test_project_creation_with_mock_sdk() {
|
|
let test_dir = TempDir::new().unwrap();
|
|
let sdk_dir = test_dir.path().join("mock-sdk");
|
|
let project_dir = test_dir.path().join("test-robot");
|
|
|
|
// Extract mock SDK
|
|
MOCK_SDK.extract(&sdk_dir).unwrap();
|
|
|
|
// Create project using weevil
|
|
let output = Command::new("cargo")
|
|
.args(&["run", "--", "new", "test-robot", "--ftc-sdk", sdk_dir.to_str().unwrap()])
|
|
.current_dir(env!("CARGO_MANIFEST_DIR"))
|
|
.output()
|
|
.expect("Failed to run weevil");
|
|
|
|
// Verify project was created
|
|
assert!(output.status.success(), "weevil new failed: {}", String::from_utf8_lossy(&output.stderr));
|
|
assert!(project_dir.join(".weevil.toml").exists());
|
|
assert!(project_dir.join("build.gradle.kts").exists());
|
|
assert!(project_dir.join("src/main/java/robot").exists());
|
|
}
|
|
|
|
#[test]
|
|
fn test_project_config_persistence() {
|
|
let test_dir = TempDir::new().unwrap();
|
|
let sdk_dir = test_dir.path().join("mock-sdk");
|
|
let project_dir = test_dir.path().join("config-test");
|
|
|
|
// Extract mock SDK
|
|
MOCK_SDK.extract(&sdk_dir).unwrap();
|
|
|
|
// Create project
|
|
Command::new("cargo")
|
|
.args(&["run", "--", "new", "config-test", "--ftc-sdk", sdk_dir.to_str().unwrap()])
|
|
.current_dir(env!("CARGO_MANIFEST_DIR"))
|
|
.output()
|
|
.expect("Failed to create project");
|
|
|
|
// Read config
|
|
let config_content = fs::read_to_string(project_dir.join(".weevil.toml")).unwrap();
|
|
|
|
assert!(config_content.contains("project_name = \"config-test\""));
|
|
assert!(config_content.contains(&format!("ftc_sdk_path = \"{}\"", sdk_dir.display())));
|
|
}
|
|
|
|
#[test]
|
|
fn test_project_upgrade_preserves_code() {
|
|
let test_dir = TempDir::new().unwrap();
|
|
let sdk_dir = test_dir.path().join("mock-sdk");
|
|
let project_dir = test_dir.path().join("upgrade-test");
|
|
|
|
// Extract mock SDK
|
|
MOCK_SDK.extract(&sdk_dir).unwrap();
|
|
|
|
// Create project
|
|
Command::new("cargo")
|
|
.args(&["run", "--", "new", "upgrade-test", "--ftc-sdk", sdk_dir.to_str().unwrap()])
|
|
.current_dir(env!("CARGO_MANIFEST_DIR"))
|
|
.output()
|
|
.expect("Failed to create project");
|
|
|
|
// Add custom code
|
|
let custom_file = project_dir.join("src/main/java/robot/CustomCode.java");
|
|
fs::write(&custom_file, "// My custom robot code").unwrap();
|
|
|
|
// Upgrade project
|
|
Command::new("cargo")
|
|
.args(&["run", "--", "upgrade", project_dir.to_str().unwrap()])
|
|
.current_dir(env!("CARGO_MANIFEST_DIR"))
|
|
.output()
|
|
.expect("Failed to upgrade project");
|
|
|
|
// Verify custom code still exists
|
|
assert!(custom_file.exists());
|
|
let content = fs::read_to_string(&custom_file).unwrap();
|
|
assert!(content.contains("My custom robot code"));
|
|
|
|
// Verify config was updated
|
|
assert!(project_dir.join(".weevil.toml").exists());
|
|
assert!(!project_dir.join(".weevil-version").exists());
|
|
}
|
|
|
|
#[test]
|
|
fn test_build_scripts_read_from_config() {
|
|
let test_dir = TempDir::new().unwrap();
|
|
let sdk_dir = test_dir.path().join("mock-sdk");
|
|
let project_dir = test_dir.path().join("build-test");
|
|
|
|
// Extract mock SDK
|
|
MOCK_SDK.extract(&sdk_dir).unwrap();
|
|
|
|
// Create project
|
|
Command::new("cargo")
|
|
.args(&["run", "--", "new", "build-test", "--ftc-sdk", sdk_dir.to_str().unwrap()])
|
|
.current_dir(env!("CARGO_MANIFEST_DIR"))
|
|
.output()
|
|
.expect("Failed to create project");
|
|
|
|
// Check build.sh contains config reading
|
|
let build_sh = fs::read_to_string(project_dir.join("build.sh")).unwrap();
|
|
assert!(build_sh.contains(".weevil.toml"));
|
|
assert!(build_sh.contains("ftc_sdk_path"));
|
|
|
|
// Check build.bat contains config reading
|
|
let build_bat = fs::read_to_string(project_dir.join("build.bat")).unwrap();
|
|
assert!(build_bat.contains(".weevil.toml"));
|
|
assert!(build_bat.contains("ftc_sdk_path"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_config_command_show() {
|
|
let test_dir = TempDir::new().unwrap();
|
|
let sdk_dir = test_dir.path().join("mock-sdk");
|
|
let project_dir = test_dir.path().join("config-show-test");
|
|
|
|
// Extract mock SDK
|
|
MOCK_SDK.extract(&sdk_dir).unwrap();
|
|
|
|
// Create project
|
|
Command::new("cargo")
|
|
.args(&["run", "--", "new", "config-show-test", "--ftc-sdk", sdk_dir.to_str().unwrap()])
|
|
.current_dir(env!("CARGO_MANIFEST_DIR"))
|
|
.output()
|
|
.expect("Failed to create project");
|
|
|
|
// Show config
|
|
let output = Command::new("cargo")
|
|
.args(&["run", "--", "config", project_dir.to_str().unwrap()])
|
|
.current_dir(env!("CARGO_MANIFEST_DIR"))
|
|
.output()
|
|
.expect("Failed to show config");
|
|
|
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
assert!(stdout.contains("config-show-test"));
|
|
assert!(stdout.contains(&sdk_dir.display().to_string()));
|
|
}
|
|
|
|
#[test]
|
|
fn test_multiple_projects_different_sdks() {
|
|
let test_dir = TempDir::new().unwrap();
|
|
let sdk1 = test_dir.path().join("sdk-v10");
|
|
let sdk2 = test_dir.path().join("sdk-v11");
|
|
let project1 = test_dir.path().join("robot1");
|
|
let project2 = test_dir.path().join("robot2");
|
|
|
|
// Create two different SDK versions
|
|
MOCK_SDK.extract(&sdk1).unwrap();
|
|
MOCK_SDK.extract(&sdk2).unwrap();
|
|
fs::write(sdk2.join(".version"), "v11.0.0").unwrap();
|
|
|
|
// Create two projects with different SDKs
|
|
Command::new("cargo")
|
|
.args(&["run", "--", "new", "robot1", "--ftc-sdk", sdk1.to_str().unwrap()])
|
|
.current_dir(env!("CARGO_MANIFEST_DIR"))
|
|
.output()
|
|
.expect("Failed to create project1");
|
|
|
|
Command::new("cargo")
|
|
.args(&["run", "--", "new", "robot2", "--ftc-sdk", sdk2.to_str().unwrap()])
|
|
.current_dir(env!("CARGO_MANIFEST_DIR"))
|
|
.output()
|
|
.expect("Failed to create project2");
|
|
|
|
// Verify each project has correct SDK
|
|
let config1 = fs::read_to_string(project1.join(".weevil.toml")).unwrap();
|
|
let config2 = fs::read_to_string(project2.join(".weevil.toml")).unwrap();
|
|
|
|
assert!(config1.contains(&sdk1.display().to_string()));
|
|
assert!(config2.contains(&sdk2.display().to_string()));
|
|
assert!(config1.contains("v10.1.1"));
|
|
assert!(config2.contains("v11.0.0"));
|
|
} |