From 38f1e0f3edbc8bab175142546f564908ef6b8d23 Mon Sep 17 00:00:00 2001 From: Eric Ratliff Date: Mon, 2 Feb 2026 22:45:53 -0600 Subject: [PATCH] Fixed warnings and patched some files, working on testing --- templates/basic/.weevil.toml.template | 2 +- templates/testing/.weevil.toml.template | 16 +- tests/template_tests.rs | 305 ++++++++++++++++++++++++ 3 files changed, 317 insertions(+), 6 deletions(-) create mode 100644 tests/template_tests.rs diff --git a/templates/basic/.weevil.toml.template b/templates/basic/.weevil.toml.template index f7a235b..b24b9e7 100644 --- a/templates/basic/.weevil.toml.template +++ b/templates/basic/.weevil.toml.template @@ -1,5 +1,5 @@ [project] -name = "{{PROJECT_NAME}}" +project_name = "{{PROJECT_NAME}}" created = "{{CREATION_DATE}}" weevil_version = "{{WEEVIL_VERSION}}" template = "{{TEMPLATE_NAME}}" diff --git a/templates/testing/.weevil.toml.template b/templates/testing/.weevil.toml.template index cb627b8..b24b9e7 100644 --- a/templates/testing/.weevil.toml.template +++ b/templates/testing/.weevil.toml.template @@ -1,5 +1,11 @@ -project_name = "my-robot" -weevil_version = "1.1.0-beta.2" -ftc_sdk_path = 'C:\Users\Eric\.weevil\ftc-sdk' -ftc_sdk_version = "v10.1.1" -android_sdk_path = 'C:\Users\Eric\.weevil\android-sdk' +[project] +project_name = "{{PROJECT_NAME}}" +created = "{{CREATION_DATE}}" +weevil_version = "{{WEEVIL_VERSION}}" +template = "{{TEMPLATE_NAME}}" + +[ftc] +sdk_version = "10.1.1" + +[build] +gradle_version = "8.5" diff --git a/tests/template_tests.rs b/tests/template_tests.rs new file mode 100644 index 0000000..1a0705e --- /dev/null +++ b/tests/template_tests.rs @@ -0,0 +1,305 @@ +use anyhow::Result; +use std::fs; +use std::process::Command; +use tempfile::TempDir; + +// Import the template system +use weevil::templates::{TemplateManager, TemplateContext}; + +/// Helper to create a test template context +fn test_context(project_name: &str) -> TemplateContext { + TemplateContext { + project_name: project_name.to_string(), + package_name: project_name.to_lowercase().replace("-", ""), + creation_date: "2026-02-02T12:00:00Z".to_string(), + weevil_version: "1.1.0-test".to_string(), + template_name: "basic".to_string(), + } +} + +#[test] +fn test_template_manager_creation() { + let mgr = TemplateManager::new(); + assert!(mgr.is_ok(), "TemplateManager should be created successfully"); +} + +#[test] +fn test_template_exists() { + let mgr = TemplateManager::new().unwrap(); + + assert!(mgr.template_exists("basic"), "basic template should exist"); + assert!(mgr.template_exists("testing"), "testing template should exist"); + assert!(!mgr.template_exists("nonexistent"), "nonexistent template should not exist"); +} + +#[test] +fn test_list_templates() { + let mgr = TemplateManager::new().unwrap(); + let templates = mgr.list_templates(); + + assert_eq!(templates.len(), 2, "Should have exactly 2 templates"); + assert!(templates.iter().any(|t| t.contains("basic")), "Should list basic template"); + assert!(templates.iter().any(|t| t.contains("testing")), "Should list testing template"); +} + +#[test] +fn test_basic_template_extraction() -> Result<()> { + let mgr = TemplateManager::new()?; + let temp_dir = TempDir::new()?; + let project_dir = temp_dir.path().join("test-robot"); + fs::create_dir(&project_dir)?; + + let context = test_context("test-robot"); + let file_count = mgr.extract_template("basic", &project_dir, &context)?; + + assert!(file_count > 0, "Should extract at least one file from basic template"); + + // Verify key files exist + assert!(project_dir.join(".gitignore").exists(), ".gitignore should exist"); + assert!(project_dir.join("README.md").exists(), "README.md should exist (processed from .template)"); + assert!(project_dir.join(".weevil.toml").exists(), ".weevil.toml should exist (processed from .template)"); + assert!(project_dir.join("build.gradle").exists(), "build.gradle should exist (processed from .template)"); + assert!(project_dir.join("settings.gradle").exists(), "settings.gradle should exist"); + + // Verify OpMode exists + let opmode_path = project_dir.join("src/main/java/robot/opmodes/BasicOpMode.java"); + assert!(opmode_path.exists(), "BasicOpMode.java should exist"); + + Ok(()) +} + +#[test] +fn test_testing_template_extraction() -> Result<()> { + let mgr = TemplateManager::new()?; + let temp_dir = TempDir::new()?; + let project_dir = temp_dir.path().join("test-showcase"); + fs::create_dir(&project_dir)?; + + let mut context = test_context("test-showcase"); + context.template_name = "testing".to_string(); + + let file_count = mgr.extract_template("testing", &project_dir, &context)?; + + assert!(file_count > 20, "Testing template should have 20+ files, got {}", file_count); + + // Verify documentation files + assert!(project_dir.join("README.md").exists(), "README.md should exist"); + assert!(project_dir.join("DESIGN_AND_TEST_PLAN.md").exists(), "DESIGN_AND_TEST_PLAN.md should exist"); + assert!(project_dir.join("TESTING_GUIDE.md").exists(), "TESTING_GUIDE.md should exist"); + + // Verify subsystems + assert!(project_dir.join("src/main/java/robot/subsystems/MotorCycler.java").exists(), "MotorCycler.java should exist"); + assert!(project_dir.join("src/main/java/robot/subsystems/WallApproach.java").exists(), "WallApproach.java should exist"); + assert!(project_dir.join("src/main/java/robot/subsystems/TurnController.java").exists(), "TurnController.java should exist"); + + // Verify hardware interfaces and implementations + assert!(project_dir.join("src/main/java/robot/hardware/MotorController.java").exists(), "MotorController interface should exist"); + assert!(project_dir.join("src/main/java/robot/hardware/FtcMotorController.java").exists(), "FtcMotorController should exist"); + assert!(project_dir.join("src/main/java/robot/hardware/DistanceSensor.java").exists(), "DistanceSensor interface should exist"); + assert!(project_dir.join("src/main/java/robot/hardware/FtcDistanceSensor.java").exists(), "FtcDistanceSensor should exist"); + + // Verify test files + assert!(project_dir.join("src/test/java/robot/subsystems/MotorCyclerTest.java").exists(), "MotorCyclerTest.java should exist"); + assert!(project_dir.join("src/test/java/robot/subsystems/WallApproachTest.java").exists(), "WallApproachTest.java should exist"); + assert!(project_dir.join("src/test/java/robot/subsystems/TurnControllerTest.java").exists(), "TurnControllerTest.java should exist"); + + // Verify mock implementations + assert!(project_dir.join("src/test/java/robot/hardware/MockMotorController.java").exists(), "MockMotorController should exist"); + assert!(project_dir.join("src/test/java/robot/hardware/MockDistanceSensor.java").exists(), "MockDistanceSensor should exist"); + + Ok(()) +} + +#[test] +fn test_template_variable_substitution() -> Result<()> { + let mgr = TemplateManager::new()?; + let temp_dir = TempDir::new()?; + let project_dir = temp_dir.path().join("my-test-robot"); + fs::create_dir(&project_dir)?; + + let context = test_context("my-test-robot"); + mgr.extract_template("basic", &project_dir, &context)?; + + // Check README.md for variable substitution + let readme_path = project_dir.join("README.md"); + let readme_content = fs::read_to_string(readme_path)?; + + assert!(readme_content.contains("my-test-robot"), "README should contain project name"); + assert!(readme_content.contains("1.1.0-test"), "README should contain weevil version"); + assert!(!readme_content.contains("{{PROJECT_NAME}}"), "README should not contain template variable"); + assert!(!readme_content.contains("{{WEEVIL_VERSION}}"), "README should not contain template variable"); + + // Check .weevil.toml for variable substitution + let weevil_toml = project_dir.join(".weevil.toml"); + let toml_content = fs::read_to_string(weevil_toml)?; + + assert!(toml_content.contains("my-test-robot"), ".weevil.toml should contain project name"); + assert!(!toml_content.contains("{{PROJECT_NAME}}"), ".weevil.toml should not contain template variable"); + + Ok(()) +} + +#[test] +fn test_invalid_template_extraction() { + let mgr = TemplateManager::new().unwrap(); + let temp_dir = TempDir::new().unwrap(); + let project_dir = temp_dir.path().join("test-robot"); + fs::create_dir(&project_dir).unwrap(); + + let context = test_context("test-robot"); + let result = mgr.extract_template("nonexistent", &project_dir, &context); + + assert!(result.is_err(), "Should fail for nonexistent template"); +} + +#[test] +fn test_package_name_sanitization() { + let context1 = test_context("my-robot"); + assert_eq!(context1.package_name, "myrobot", "Hyphens should be removed"); + + let context2 = test_context("team_1234_bot"); + assert_eq!(context2.package_name, "team1234bot", "Underscores should be removed"); +} + +/// Integration test: Create a project with testing template and run gradle tests +/// This is marked with #[ignore] by default since it requires: +/// - Java installed +/// - Network access (first time to download gradle wrapper) +/// - Takes ~1-2 minutes to run +/// +/// Run with: cargo test test_testing_template_gradle_build -- --ignored --nocapture +#[test] +#[ignore] +fn test_testing_template_gradle_build() -> Result<()> { + println!("Testing complete gradle build and test execution..."); + + let mgr = TemplateManager::new()?; + let temp_dir = TempDir::new()?; + let project_dir = temp_dir.path().join("gradle-test-robot"); + fs::create_dir(&project_dir)?; + + // Extract testing template + let mut context = test_context("gradle-test-robot"); + context.template_name = "testing".to_string(); + + let file_count = mgr.extract_template("testing", &project_dir, &context)?; + println!("Extracted {} files from testing template", file_count); + + // Check if gradlew exists (should be in testing template) + let gradlew = if cfg!(windows) { + project_dir.join("gradlew.bat") + } else { + project_dir.join("gradlew") + }; + + if !gradlew.exists() { + println!("WARNING: gradlew not found in template, skipping gradle test"); + return Ok(()); + } + + // Make gradlew executable on Unix + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + let mut perms = fs::metadata(&gradlew)?.permissions(); + perms.set_mode(0o755); + fs::set_permissions(&gradlew, perms)?; + } + + println!("Running gradle test..."); + + // Run gradlew test + let output = Command::new(&gradlew) + .arg("test") + .current_dir(&project_dir) + .output()?; + + println!("=== Gradle Output ==="); + println!("{}", String::from_utf8_lossy(&output.stdout)); + + if !output.status.success() { + println!("=== Gradle Errors ==="); + println!("{}", String::from_utf8_lossy(&output.stderr)); + panic!("Gradle tests failed with status: {}", output.status); + } + + // Verify test output mentions 45 tests + let stdout = String::from_utf8_lossy(&output.stdout); + + // Look for test success indicators + let has_success = stdout.contains("BUILD SUCCESSFUL") || + stdout.contains("45 tests") || + stdout.to_lowercase().contains("tests passed"); + + assert!(has_success, "Gradle test output should indicate success"); + + println!("✓ All 45 tests passed!"); + + Ok(()) +} + +/// Test that basic template creates a valid directory structure +#[test] +fn test_basic_template_directory_structure() -> Result<()> { + let mgr = TemplateManager::new()?; + let temp_dir = TempDir::new()?; + let project_dir = temp_dir.path().join("structure-test"); + fs::create_dir(&project_dir)?; + + let context = test_context("structure-test"); + mgr.extract_template("basic", &project_dir, &context)?; + + // Verify directory structure + assert!(project_dir.join("src").is_dir(), "src directory should exist"); + assert!(project_dir.join("src/main").is_dir(), "src/main directory should exist"); + assert!(project_dir.join("src/main/java").is_dir(), "src/main/java directory should exist"); + assert!(project_dir.join("src/main/java/robot").is_dir(), "src/main/java/robot directory should exist"); + assert!(project_dir.join("src/main/java/robot/opmodes").is_dir(), "opmodes directory should exist"); + assert!(project_dir.join("src/test/java/robot").is_dir(), "test directory should exist"); + + Ok(()) +} + +/// Test that .gitignore is not named ".gitignore.template" +#[test] +fn test_gitignore_naming() -> Result<()> { + let mgr = TemplateManager::new()?; + let temp_dir = TempDir::new()?; + let project_dir = temp_dir.path().join("gitignore-test"); + fs::create_dir(&project_dir)?; + + let context = test_context("gitignore-test"); + mgr.extract_template("basic", &project_dir, &context)?; + + assert!(project_dir.join(".gitignore").exists(), ".gitignore should exist"); + assert!(!project_dir.join(".gitignore.template").exists(), ".gitignore.template should NOT exist"); + + Ok(()) +} + +/// Test that template extraction doesn't fail with unusual project names +#[test] +fn test_unusual_project_names() -> Result<()> { + let mgr = TemplateManager::new()?; + + let test_names = vec![ + "robot-2024", + "team_1234", + "FTC_Bot", + "my-awesome-bot", + ]; + + for name in test_names { + let temp_dir = TempDir::new()?; + let project_dir = temp_dir.path().join(name); + fs::create_dir(&project_dir)?; + + let context = test_context(name); + let result = mgr.extract_template("basic", &project_dir, &context); + + assert!(result.is_ok(), "Should handle project name: {}", name); + assert!(project_dir.join("README.md").exists(), "README should exist for {}", name); + } + + Ok(()) +} \ No newline at end of file