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.
157 lines
6.0 KiB
Rust
157 lines
6.0 KiB
Rust
// File: tests/project_lifecycle.rs
|
|
// Integration tests - full project lifecycle
|
|
|
|
use tempfile::TempDir;
|
|
use std::fs;
|
|
use weevil::project::{ProjectBuilder, ProjectConfig};
|
|
use weevil::sdk::SdkConfig;
|
|
|
|
// Note: These tests use the actual FTC SDK if available, or skip if not
|
|
// For true unit testing with mocks, we'd need to refactor to use dependency injection
|
|
|
|
#[test]
|
|
fn test_config_create_and_save() {
|
|
let temp_dir = TempDir::new().unwrap();
|
|
let sdk_path = temp_dir.path().join("mock-sdk");
|
|
|
|
// Create minimal SDK structure
|
|
fs::create_dir_all(sdk_path.join("TeamCode/src/main/java")).unwrap();
|
|
fs::create_dir_all(sdk_path.join("FtcRobotController")).unwrap();
|
|
fs::write(sdk_path.join("build.gradle"), "// test").unwrap();
|
|
fs::write(sdk_path.join(".version"), "v10.1.1").unwrap();
|
|
|
|
let config = ProjectConfig::new("test-robot", sdk_path.clone()).unwrap();
|
|
|
|
assert_eq!(config.project_name, "test-robot");
|
|
assert_eq!(config.ftc_sdk_path, sdk_path);
|
|
assert_eq!(config.weevil_version, "1.0.0");
|
|
|
|
// Save and reload
|
|
let project_path = temp_dir.path().join("project");
|
|
fs::create_dir_all(&project_path).unwrap();
|
|
config.save(&project_path).unwrap();
|
|
|
|
let loaded = ProjectConfig::load(&project_path).unwrap();
|
|
assert_eq!(loaded.project_name, config.project_name);
|
|
assert_eq!(loaded.ftc_sdk_path, config.ftc_sdk_path);
|
|
}
|
|
|
|
#[test]
|
|
fn test_config_toml_format() {
|
|
let temp_dir = TempDir::new().unwrap();
|
|
let sdk_path = temp_dir.path().join("sdk");
|
|
|
|
// Create minimal SDK
|
|
fs::create_dir_all(sdk_path.join("TeamCode/src/main/java")).unwrap();
|
|
fs::create_dir_all(sdk_path.join("FtcRobotController")).unwrap();
|
|
fs::write(sdk_path.join("build.gradle"), "// test").unwrap();
|
|
fs::write(sdk_path.join(".version"), "v10.1.1").unwrap();
|
|
|
|
let config = ProjectConfig::new("my-robot", sdk_path).unwrap();
|
|
|
|
let project_path = temp_dir.path().join("project");
|
|
fs::create_dir_all(&project_path).unwrap();
|
|
config.save(&project_path).unwrap();
|
|
|
|
let content = fs::read_to_string(project_path.join(".weevil.toml")).unwrap();
|
|
|
|
assert!(content.contains("project_name = \"my-robot\""));
|
|
assert!(content.contains("weevil_version = \"1.0.0\""));
|
|
assert!(content.contains("ftc_sdk_path"));
|
|
assert!(content.contains("ftc_sdk_version"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_project_structure_creation() {
|
|
let temp_dir = TempDir::new().unwrap();
|
|
let sdk_path = temp_dir.path().join("sdk");
|
|
|
|
// Create minimal SDK
|
|
fs::create_dir_all(sdk_path.join("TeamCode/src/main/java")).unwrap();
|
|
fs::create_dir_all(sdk_path.join("FtcRobotController")).unwrap();
|
|
fs::write(sdk_path.join("build.gradle"), "// test").unwrap();
|
|
fs::write(sdk_path.join(".version"), "v10.1.1").unwrap();
|
|
|
|
let sdk_config = SdkConfig {
|
|
ftc_sdk_path: sdk_path.clone(),
|
|
android_sdk_path: temp_dir.path().join("android-sdk"),
|
|
cache_dir: temp_dir.path().join("cache"),
|
|
};
|
|
|
|
let builder = ProjectBuilder::new("test-robot", &sdk_config).unwrap();
|
|
let project_path = temp_dir.path().join("test-robot");
|
|
|
|
builder.create(&project_path, &sdk_config).unwrap();
|
|
|
|
// Verify structure
|
|
assert!(project_path.join("README.md").exists());
|
|
assert!(project_path.join("build.gradle.kts").exists());
|
|
assert!(project_path.join("gradlew").exists());
|
|
assert!(project_path.join(".weevil.toml").exists());
|
|
assert!(project_path.join("src/main/java/robot").exists());
|
|
assert!(project_path.join("src/test/java/robot").exists());
|
|
}
|
|
|
|
#[test]
|
|
fn test_build_scripts_contain_config_reading() {
|
|
let temp_dir = TempDir::new().unwrap();
|
|
let sdk_path = temp_dir.path().join("sdk");
|
|
|
|
// Create minimal SDK
|
|
fs::create_dir_all(sdk_path.join("TeamCode/src/main/java")).unwrap();
|
|
fs::create_dir_all(sdk_path.join("FtcRobotController")).unwrap();
|
|
fs::write(sdk_path.join("build.gradle"), "// test").unwrap();
|
|
fs::write(sdk_path.join(".version"), "v10.1.1").unwrap();
|
|
|
|
let sdk_config = SdkConfig {
|
|
ftc_sdk_path: sdk_path,
|
|
android_sdk_path: temp_dir.path().join("android-sdk"),
|
|
cache_dir: temp_dir.path().join("cache"),
|
|
};
|
|
|
|
let builder = ProjectBuilder::new("test", &sdk_config).unwrap();
|
|
let project_path = temp_dir.path().join("test");
|
|
|
|
builder.create(&project_path, &sdk_config).unwrap();
|
|
|
|
// Check build.sh reads from config
|
|
let build_sh = fs::read_to_string(project_path.join("build.sh")).unwrap();
|
|
assert!(build_sh.contains(".weevil.toml"));
|
|
assert!(build_sh.contains("ftc_sdk_path"));
|
|
|
|
// Check build.bat reads from config
|
|
let build_bat = fs::read_to_string(project_path.join("build.bat")).unwrap();
|
|
assert!(build_bat.contains(".weevil.toml"));
|
|
assert!(build_bat.contains("ftc_sdk_path"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_config_persistence_across_operations() {
|
|
let temp_dir = TempDir::new().unwrap();
|
|
let sdk_path = temp_dir.path().join("sdk");
|
|
|
|
// Create minimal SDK
|
|
fs::create_dir_all(sdk_path.join("TeamCode/src/main/java")).unwrap();
|
|
fs::create_dir_all(sdk_path.join("FtcRobotController")).unwrap();
|
|
fs::write(sdk_path.join("build.gradle"), "// test").unwrap();
|
|
fs::write(sdk_path.join(".version"), "v10.1.1").unwrap();
|
|
|
|
let sdk_config = SdkConfig {
|
|
ftc_sdk_path: sdk_path.clone(),
|
|
android_sdk_path: temp_dir.path().join("android-sdk"),
|
|
cache_dir: temp_dir.path().join("cache"),
|
|
};
|
|
|
|
let builder = ProjectBuilder::new("persist-test", &sdk_config).unwrap();
|
|
let project_path = temp_dir.path().join("persist-test");
|
|
|
|
builder.create(&project_path, &sdk_config).unwrap();
|
|
|
|
// Load config
|
|
let config = ProjectConfig::load(&project_path).unwrap();
|
|
|
|
assert_eq!(config.project_name, "persist-test");
|
|
assert_eq!(config.ftc_sdk_path, sdk_path);
|
|
// Version may be "unknown" if SDK isn't a git repo, which is fine for mock SDKs
|
|
assert!(config.ftc_sdk_version == "v10.1.1" || config.ftc_sdk_version == "unknown" || config.ftc_sdk_version.starts_with("commit-"));
|
|
} |