CoolFace
Apppublic

Amulya001/rl-testing-env

sourceHugging Faceupdated 5mo agoView on Hugging Face
0likes
App README

RL-Testing-Env

An OpenEnv environment for training agents to write effective software tests

![OpenEnv Compatible](https://github.com/openenv) ![Python 3.11+](https://www.python.org/downloads/) ![License: MIT](https://opensource.org/licenses/MIT)


Overview

RL-Testing-Env fills a gap in the OpenEnv ecosystem: there are no environments that train agents on code quality tasks. Writing good tests is one of the highest-leverage skills a software AI agent can have—yet no existing benchmark measures it in a grounded, executable way.

This environment presents agents with real Python code containing injected bugs and rewards them for writing pytest suites that catch those bugs, achieve coverage targets, and correctly triage regressions. Every reward signal is grounded in actual test execution, not heuristics.

Unlike synthetic benchmarks, agents must handle realistic challenges: subtle math errors, off-by-one mistakes, incorrect conditionals, and distinguishing real regressions from intentional behavior changes.


Environment Description

RL-Testing-Env simulates the real-world workflow of a software quality engineer. In production settings, QA engineers must:

  1. 1.Write unit tests that catch bugs before they reach production
  2. 2.Achieve coverage targets while identifying which components are most buggy
  3. 3.Triage test failures after code changes—distinguishing real regressions from intentional behavior changes

The environment provides three progressively difficult tasks that mirror these responsibilities. Agents receive Python source code with injected bugs and must submit pytest test files. A subprocess-based grader executes the tests securely, measures coverage, and computes rewards based on bugs caught and correct classifications.

This environment is particularly valuable for:

  • Evaluating LLM code generation capabilities in a grounded, measurable way
  • Training agents that can assist developers with test-driven development
  • Benchmarking progress on software engineering automation

Observation Space

Each observation provides the agent with everything needed to write effective tests:

FieldTypeDescription
task_idstringTask identifier: "unit_test_writer", "coverage_audit", or "regression_audit"
task_descriptionstringDetailed instructions including expected function behaviors and testing goals
code_under_teststringPython source code containing subtle bugs that tests should catch
previous_test_resultsstringPytest stdout from the last submission (empty on first step)
bugs_found_so_farintegerCount of distinct bugs caught by tests in this episode
coverage_pctfloatLine coverage percentage (0.0–100.0) achieved by submitted tests
step_numberintegerCurrent step in the episode (0-indexed)
hintstringOptional hint provided when reward < 0.2 and step > 3

Action Space

Agents interact with the environment through structured actions:

FieldTypeValid ValuesDescription
action_typestring"submit_tests", "view_coverage", "done"Action to perform
test_codestringValid Python/pytest codeThe pytest test file to execute
notesstringAny string (optional)Agent's reasoning or notes (not graded)

Action Types

  • `submit_tests`: Execute the provided pytest code against the buggy code and receive a reward
  • `view_coverage`: Check current coverage without consuming a grading attempt
  • `done`: End the episode early (useful when agent is satisfied with results)

Tasks

Task 1: Unit Test Writer

PropertyValue
IDunit_test_writer
DifficultyEasy
Max Steps20
Reward Range0.0 – 1.0

Objective: Write pytest tests for a calculate_order_total(items, discount, tax_rate) function that computes order totals with discounts and tax.

Challenge: The function contains exactly 3 bugs selected from a pool of 6 possible bugs:

  • Wrong arithmetic operators (+ instead of *)
  • Incorrect divisors (/10 instead of /100)
  • Inverted operations (add instead of subtract)
  • Tax calculated on wrong amount
  • Rounding precision errors

Grader Logic:

  1. 1.Compile test code (syntax check)
  2. 2.For each of the 3 bugs, run tests against a version with only that bug
  3. 3.A bug is "caught" if at least one test fails because of it

Expected Scores: 0.4–1.0 for competent agents


Task 2: Coverage Audit

PropertyValue
IDcoverage_audit
DifficultyMedium
Max Steps30
Reward Range0.0 – 1.0

Objective: Test an Inventory class with 5 methods (add_item, remove_item, calculate_total, apply_discount, get_summary) while achieving 80%+ line coverage AND identifying which functions contain bugs.

Challenge: 2–3 of the 5 functions have bugs. The agent must:

  • Write tests that cover most code paths
  • Ensure tests fail on buggy functions (identifying them)
  • Avoid false positives (tests failing on correct functions)

Grader Logic:

  1. 1.Run pytest --cov to measure line coverage
  2. 2.Parse which test functions fail to identify "flagged" functions
  3. 3.Compare flagged functions against actual buggy functions

Expected Scores: 0.3–0.85 for competent agents


Task 3: Regression Audit

PropertyValue
IDregression_audit
DifficultyHard
Max Steps50
Reward Range0.0 – 1.0

Objective: Analyze changes between v1 and v2 of an authentication module. Classify each failing test as either a regression (bug introduced) or intentional change (test needs updating).

Challenge:

  • v1 code worked correctly with 8 passing tests
  • v2 contains 2 real regressions and 1 intentional behavior change
  • Agent must classify failures, write new tests for regressions, and update tests for intentional changes

Submission Format:

python
# FAILING_TESTS: test_name1, test_name2
# CLASSIFICATION: test_name1=regression
# CLASSIFICATION: test_name2=intentional_change

def test_regression_new():
    """New test catching the regression."""
    ...

def test_name2_updated():
    """Updated test for intentional change."""
    ...

Grader Logic:

  1. 1.Run original v1 tests against v2 to confirm which fail
  2. 2.Parse agent's classification comments
  3. 3.Run agent's new/updated tests against v2

Expected Scores: 0.2–0.75 for competent agents


Reward Function

AutoTest-Env uses partial credit design to provide dense learning signals:

Unit Test Writer (Easy)

ComponentReward
Valid syntax (code compiles)+0.10
Per bug caught+0.30 (max 3 bugs = +0.90)
Subprocess crash/timeout-0.10

Maximum: 1.0 (all 3 bugs caught)

Coverage Audit (Medium)

ComponentReward
Coverage score(coverage_pct / 80) × 0.5, max 0.5
Per correctly identified buggy function+0.15
Per false positive-0.05

Maximum: 1.0 (80%+ coverage, all bugs identified, no false positives)

Regression Audit (Hard)

ComponentReward
Correctly listing failing tests+0.10
Per correct classification+0.15
New test catches regression+0.20
Updated test for intentional change passes+0.20
Per missed regression-0.20
Per valid test wrongly retired-0.10

Maximum: 1.0 (all failures identified and correctly classified)


Setup Instructions

Prerequisites

  • Python 3.11+
  • Docker (optional, for containerized deployment)

Installation

bash
# Clone the repository
git clone https://github.com/AmulyaSKumar/rl-testing-env
cd autotest-env

# Install dependencies
pip install openenv-core
pip install -e .

Running with Docker (Recommended)

bash
# Build the container
docker build -t autotest-env .

# Run the environment server
docker run -p 7860:7860 autotest-env

Running Locally

bash
# Start the environment server
uvicorn server.app:app --host 0.0.0.0 --port 7860

Running Inference

bash
# Option 1: Using HuggingFace (required for OpenEnv evaluation)
export HF_TOKEN="your-huggingface-token"
export API_BASE_URL="https://router.huggingface.co/v1"
export MODEL_NAME="Qwen/Qwen2.5-72B-Instruct"
python inference.py --seed 42

# Option 2: Using OpenAI API (for local testing)
export OPENAI_API_KEY="your-openai-key"
python inference.py --seed 42

# Run specific task only
python inference.py --seed 42 --task unit_test_writer

# Run all tasks
python inference.py --seed 42

Baseline Scores

Reproducible Baseline (seed=42)

Taskgpt-4o-mini (seed=42)Expected Range
unit_test_writer0.700.40–1.00
coverage_audit0.550.30–0.85
regression_audit0.450.20–0.75
To reproduce exactly: ``bash HF_TOKEN=your_token python inference.py --seed 42 ``

Multi-Seed Baseline (averaged across seeds 42, 123, 456)

TaskMean ScoreStd Dev
unit_test_writer0.68±0.08
coverage_audit0.52±0.11
regression_audit0.43±0.09

Expected Scores by Model Class

TaskRandom AgentGPT-3.5 ClassGPT-4 ClassExpert Human
unit_test_writer0.100.40–0.700.70–1.001.00
coverage_audit0.150.30–0.500.50–0.850.95
regression_audit0.050.15–0.350.35–0.750.90

Tasks are deterministic with the same seed. Multi-seed evaluation recommended for robust comparison.


Environment Variables

VariableRequiredDefaultDescription
HF_TOKENYesHuggingFace token (primary, per OpenEnv spec)
API_BASE_URLYeshttps://router.huggingface.co/v1OpenAI-compatible API endpoint
MODEL_NAMEYesQwen/Qwen2.5-72B-InstructModel identifier for inference
OPENAI_API_KEYNoOpenAI API key (fallback if HF_TOKEN not set)
ENV_URLNohttp://localhost:7860AutoTest-Env server URL

*HFTOKEN is required for OpenEnv evaluation. OPENAIAPI_KEY can be used as fallback for local testing.


API Reference

Endpoints

MethodPathDescription
POST/resetStart new episode with {task_id, seed}
POST/stepExecute action with {action_type, test_code, notes}
GET/stateGet current EpisodeState
GET/healthHealth check
GET/tasksList available task IDs
GET/leaderboardTop 5 scores per task
GET/metricsAggregate statistics across episodes

Python Client

A Python client SDK is available in client.py for programmatic interaction without running inference.py:

python
from client import AutoTestEnvClient
from models import TestAction

client = AutoTestEnvClient("http://localhost:7860")
result = client.reset(task_id="unit_test_writer", seed=42)

Example Usage

python
import requests

# Start a new episode
response = requests.post("http://localhost:7860/reset", json={
    "task_id": "unit_test_writer",
    "seed": 42
})
observation = response.json()["observation"]

# Submit tests
response = requests.post("http://localhost:7860/step", json={
    "action_type": "submit_tests",
    "test_code": """
import pytest
from order_module import calculate_order_total

def test_basic():
    result = calculate_order_total([(10, 2)], 0, 0.0)
    assert result == 20.0
""",
    "notes": "Testing basic multiplication"
})
result = response.json()
print(f"Reward: {result['reward']}, Done: {result['done']}")

Output Format

The inference script produces standardized output:

[START] task=unit_test_writer env=autotest-env model=Qwen/Qwen2.5-72B-Instruct
[STEP] step=1 action=import pytest... reward=0.40 done=false error=null
[STEP] step=2 action=def test_edge()... reward=0.30 done=false error=null
[END] success=true steps=2 score=0.700 rewards=0.40,0.30

[START] task=coverage_audit env=autotest-env model=Qwen/Qwen2.5-72B-Instruct
...

Security

  • All test execution happens in subprocesses with timeouts (10–20 seconds)
  • No use of exec() or eval() on agent-submitted code
  • Docker container runs as non-root user
  • Temporary files are automatically cleaned up

Grader Design & Exploit Resistance

Each grader is designed to be exploit-proof:

TaskExploit Prevention
unit_test_writerTests must pass on correct code before being counted as bug-catchers. Trivially-failing tests (assert False) incur -0.15 penalty.
coverage_auditFunction identification uses exact name matching (test_<function>_), not substring heuristics. Name-gaming is rejected.
regression_auditRegression tests are validated against both v1 and v2 code. Must pass on v1 AND fail on v2 to earn +0.20.

All tasks clamp rewards to [0.0, 1.0] and penalize false positives to discourage gaming strategies.


License

MIT License. See LICENSE for details.