TEST: add unit and integration tests for GCG attack#1684
Conversation
Add 26 new unit tests covering: - get_filtered_cands: filtering, clamping, padding behavior - target_loss / control_loss: shape, finiteness, loss ordering - sample_control: shape, vocab bounds, single-position changes, non-ASCII filtering - _build_params: ConfigDict construction from kwargs - _apply_target_augmentation: length preservation, modification, seed reproducibility - _create_attack: transfer flag routing (Progressive vs Individual) - Embedding helpers: error handling for unknown model types - PromptManager init: validation of goals/targets - EvaluateAttack init: worker count validation Total GCG test count: 24 -> 50 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Data & config tests (test_data_and_config.py, 12 tests): - YAML loading: valid files, list values, missing file error - Real config validation: all 11 shipped configs parse, have required keys, individual vs transfer configs have correct settings - get_goals_and_targets: seed reproducibility, different seeds differ, separate test data files, n_train_data limiting - run_trainer validation: unsupported model names, missing HF token Lifecycle tests (test_lifecycle.py, 7 tests): - GPU memory: nvidia-smi parsing (single/multi GPU), MLflow logging, failure handling - generate_suffix lifecycle: MLflow started before training, workers stopped after training, BUG CHARACTERIZATION: workers NOT stopped on failure (leak) Total GCG test count: 24 -> 69 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add 10 integration tests that exercise the GCG attack pipeline with a real GPT-2 model on CPU, validating end-to-end correctness: - token_gradients: gradient shape matches (n_control, vocab_size), values are finite and non-zero - GCGAttackPrompt: initializes with valid non-overlapping slices, grad() returns correct shape, test_loss() returns finite positive float - GCGPromptManager.sample_control: sampled candidates are decodable, correct batch size - Embedding helpers: layer/matrix/embeddings work with GPT2LMHeadModel, get_nonascii_toks returns non-empty tensor Uses llama-2 conversation template (has explicit handling in _update_ids). Marked @run_only_if_all_tests (requires RUN_ALL_TESTS=true + torch/transformers). Runs in ~18s on CPU. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
These tests only need optional Python packages (torch, transformers, fastchat), not external services or credentials. The importorskip at the top already handles skipping when deps are not installed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Move class references to module level to fix N806 (variable naming) - Add noqa: E402 for imports after importorskip guards - Fix ruff format issues - Remove outdated RUN_ALL_TESTS reference in docstring Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
rlundeen2
left a comment
There was a problem hiding this comment.
Review of PR #1684: GCG Attack Test Coverage
Overall: Excellent PR — well-structured, comprehensive test coverage with clear documentation. The bug characterization test for the worker leak is particularly good engineering practice. One actionable issue below.
🐛 Missing @pytest.mark.run_only_if_all_tests on integration tests
File: tests/integration/auxiliary_attacks/test_gcg_integration.py
Per test.instructions.md, integration tests must be marked with @pytest.mark.run_only_if_all_tests (skipped unless RUN_ALL_TESTS=true).
The pytest.importorskip at module level handles missing deps, but serves a different purpose — run_only_if_all_tests ensures these slower tests (~18s + potential model download) only run intentionally, not in every CI pass where torch happens to be installed.
The PR description says "Marked @run_only_if_all_tests" but the code doesn't actually have it. Easiest fix is a module-level marker:
python pytestmark = pytest.mark.run_only_if_all_tests
Minor observations (non-blocking)
-
The
object.__new__(GCGPromptManager)pattern in the integration test (to callsample_controlwithout full init) is pragmatic but slightly undermines the "integration" nature of the test. IfGCGPromptManager.__init__ever adds required state, this will silently break. Consider a brief inline comment noting why full construction is avoided (e.g., it requires multiple coordinated workers). -
The
CONFIGS_DIRrelative path intest_data_and_config.pyuses 4 levels of..— fragile if the test file ever moves. A helper likepathlib.Path(__file__).resolve().parents[4] / "pyrit" / ...would be slightly more readable but not critical.
Summary
Adds 55 new tests for the GCG (Greedy Coordinate Gradient) attack code, bringing total GCG test coverage from 24 to 79 tests. This covers Phase 1a (unit tests) and Phase 1b (integration tests) of a larger GCG refactoring effort.
New unit test files
test_gcg_core.pytest_data_and_config.pytest_lifecycle.pyNew integration test file
test_gcg_integration.pytoken_gradients(gradient shape, finiteness),GCGAttackPrompt(init slices, grad, loss),sample_control(decodable candidates), embedding helpers with real model,get_nonascii_toksIntegration tests use GPT-2 (~124M params) with the llama-2 conversation template. Marked
@run_only_if_all_tests. Runs in ~18s on CPU.Notable finding
generate_suffix()does not use try/finally for worker cleanup. Ifattack.run()raises an exception, workers are never stopped — this is a resource leak. Documented with a characterization test (test_workers_not_stopped_on_training_failure) that will be fixed in a later phase.Context
This is the first PR in a planned GCG refactor effort (tracked in #960) that will:
apply_chat_templateFEAT replace fastchat in GCG #965 building on PR FEAT: Drop fastchat from GCG (#965) #1049, model adapter registry FEAT support more models for evaluating suffixes in GCG #990, fix OOM BUG GCG runs out of memory even on huge machines #961)No source code changes
This PR only adds test files — no modifications to existing source code.