Chapter 12Lesson 05300–420 min

Checkpoint Lab — Resource Files, Variable Files, Library Imports, and Project Layout

Refactor and prove a portable multi-file Robot Framework project, run it from two working directories under one command contract, diagnose an import-path failure and keyword collision, and produce an evidence-backed dependency diagram.

Checkpoint labPortable layoutImport diagnosticsCollision repairEvidence packet

Learning objectives

  • Build the complete disposable project from an empty directory with explicit source/output boundaries.
  • Predict how CURDIR, EXECDIR, module search path, and result locations change across two launch directories.
  • Inject and diagnose one import-path failure without losing the first-failure artifacts.
  • Inject and diagnose one duplicate-keyword collision and repair the ownership boundary.
  • Produce a dependency diagram, verification checklist, and cleanup plan suitable for CI handoff.

Current compatibility baseline. Verified 2026-08-31: Robot Framework 7.4.2 is the current stable release and requires Python 3.8+; 7.5b1 is a pre-release and is not required in this chapter. In 7.4.2, .resource is the recommended resource-file extension. Resource and Settings-level variable-file paths are resolved relative to the importing data file first and then via Python's module search path. ${CURDIR} is the absolute directory of the current data file; ${EXECDIR} is the absolute directory where execution started. JSON variable files require no optional package; YAML variable files require PyYAML.

1. Checkpoint contract and safety boundary

Create a fresh rf12-checkpoint directory using only synthetic strings/files. The mandatory path uses Robot Framework core, BuiltIn/OperatingSystem/String, one tiny local Python library, one Python variable file, and one JSON variable file. No network, browser, API, database, SSH, Remote library, container, CI account, production credential, or paid service is required.

Failure injection is source-only and reversible. Do not modify global PYTHONPATH, system Python, production files, or shared CI configuration. Preserve each failing result directory before repair.

2. Preflight and exact assumptions

python --version
python -m robot --version
python -c "import os, sys; print('cwd=', os.getcwd()); print('python=', sys.executable)"

Required baseline: Robot Framework 7.4.2 on Python 3.8+. Record the actual Python version. JSON uses the Python standard library and needs no optional package. Pabot, Browser/Selenium, external API/database/SSH libraries, CI providers, and containers are not used.

3. Build the project tree

rf12-checkpoint/
├── tests/
│   └── labels.robot
├── resources/
│   ├── labels.resource
│   └── collision.resource      # added only during failure injection
├── variables/
│   ├── demo.py
│   └── demo.json
├── libraries/
│   ├── __init__.py
│   └── text_tools.py
├── data/
│   └── sample-label.txt
└── results/
    ├── dryrun/
    ├── root-run/
    ├── other-cwd-run/
    ├── broken-import/
    ├── repaired-import/
    ├── collision/
    └── repaired-collision/

4. Implement the clean source files

# variables/demo.py
PREFIX = "build-"
EXPECTED = "BUILD-ALPHA"
{
  "fixture_name": "sample-label.txt",
  "expected_file_value": "build-beta"
}
# libraries/text_tools.py

def normalize_label(value: str) -> str:
    return value.strip().upper()
*** Settings ***
Library      libraries.text_tools
Library      OperatingSystem
Library      String
Variables    ../variables/demo.py
Variables    ../variables/demo.json

*** Keywords ***
Label Should Normalize
    [Arguments]    ${raw}    ${expected}
    ${actual}=    Normalize Label    ${raw}
    Should Be Equal    ${actual}    ${expected}

Fixture Label Should Match
    ${path}=    Set Variable    ${CURDIR}/../data/${fixture_name}
    ${raw}=    Get File    ${path}
    ${raw}=    Strip String    ${raw}
    Should Be Equal    ${raw}    ${expected_file_value}
*** Settings ***
Resource    ../resources/labels.resource

*** Test Cases ***
Configured normalization
    Label Should Normalize    ${PREFIX}alpha    ${EXPECTED}

Source-relative fixture
    Fixture Label Should Match

Path evidence
    Log    suite CURDIR=${CURDIR}
    Log    EXECDIR=${EXECDIR}
build-beta

5. Write predictions before running

Prediction Expected result Independent verification
Moving cwd changes EXECDIR Different absolute launch directories Compare log.html Path evidence calls
Moving cwd does not break Resource/Variables imports Both runs PASS Report + import logs
Fixture path stays source-owned Reads the same data/sample-label.txt Resource CURDIR-derived path in log/source
Project-root pythonpath finds library libraries.text_tools imports in both runs Libdoc + Python __file__
Results are isolated Each run writes only to its named result directory Directory tree / git status

6. Establish one documented command contract

Define RF12_ROOT as the absolute checkpoint root. From any directory, the execution contract is:

python -m robot   --pythonpath "$RF12_ROOT"   --outputdir "$RF12_ROOT/results/RUN_NAME"   "$RF12_ROOT/tests"

PowerShell uses the same semantic contract with $env:RF12_ROOT. Only RUN_NAME changes so evidence is not overwritten. The project does not depend on the current directory for imports or fixture lookup.

7. Dry-run and Libdoc verification

python -m robot --pythonpath "$RF12_ROOT" --dryrun   --outputdir "$RF12_ROOT/results/dryrun" "$RF12_ROOT/tests"

python -m robot.libdoc --pythonpath "$RF12_ROOT"   libraries.text_tools "$RF12_ROOT/results/text-tools.html"

python -m robot.libdoc --pythonpath "$RF12_ROOT"   "$RF12_ROOT/resources/labels.resource"   "$RF12_ROOT/results/labels-resource.html"

Expected: dry-run PASS and two Libdoc files generated. Record the exact commands and file paths in the evidence packet.

8. Execute from two working directories

Run A: change directory to $RF12_ROOT and use RUN_NAME=root-run. Run B: change directory to a safe unrelated directory such as its parent or your temporary directory and use RUN_NAME=other-cwd-run.

Both runs should PASS. The EXECDIR log differs. The suite/resource import graph, Python library identity, fixture value, and test results remain the same.

9. Failure injection 1: break one Resource path

Change only this line in tests/labels.robot:

# Broken on purpose:
Resource    labels.resource

Run with RUN_NAME=broken-import. Preserve the console output and all generated artifacts. Use the diagnostic sequence: importer is tests/labels.robot; labels.resource is not beside that file; module-search fallback does not provide such a resource. Repair to ../resources/labels.resource, then run RUN_NAME=repaired-import.

10. Failure injection 2: create a duplicate keyword owner

# resources/collision.resource
*** Keywords ***
Label Should Normalize
    [Arguments]    ${raw}    ${expected}
    Fail    Deliberate duplicate owner should not be selected implicitly.

Import collision.resource beside labels.resource in the suite and keep the unqualified call. Run with RUN_NAME=collision and preserve the ambiguity/failure evidence. Then:

  1. Qualify the intended call as labels.Label Should Normalize to prove resolution.
  2. Remove the duplicate resource import and delete the synthetic collision file.
  3. Run RUN_NAME=repaired-collision and confirm PASS.

The final architecture has one domain owner; qualification is diagnostic evidence, not permanent permission for duplicated abstractions.

11. Produce the final dependency diagram

Checkpoint project dependency direction
flowchart TD
T[tests/labels.robot] --> R[resources/labels.resource]
R --> VP[variables/demo.py]
R --> VJ[variables/demo.json]
R --> L[libraries.text_tools]
R --> F[data/sample-label.txt]
P[--pythonpath project root] --> L
T --> O[results/RUN_NAME]
O -. evidence only .-> T

Annotate the diagram in your evidence notes: Resource/Variables source paths are owned by the importing files; the Python library is found through the runner-supplied project root; the data file is addressed from the resource's ${CURDIR}; result files flow outward and are never imported back into source.

12. Required evidence packet

  • Python and Robot Framework version output.
  • Project tree before and after the refactor.
  • The exact RF12_ROOT-based command contract.
  • Dry-run output and both Libdoc files.
  • Root-run and other-cwd-run output.xml/log.html/report.html.
  • Logged CURDIR/EXECDIR observations from both launch locations.
  • Broken-import console/result evidence and repaired-import evidence.
  • Keyword-collision evidence, qualified diagnostic call, and final repaired-collision evidence.
  • Python library __file__ provenance if module identity was in doubt.
  • Final dependency diagram and a one-paragraph path/import convention.

13. Verification checklist

  • Exactly one project-root search-path contract is documented.
  • No absolute developer-specific path is committed in Robot source.
  • No brittle cwd-relative import is required.
  • Both working-directory runs PASS with the same test behavior.
  • The two injected failures were preserved in separate result directories before repair.
  • No duplicate keyword owner remains in the final source.
  • No Python variable file performs network calls, random generation, secret logging, or filesystem mutation at import time.
  • JSON is the only non-Python variable format required; YAML/PyYAML remains optional.
  • Generated results stay under results/.
  • No real secret, PII, production URL, or destructive target appears in source or evidence.

14. Cleanup / rollback

The lab creates only the disposable rf12-checkpoint tree. Archive the evidence packet first if required. Then remove only that verified lab directory. Do not use a recursive delete command with an empty/unverified variable. If this project is in Git, confirm git status --short before deleting anything so you do not remove unrelated work.

15. What Chapter 12 adds to a production operating model

You now have an explicit import/dependency contract: source-relative files are owned by their importer; local Python modules are resolved through a documented root or installed package; variable imports stay deterministic and low-side-effect; keyword collisions are never chosen accidentally; outputs have a separate evidence root. This is the portability foundation needed before Chapter 13 adds assertion/error/recovery policies across these reusable layers.

16. Knowledge check

Why do both cwd runs pass even though EXECDIR changes?

What does the broken Resource path prove?

Why preserve the duplicate-keyword failure before qualifying the call?

When should libraries.text_tools stop relying on --pythonpath?

What does Chapter 13 add next?

17. Checkpoint complete

You can now design and prove a portable multi-file Robot Framework project. Imports have explicit owners, path semantics survive different launch directories, variable formats/dependencies are documented, Python module identity is observable, collisions and broken paths are diagnosed without guesswork, and result artifacts remain separate from source. Chapter 13 builds trustworthy failure semantics on top of this layout.

Next lesson

Assertions, Error Handling, Expected Failures, and Recovery Patterns: Core Concepts and Mental Model

Continue with Assertions, Error Handling, Expected Failures, and Recovery Patterns: Core Concepts and Mental Model. It builds directly on the state, evidence, and operating assumptions established here, so carry those constraints forward rather than treating the next page as an isolated topic.

Further reading

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.