diff --git a/analyses-snapshot-testing/Makefile b/analyses-snapshot-testing/Makefile index 13c4e603f3c..e6041e11f76 100644 --- a/analyses-snapshot-testing/Makefile +++ b/analyses-snapshot-testing/Makefile @@ -1,22 +1,28 @@ +ifeq ($(CI), true) + PYTHON=python +else + PYTHON=pyenv exec python +endif + .PHONY: black black: - python -m pipenv run python -m black . + $(PYTHON) -m pipenv run python -m black . .PHONY: black-check black-check: - python -m pipenv run python -m black . --check + $(PYTHON) -m pipenv run python -m black . --check .PHONY: ruff ruff: - python -m pipenv run python -m ruff check . --fix + $(PYTHON) -m pipenv run python -m ruff check . --fix .PHONY: ruff-check ruff-check: - python -m pipenv run python -m ruff check . + $(PYTHON) -m pipenv run python -m ruff check . .PHONY: mypy mypy: - python -m pipenv run python -m mypy automation tests citools + $(PYTHON) -m pipenv run python -m mypy automation tests citools .PHONY: lint lint: black-check ruff-check mypy @@ -32,7 +38,7 @@ format: .PHONY: test-ci test-ci: - python -m pipenv run python -m pytest -m "emulated_alpha" + $(PYTHON) -m pipenv run python -m pytest -m "emulated_alpha" .PHONY: test-protocol-analysis test-protocol-analysis: @@ -40,11 +46,11 @@ test-protocol-analysis: .PHONY: setup setup: install-pipenv - python -m pipenv install + $(PYTHON) -m pipenv install .PHONY: teardown teardown: - python -m pipenv --rm + $(PYTHON) -m pipenv --rm .PHONY: format-readme format-readme: @@ -52,7 +58,7 @@ format-readme: .PHONY: install-pipenv install-pipenv: - python -m pip install -U pipenv + $(PYTHON) -m pip install -U pipenv ANALYSIS_REF ?= edge PROTOCOL_NAMES ?= all @@ -67,14 +73,14 @@ snapshot-test: @echo "ANALYSIS_REF is $(ANALYSIS_REF)" @echo "PROTOCOL_NAMES is $(PROTOCOL_NAMES)" @echo "OVERRIDE_PROTOCOL_NAMES is $(OVERRIDE_PROTOCOL_NAMES)" - python -m pipenv run pytest -k analyses_snapshot_test -vv + $(PYTHON) -m pipenv run pytest -k analyses_snapshot_test -vv .PHONY: snapshot-test-update snapshot-test-update: @echo "ANALYSIS_REF is $(ANALYSIS_REF)" @echo "PROTOCOL_NAMES is $(PROTOCOL_NAMES)" @echo "OVERRIDE_PROTOCOL_NAMES is $(OVERRIDE_PROTOCOL_NAMES)" - python -m pipenv run pytest -k analyses_snapshot_test --snapshot-update + $(PYTHON) -m pipenv run pytest -k analyses_snapshot_test --snapshot-update CACHEBUST := $(shell date +%s) @@ -88,7 +94,7 @@ build-opentrons-analysis: .PHONY: generate-protocols generate-protocols: - python -m pipenv run python -m automation.data.protocol_registry + $(PYTHON) -m pipenv run python -m automation.data.protocol_registry OPENTRONS_VERSION ?= edge @@ -112,3 +118,31 @@ run-ot2: @echo "Running opentrons-robot-server:$(OPENTRONS_VERSION)" @echo "If you want to run a different version, run 'make run-ot2 OPENTRONS_VERSION=chore_release-8.0.0'" docker run -p 31950:31950 --env-file ../robot-server/dev.env opentrons-robot-server:$(OPENTRONS_VERSION) + +BUILD_FLAGS := $(if $(NO_CACHE),--no-cache) + +.PHONY: local-build +local-build: + @echo "Building docker image for your local opentrons code" + @echo "The image will be named opentrons-analysis:local" + @echo "For a fresh build, run 'make local-build NO_CACHE=1'" + sed -i.bak '/^.git/s/^/#/' ../.dockerignore || true + docker build $(BUILD_FLAGS) -t opentrons-analysis:local -f citools/Dockerfile.local .. || true + mv ../.dockerignore.bak ../.dockerignore || true + @echo "Build complete" + +.PHONY: local-snapshot-test +local-snapshot-test: + @echo "Running against local opentrons-analysis:local" + @echo "You must run this command like 'make local-snapshot-test ANALYSIS_REF=local'" + @echo "This is because the test suite reads the ANALYSIS_REF environment variable." + @echo "And uses it to determine which image to run against." + @echo "ANALYSIS_REF is $(ANALYSIS_REF)" + @if [ "$(ANALYSIS_REF)" = "local" ]; then \ + echo "ANALYSIS_REF is correctly set to 'local'"; \ + else \ + echo "Warning: ANALYSIS_REF is not set to 'local'"; \ + fi + @echo "PROTOCOL_NAMES is $(PROTOCOL_NAMES)" + @echo "OVERRIDE_PROTOCOL_NAMES is $(OVERRIDE_PROTOCOL_NAMES)" + $(PYTHON) -m pipenv run pytest -k analyses_snapshot_test -vv diff --git a/analyses-snapshot-testing/README.md b/analyses-snapshot-testing/README.md index 51a8e194ca1..cd9a230bc16 100644 --- a/analyses-snapshot-testing/README.md +++ b/analyses-snapshot-testing/README.md @@ -58,3 +58,12 @@ cd analyses-snapshot-testing \ ### Default OPENTRONS_VERSION=edge in the Makefile so you can omit it if you want latest edge `cd analyses-snapshot-testing && make build-rs && make run-rs` + +## Running the Analyses Battery against your local code + +1. `make build-local` +1. `make local-snapshot-test` + +You have the option to specify one or many protocols to run the analyses on. This is also described above [Running the tests against specific protocols](#running-the-tests-against-specific-protocols) + +- `make local-snapshot-test PROTOCOL_NAMES=Flex_S_v2_19_Illumina_DNA_PCR_Free OVERRIDE_PROTOCOL_NAMES=none` diff --git a/analyses-snapshot-testing/citools/Dockerfile.local b/analyses-snapshot-testing/citools/Dockerfile.local new file mode 100644 index 00000000000..34e695e2c1f --- /dev/null +++ b/analyses-snapshot-testing/citools/Dockerfile.local @@ -0,0 +1,25 @@ +# Use Python 3.10, same as the app +FROM python:3.10-slim-bullseye + +# Update packages and install required dependencies +RUN apt-get update && \ + apt-get upgrade -y && \ + apt-get install -y git libsystemd-dev + +# Set the working directory in the container +WORKDIR /opentrons + +# Copy everything from the build context into the /opentrons directory +COPY . /opentrons + +# List the contents of the /opentrons directory to verify the .git folder is copied +RUN ls -al /opentrons/.git + +# Install required packages from the copied code +RUN python -m pip install -U ./shared-data/python +RUN python -m pip install -U ./hardware[flex] +RUN python -m pip install -U ./api +RUN python -m pip install -U pandas==1.4.3 + +# The default command to keep the container running +CMD ["tail", "-f", "/dev/null"] diff --git a/analyses-snapshot-testing/citools/generate_analyses.py b/analyses-snapshot-testing/citools/generate_analyses.py index f67d0394429..7e4aa2281ea 100644 --- a/analyses-snapshot-testing/citools/generate_analyses.py +++ b/analyses-snapshot-testing/citools/generate_analyses.py @@ -24,7 +24,7 @@ HOST_RESULTS: Path = Path(Path(__file__).parent.parent, "analysis_results") ANALYSIS_SUFFIX: str = "analysis.json" ANALYSIS_TIMEOUT_SECONDS: int = 30 -ANALYSIS_CONTAINER_INSTANCES: int = 5 +MAX_ANALYSIS_CONTAINER_INSTANCES: int = 5 console = Console() @@ -240,6 +240,12 @@ def analyze_against_image(tag: str, protocols: List[TargetProtocol], num_contain return protocols +def get_container_instances(protocol_len: int) -> int: + # Scaling linearly with the number of protocols + instances = max(1, min(MAX_ANALYSIS_CONTAINER_INSTANCES, protocol_len // 10)) + return instances + + def generate_analyses_from_test(tag: str, protocols: List[Protocol]) -> None: """Generate analyses from the tests.""" start_time = time.time() @@ -259,6 +265,7 @@ def generate_analyses_from_test(tag: str, protocols: List[Protocol]) -> None: protocol_custom_labware_paths_in_container(test_protocol), ) ) - analyze_against_image(tag, protocols_to_process, ANALYSIS_CONTAINER_INSTANCES) + instance_count = get_container_instances(len(protocols_to_process)) + analyze_against_image(tag, protocols_to_process, instance_count) end_time = time.time() console.print(f"Clock time to generate analyses: {end_time - start_time:.2f} seconds.") diff --git a/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[4389e3ab18][OT2_X_v6_P20S_None_SimpleTransfer].json b/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[4389e3ab18][OT2_X_v6_P20S_None_SimpleTransfer].json index 579a4670bcc..e5fa2d5e078 100644 --- a/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[4389e3ab18][OT2_X_v6_P20S_None_SimpleTransfer].json +++ b/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[4389e3ab18][OT2_X_v6_P20S_None_SimpleTransfer].json @@ -1757,7 +1757,7 @@ "createdAt": "TIMESTAMP", "error": { "createdAt": "TIMESTAMP", - "detail": "Cannot aspirate 21.0 µL when only 20 is available.", + "detail": "Cannot aspirate 21.0 µL when only 20 is available in the tip.", "errorCode": "4000", "errorInfo": { "attempted_aspirate_volume": 21.0, @@ -1800,7 +1800,7 @@ "errors": [ { "createdAt": "TIMESTAMP", - "detail": "Cannot aspirate 21.0 µL when only 20 is available.", + "detail": "Cannot aspirate 21.0 µL when only 20 is available in the tip.", "errorCode": "4000", "errorInfo": { "attempted_aspirate_volume": 21.0,