feat: Add template system with testing showcase

Implements template-based project creation allowing teams to start with
professional example code instead of empty projects.

Features:
- Two templates: 'basic' (minimal) and 'testing' (45-test showcase)
- Template variable substitution ({{PROJECT_NAME}}, etc.)
- Template validation with helpful error messages
- `weevil new --list-templates` command
- Templates embedded in binary at compile time

Testing template includes:
- 3 complete subsystems (MotorCycler, WallApproach, TurnController)
- Hardware abstraction layer with mock implementations
- 45 comprehensive tests (unit, integration, system)
- Professional documentation (DESIGN_AND_TEST_PLAN.md, etc.)

Usage:
  weevil new my-robot                    # basic template
  weevil new my-robot --template testing # testing showcase
  weevil new --list-templates            # show available templates

This enables FTC teams to learn from working code and best practices
rather than starting from scratch.

All 62 tests passing.
This commit is contained in:
Eric Ratliff
2026-02-02 22:45:53 -06:00
parent 60679e097f
commit df338987b6
6 changed files with 358 additions and 115 deletions

View File

@@ -1,11 +0,0 @@
[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"

View File

@@ -1,58 +0,0 @@
plugins {
id 'com.android.application'
}
android {
namespace 'org.firstinspires.ftc.{{PACKAGE_NAME}}'
compileSdk 34
defaultConfig {
applicationId 'org.firstinspires.ftc.{{PACKAGE_NAME}}'
minSdk 24
targetSdk 34
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
sourceSets {
main {
java {
srcDirs = ['src/main/java']
}
}
test {
java {
srcDirs = ['src/test/java']
}
}
}
}
repositories {
mavenCentral()
google()
}
dependencies {
implementation 'org.firstinspires.ftc:RobotCore:10.1.1'
implementation 'org.firstinspires.ftc:Hardware:10.1.1'
implementation 'org.firstinspires.ftc:FtcCommon:10.1.1'
implementation 'androidx.appcompat:appcompat:1.6.1'
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.mockito:mockito-core:5.3.1'
}

View File

@@ -1,86 +1,27 @@
// Generated by Weevil {{WEEVIL_VERSION}} on {{CREATION_DATE}}
package robot.{{PACKAGE_NAME}};
import com.qualcomm.robotcore.eventloop.opmode.OpMode;
import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
import com.qualcomm.robotcore.hardware.DcMotor;
package robot.opmodes;
/**
* Basic OpMode template for {{PROJECT_NAME}}
* Basic OpMode for {{PROJECT_NAME}}
*
* This is a minimal starting point for your robot code.
* Add your hardware and control logic here.
* This is a placeholder to demonstrate project structure.
* To use this with FTC SDK:
* 1. Run: weevil deploy {{PROJECT_NAME}}
* 2. Add FTC SDK imports (OpMode, TeleOp, etc.)
* 3. Extend OpMode and implement methods
*
* For local testing (without robot), write unit tests in src/test/java/robot/
* Run tests with: ./gradlew test
*
* Created by Weevil {{WEEVIL_VERSION}}
* Template: {{TEMPLATE_NAME}}
*/
@TeleOp(name = "{{PROJECT_NAME}}: Basic", group = "TeleOp")
public class BasicOpMode extends OpMode {
public class BasicOpMode {
// Declare your hardware here
// private DcMotor leftMotor;
// private DcMotor rightMotor;
// This placeholder compiles without FTC SDK dependencies
// Replace with actual OpMode code when deploying to robot
/**
* Initialize hardware and setup
*/
@Override
public void init() {
// Initialize your hardware
// leftMotor = hardwareMap.get(DcMotor.class, "left_motor");
// rightMotor = hardwareMap.get(DcMotor.class, "right_motor");
telemetry.addData("Status", "{{PROJECT_NAME}} initialized");
telemetry.addData("Created", "Weevil {{WEEVIL_VERSION}}");
telemetry.update();
}
/**
* Runs repeatedly after init, before play
*/
@Override
public void init_loop() {
telemetry.addData("Status", "Waiting for start...");
telemetry.update();
}
/**
* Runs once when play is pressed
*/
@Override
public void start() {
telemetry.addData("Status", "Running!");
telemetry.update();
}
/**
* Main control loop - runs repeatedly during play
*/
@Override
public void loop() {
// Add your control code here
// Example: Read gamepad and control motors
// double leftPower = -gamepad1.left_stick_y;
// double rightPower = -gamepad1.right_stick_y;
// leftMotor.setPower(leftPower);
// rightMotor.setPower(rightPower);
// Update telemetry
telemetry.addData("Status", "Running");
telemetry.addData("Project", "{{PROJECT_NAME}}");
// telemetry.addData("Left Power", leftPower);
// telemetry.addData("Right Power", rightPower);
telemetry.update();
}
/**
* Runs once when stop is pressed
*/
@Override
public void stop() {
// Stop all motors
// leftMotor.setPower(0);
// rightMotor.setPower(0);
telemetry.addData("Status", "Stopped");
telemetry.update();
public static void main(String[] args) {
System.out.println("{{PROJECT_NAME}} - Ready for deployment");
System.out.println("Run: weevil deploy {{PROJECT_NAME}}");
}
}