From 67e39b16614e3ddebeed0625fb9d04af16313ccc Mon Sep 17 00:00:00 2001 From: Ryan Lewis Date: Mon, 8 Jan 2024 14:00:49 -0600 Subject: [PATCH 1/2] Switch to docker/make; add pytests --- .github/workflows/docker-publish.yml | 1 - .gitignore | 3 + .pre-commit-config.yaml | 26 ++++++ Dockerfile | 28 +++++-- Makefile | 25 ++++++ compose.yaml | 62 ++++++++++++-- make/boilerplate.mk | 28 +++++++ make/config.mk | 67 +++++++++++++++ make/docker.mk | 42 ++++++++++ pyproject.toml | 109 ++++++++++++++++++++++++- scripts/build.sh | 11 --- scripts/logs.sh | 6 -- scripts/publish.sh | 7 -- scripts/restart.sh | 6 -- scripts/script_helper.sh | 19 ----- scripts/start.sh | 6 -- scripts/stop.sh | 6 -- src/webcam_rest_node.py | 16 ++-- tests/test_module.py | 62 ++++++++++++++ tests/workcell_defs/test_workcell.yaml | 20 +++++ tests/workflow_defs/test_workflow.yaml | 15 ++++ 21 files changed, 484 insertions(+), 81 deletions(-) create mode 100644 .pre-commit-config.yaml create mode 100644 Makefile create mode 100644 make/boilerplate.mk create mode 100644 make/config.mk create mode 100644 make/docker.mk delete mode 100755 scripts/build.sh delete mode 100755 scripts/logs.sh delete mode 100755 scripts/publish.sh delete mode 100755 scripts/restart.sh delete mode 100755 scripts/script_helper.sh delete mode 100755 scripts/start.sh delete mode 100755 scripts/stop.sh create mode 100644 tests/test_module.py create mode 100644 tests/workcell_defs/test_workcell.yaml create mode 100644 tests/workflow_defs/test_workflow.yaml diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 4738982..495bd43 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -62,4 +62,3 @@ jobs: labels: ${{ steps.meta.outputs.labels }} #cache-from: type=gha #cache-to: type=gha,mode=max - diff --git a/.gitignore b/.gitignore index 68bc17f..543d4ba 100644 --- a/.gitignore +++ b/.gitignore @@ -158,3 +158,6 @@ cython_debug/ # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ + +# WEI +.wei diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..0e70236 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,26 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: check-yaml + - id: check-json + - id: check-toml + - id: check-ast + - id: check-merge-conflict + - id: check-added-large-files + - id: mixed-line-ending + - id: end-of-file-fixer + - id: trailing-whitespace + - repo: https://github.com/kynan/nbstripout + rev: 0.6.1 + hooks: + - id: nbstripout + - repo: https://github.com/astral-sh/ruff-pre-commit + # Ruff version. + rev: v0.1.10 + hooks: + # Run the linter. + - id: ruff + args: [--fix] + # Run the formatter. + - id: ruff-format diff --git a/Dockerfile b/Dockerfile index b697105..c3f3cb1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,15 +4,31 @@ LABEL org.opencontainers.image.source=https://github.com/AD-SDL/webcam_module LABEL org.opencontainers.image.description="An example module that implements a simple webcam snapshot action" LABEL org.opencontainers.image.licenses=MIT -USER wei -WORKDIR /home/wei +ARG USER_ID=1000 +ARG GROUP_ID=1000 +ARG CONTAINER_USER=app +ARG CONTAINER_GROUP=app +USER ${CONTAINER_USER} +WORKDIR /home/${CONTAINER_USER} + +# Add python packages to path +ENV PATH="$PATH:/home/${CONTAINER_USER}/.local/bin" + +######################################### +# Module specific logic goes below here # +######################################### RUN mkdir -p webcam_module -COPY --chown=wei:wei ./src webcam_module/src -COPY --chown=wei:wei ./README.md webcam_module/README.md -COPY --chown=wei:wei ./pyproject.toml webcam_module/pyproject.toml +COPY --chown=${CONTAINER_USER}:${CONTAINER_GROUP} ./src webcam_module/src +COPY --chown=${CONTAINER_USER}:${CONTAINER_GROUP} ./README.md webcam_module/README.md +COPY --chown=${CONTAINER_USER}:${CONTAINER_GROUP} ./pyproject.toml webcam_module/pyproject.toml +COPY --chown=${CONTAINER_USER}:${CONTAINER_GROUP} ./tests webcam_module/tests -RUN pip install ./webcam_module +RUN --mount=type=cache,target=/home/${CONTAINER_USER}/.cache,uid=${USER_ID},gid=${GROUP_ID} \ + pip install -e ./webcam_module CMD ["python", "webcam_module/src/webcam_rest_node.py"] + +######################################### +USER root diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0dd4077 --- /dev/null +++ b/Makefile @@ -0,0 +1,25 @@ +################################################################################ +# AD-SDL WEI Template Makefile +################################################################################ +MAKEFILE := $(lastword $(MAKEFILE_LIST)) +MAKEFILE_DIR := $(dir $(MAKEFILE)) +INCLUDE_DIR := $(MAKEFILE_DIR)/make + +include $(INCLUDE_DIR)/boilerplate.mk # Boilerplate, can probably leave as-is +include $(INCLUDE_DIR)/config.mk # Project-specific configuration +include $(INCLUDE_DIR)/docker.mk # Docker-related rules + +################################################################################ +# Rules: Add anything you want to be able to run with `make ` below + +checks: # Runs all the pre-commit checks + @pre-commit install + @pre-commit run --all-files || { echo "Checking fixes\n" ; pre-commit run --all-files; } + +test: init # Runs all the tests + docker compose -f $(COMPOSE_FILE) exec -u app $(APP_NAME) pytest ${MODULE_NAME} $(args) + +################################################################################ + +# Determine which rules don't correspond to actual files (add rules to NOT_PHONY to exclude) +.PHONY: $(filter-out $(NOT_PHONY), $(RULES)) diff --git a/compose.yaml b/compose.yaml index ab869d2..e18d7df 100644 --- a/compose.yaml +++ b/compose.yaml @@ -1,14 +1,64 @@ -version: "3.0" name: webcam_module services: - webcam_node: - image: ghcr.io/ad-sdl/webcam_module + ########### + # Modules # + ########### + webcam_module: + container_name: webcam_module + image: ${IMAGE} build: context: . dockerfile: Dockerfile - command: python /webcam_module/src/webcam_rest_node.py + command: python webcam_module/src/webcam_rest_node.py --port 2000 + env_file: .env privileged: true volumes: - - /dev/video0:/dev/video0 + - ${DEVICE}:/dev/video0 + - ${WEI_DATA_DIR}:/home/app/.wei + - ./src:/home/app/webcam_module/src + - ./tests:/home/app/webcam_module/tests ports: - - 2001:2000 + - 2000:2000 + + ##################### + # WEI Core Services # + ##################### + wei_server: + image: ghcr.io/ad-sdl/wei + container_name: wei_server + ports: + - 8000:8000 + volumes: + - ${WORKCELLS_DIR}:/workcell_defs + - ${WEI_DATA_DIR}:/home/app/.wei + - diaspora_config:/home/app/.diaspora + env_file: .env + command: python3 -m wei.server --workcell /workcell_defs/${WORKCELL_FILENAME} --use_diaspora ${USE_DIASPORA} + depends_on: + - wei_redis + wei_engine: + image: ghcr.io/ad-sdl/wei + container_name: wei_engine + volumes: + - ${WORKCELLS_DIR}:/workcell_defs + - ${WEI_DATA_DIR}:/home/app/.wei + env_file: .env + command: python3 -m wei.engine --workcell /workcell_defs/${WORKCELL_FILENAME} --use_diaspora ${USE_DIASPORA} + depends_on: + - wei_redis + - wei_server + wei_redis: + image: redis + container_name: wei_redis + ports: + - 6379:6379 + volumes: + - ${REDIS_DIR}:/data + command: redis-server --save 60 1 --loglevel warning + +################ +# Data Storage # +################ +volumes: + diaspora_config: + driver: local diff --git a/make/boilerplate.mk b/make/boilerplate.mk new file mode 100644 index 0000000..2a6a77c --- /dev/null +++ b/make/boilerplate.mk @@ -0,0 +1,28 @@ +###################################### +# Boilerplate Makefile Configuration # +# You can probably leave as is # +###################################### + +# List all rules in the project +RULES := $(foreach makefile,$(MAKEFILE_LIST),\ + $(shell grep -E '^[a-zA-Z0-9_-]+:.*' $(makefile) | sed -E 's/^([a-zA-Z0-9_-]+):.*$$/\1/' | sort -u)) + +# Variables that we don't save to the .env file +ENV_FILTER := $(.VARIABLES) +ENV_FILTER += TEMP .SHELLSTATUS PHONY_RULES + +##################### +# Boilerplate Rules # +##################### + +help: # Show help for each target + @echo "" + @echo "Usage: make , where is one of:" + @echo "" + @for file in $(MAKEFILE_LIST); \ + do grep -E '^[a-zA-Z0-9 -_]+:.*#' $$file | sort | \ + while read -r l; \ + do printf "\033[1;32m$$(echo $$l | cut -f 1 -d':')\033[00m:$$(echo $$l | cut -f 2- -d'#')\n"; \ + done; \ + done \ + | sort -u diff --git a/make/config.mk b/make/config.mk new file mode 100644 index 0000000..9a63805 --- /dev/null +++ b/make/config.mk @@ -0,0 +1,67 @@ +##################### +# WEI Configuration # +##################### + +# Adjust these parameters to meet your needs +# You can override these at run time by running `make ="..."` + +# Project Configuration +MODULE_NAME := webcam_module +PROJECT_DIR := $(abspath $(MAKEFILE_DIR)) +WORKCELLS_DIR := $(PROJECT_DIR)/tests/workcell_defs +WORKCELL_FILENAME := test_workcell.yaml +DEVICE := /dev/video0 + +# Python Configuration +PYPROJECT_TOML := $(PROJECT_DIR)/pyproject.toml +PROJECT_VERSION := $(shell grep -oP '(?<=version = ")[^"]+' $(PYPROJECT_TOML) | head -n 1) + +# Docker Configuration +COMPOSE_FILE := $(PROJECT_DIR)/compose.yaml +DOCKERFILE := $(PROJECT_DIR)/Dockerfile +# Make sure ENV_FILE is in .gitignore or equivalent +ENV_FILE := $(PROJECT_DIR)/.env +REGISTRY := ghcr.io +ORGANIZATION := ad-sdl +IMAGE_NAME := $(MODULE_NAME) +IMAGE := $(REGISTRY)/$(ORGANIZATION)/$(IMAGE_NAME) + +# APP_NAME needs to match the name of the module's service in the compose file +APP_NAME := $(MODULE_NAME) +# This is where the data from the workcell and your application will be stored +# If these directories don't exist, they will be created +WEI_DATA_DIR := $(PROJECT_DIR)/.wei +REDIS_DIR := $(WEI_DATA_DIR)/redis +# Whether or not to send events to Diaspora (set to true to turn on) +USE_DIASPORA := false +# This is the default target to run when you run `make` with no arguments +.DEFAULT_GOAL := help + +######################## +# Config-related Rules # +######################## + +init: .env $(WEI_DATA_DIR) $(REDIS_DIR) # Do the initial configuration of the project + +# Generate our .env whenever we change our config +# If you depend on files besides the makefiles to generate config, +# add them as dependencies to the .env rule +NOT_PHONY += .env +.env: $(MAKEFILE_LIST) + @echo Generating .env... + @echo "# THIS FILE IS AUTOGENERATED, CHANGE THE VALUES IN THE MAKEFILE" > $(ENV_FILE) + @echo "USER_ID=$(shell id -u)" >> $(ENV_FILE) + @echo "GROUP_ID=$(shell id -g)" >> $(ENV_FILE) + @echo "MHF_HOST_UID=$(shell id -u)" >> $(ENV_FILE) + @echo "MHF_HOST_GID=$(shell id -g)" >> $(ENV_FILE) +# The following adds every variable in the Makefiles to the .env file, +# except for everything in ENV_FILTER and ENV_FILTER itself + @$(foreach v,\ + $(filter-out $(ENV_FILTER) ENV_FILTER,$(.VARIABLES)),\ + echo "$(v)=$($(v))" >> $(ENV_FILE);) + +$(WEI_DATA_DIR): + mkdir -p $(WEI_DATA_DIR) + +$(REDIS_DIR): + mkdir -p $(REDIS_DIR) diff --git a/make/docker.mk b/make/docker.mk new file mode 100644 index 0000000..7b4d78d --- /dev/null +++ b/make/docker.mk @@ -0,0 +1,42 @@ +############################################ +# Docker- and Docker Compose-related rules # +############################################ + +exec: init # Opens a shell in the APP_NAME container + docker compose -f $(COMPOSE_FILE) exec -u app $(APP_NAME) /bin/bash $(args) + +build: init # Builds the docker image for APP_NAME + docker build -f $(DOCKERFILE) \ + -t ${IMAGE}:${PROJECT_VERSION} \ + -t ${IMAGE}:latest \ + -t ${IMAGE}:dev \ + $(PROJECT_DIR) + docker compose -f $(COMPOSE_FILE) build $(args) + +start: init # Starts all the docker containers and detaches, allowing you to run other commands +start: $(if $(findstring $(USE_DIASPORA),true), register_diaspora) + docker compose -f $(COMPOSE_FILE) up -d $(args) + +up: init # Starts all the docker containers and attaches, allowing you to see the logs + docker compose -f $(COMPOSE_FILE) up $(args) + +ps: init # Shows the status of all the docker containers + docker compose -f $(COMPOSE_FILE) ps $(args) + +restart: init # Restarts all the docker containers + docker compose -f $(COMPOSE_FILE) restart $(args) + +pull: init # Pull the latest versions of the required containers + docker compose -f ${COMPOSE_FILE} pull + +update: init pull build stop start # Pulls, builds, stops, and then starts all containers + +down: stop # Stops all the docker containers +stop: init # Stops all the docker containers + docker compose -f $(COMPOSE_FILE) down $(args) + +logs: init # Shows the logs for all the docker containers + docker compose -f $(COMPOSE_FILE) logs -f $(args) + +remove: init # Removes all the docker containers, but preserves volumes + docker compose -f $(COMPOSE_FILE) down --rmi all $(args) diff --git a/pyproject.toml b/pyproject.toml index e103a6b..cdd89ae 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "webcam_module" -version = "1.0.0" +version = "1.1.0" description = "A simple module that supports taking snapshots with a webcam or other video device." authors = [{name = "Ryan D. Lewis", email="ryan.lewis@anl.gov"}] dependencies = [ @@ -8,6 +8,7 @@ dependencies = [ "uvicorn>=0.14.0", "opencv-python-headless", "ad_sdl.wei", + "pytest" ] requires-python = ">=3.8.1" readme = "README.md" @@ -15,3 +16,109 @@ license = {text = "MIT"} [project.urls] homepage = "https://github.com/AD-SDL/webcam_module" + +##################### +# Development Tools # +##################### + +[tool.ruff] +# https://docs.astral.sh/ruff/configuration/ + +# Exclude a variety of commonly ignored directories. +exclude = [ + ".bzr", + ".direnv", + ".eggs", + ".git", + ".git-rewrite", + ".hg", + ".mypy_cache", + ".nox", + ".pants.d", + ".pytype", + ".ruff_cache", + ".svn", + ".tox", + ".venv", + "__pypackages__", + "_build", + "buck-out", + "build", + "dist", + "node_modules", + "venv", + "docs", +] + +# Same as Black. +line-length = 88 +indent-width = 4 + +# Assume Python 3.8 +target-version = "py38" + +[tool.ruff.lint] +# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default. +select = [ + # pycodestyle + "E", + # Pyflakes + "F", + # pyupgrade + # "UP", + # flake8-bugbear + "B", + # flake8-simplify + # "SIM", + # isort + "I", + # Warning + "W", + # pydocstyle + "D100", "D101", "D102", "D103", "D104", "D105", "D106", "D107", + # ruff + # "RUF" +] +ignore = [ + "E501" # Line too long +] + +# Allow fix for all enabled rules (when `--fix`) is provided. +fixable = ["ALL"] +unfixable = [] + +# Allow unused variables when underscore-prefixed. +dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" + +[tool.ruff.format] +# Like Black, use double quotes for strings. +quote-style = "double" + +# Like Black, indent with spaces, rather than tabs. +indent-style = "space" + +# Like Black, respect magic trailing commas. +skip-magic-trailing-comma = false + +# Like Black, automatically detect the appropriate line ending. +line-ending = "auto" + +[tool.pytest.ini_options] +# https://docs.pytest.org/en/stable/customize.html +addopts = "-x" +junit_family="xunit1" +filterwarnings = [ + "ignore::DeprecationWarning", + "ignore::pottery.exceptions.InefficientAccessWarning", +] + +[tool.mypy] +# https://mypy.readthedocs.io/en/stable/config_file.html#using-a-pyproject-toml +show_error_codes = true +check_untyped_defs = true +follow_imports = "normal" +strict_optional = true +plugins = ["pydantic.mypy"] +strict = true +disallow_untyped_defs = true +implicit_reexport = true diff --git a/scripts/build.sh b/scripts/build.sh deleted file mode 100755 index dbe4da2..0000000 --- a/scripts/build.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env bash - -source "$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"/script_helper.sh - -cd $project_path - -docker build \ - -t ghcr.io/ad-sdl/${project_name}:${project_version} \ - -t ghcr.io/ad-sdl/${project_name}:dev \ - -t ghcr.io/ad-sdl/${project_name}:latest \ - . diff --git a/scripts/logs.sh b/scripts/logs.sh deleted file mode 100755 index 409bf7e..0000000 --- a/scripts/logs.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash - -source "$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"/script_helper.sh -cd $project_path - -docker compose logs "$@" diff --git a/scripts/publish.sh b/scripts/publish.sh deleted file mode 100755 index 71d2a05..0000000 --- a/scripts/publish.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash - -source "$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"/script_helper.sh - -$scripts_path/build.sh - -docker push ghcr.io/ad-sdl/${project_name}:${project_version} diff --git a/scripts/restart.sh b/scripts/restart.sh deleted file mode 100755 index b7dcb84..0000000 --- a/scripts/restart.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash - -source "$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"/script_helper.sh -cd $project_path - -docker compose restart diff --git a/scripts/script_helper.sh b/scripts/script_helper.sh deleted file mode 100755 index 57ecf68..0000000 --- a/scripts/script_helper.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env bash - -# Invoke this helper from other scripts in the scripts dir of a project with: -# source "$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"/script_helper.sh - -USER_ID=$(id -u) -GROUP_ID=$(id -g) - -scripts_path="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" -cd $scripts_path -project_path=`git rev-parse --show-toplevel` -if [[ -f ${project_path}/pyproject.toml ]]; then - project_name=`grep -oP '(?<=name = ")[^"]+' $project_path/pyproject.toml | head -n 1` - project_version=`grep -oP '(?<=version = ")[^"]+' $project_path/pyproject.toml | head -n 1` -else - project_name=`basename $project_path` - project_version="0.0.0" -fi - diff --git a/scripts/start.sh b/scripts/start.sh deleted file mode 100755 index 194acb4..0000000 --- a/scripts/start.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash - -source "$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"/script_helper.sh -cd $project_path - -docker compose up "$@" diff --git a/scripts/stop.sh b/scripts/stop.sh deleted file mode 100755 index f0d887d..0000000 --- a/scripts/stop.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash - -source "$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"/script_helper.sh -cd $project_path - -docker compose down diff --git a/src/webcam_rest_node.py b/src/webcam_rest_node.py index 77ce3e5..38f7698 100644 --- a/src/webcam_rest_node.py +++ b/src/webcam_rest_node.py @@ -7,9 +7,9 @@ from typing import Union import cv2 +import numpy as np from fastapi import FastAPI from fastapi.responses import JSONResponse - from wei.core.data_classes import ( ModuleStatus, StepFileResponse, @@ -103,13 +103,17 @@ def do_action( try: if action_handle == "take_picture": image_name = json.loads(action_vars)["file_name"] - camera = cv2.VideoCapture(0) - _, frame = camera.read() - cv2.imwrite(image_name, frame) - camera.release() + try: + camera = cv2.VideoCapture(0) + _, frame = camera.read() + cv2.imwrite(image_name, frame) + camera.release() + except Exception: + print("Camera unavailable, returning empty image") + blank_image = np.zeros(shape=[512, 512, 3], dtype=np.uint8) + cv2.imwrite(image_name, blank_image) state = ModuleStatus.IDLE - print("success") return StepFileResponse( action_response=StepStatus.SUCCEEDED, path=image_name, diff --git a/tests/test_module.py b/tests/test_module.py new file mode 100644 index 0000000..f60b297 --- /dev/null +++ b/tests/test_module.py @@ -0,0 +1,62 @@ +"""Tests the basic functionality of the Webcam Module.""" + +import time +import unittest +from pathlib import Path + +import requests +from wei import ExperimentClient +from wei.core.data_classes import WorkcellData, WorkflowStatus + + +class TestWEI_Base(unittest.TestCase): + """Base class for WEI's pytest tests""" + + def __init__(self, *args, **kwargs): + """Basic setup for WEI's pytest tests""" + super().__init__(*args, **kwargs) + self.root_dir = Path(__file__).resolve().parent.parent + self.workcell_file = self.root_dir / Path( + "tests/workcell_defs/test_workcell.yaml" + ) + self.workcell = WorkcellData.from_yaml(self.workcell_file) + self.server_host = self.workcell.config.server_host + self.server_port = self.workcell.config.server_port + self.url = f"http://{self.server_host}:{self.server_port}" + self.redis_host = self.workcell.config.redis_host + + # Check to see that server is up + start_time = time.time() + while True: + try: + if requests.get(self.url + "/wc/state").status_code == 200: + break + except Exception: + pass + time.sleep(1) + if time.time() - start_time > 60: + raise TimeoutError("Server did not start in 60 seconds") + + +class TestWebcamModule(TestWEI_Base): + """Tests the basic functionality of the Sleep Module.""" + + def test_take_picture_action(self): + """Tests that the take_picture action works""" + exp = ExperimentClient(self.server_host, self.server_port, "") + + result = exp.start_run( + Path(self.root_dir) / Path("tests/workflow_defs/test_workflow.yaml"), + simulate=False, + blocking=True, + ) + assert result["status"] == WorkflowStatus.COMPLETED + exp.get_file( + input_filepath=result["hist"]["Take Picture"]["action_msg"], + output_filepath=Path("~/.wei/temp/test_image.jpg").expanduser(), + ) + assert Path("~/.wei/temp/test_image.jpg").expanduser().exists() + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/workcell_defs/test_workcell.yaml b/tests/workcell_defs/test_workcell.yaml new file mode 100644 index 0000000..c896d4a --- /dev/null +++ b/tests/workcell_defs/test_workcell.yaml @@ -0,0 +1,20 @@ +name: Test_Workcell + +#Info about data processing and location of the workcell +config: + workcell_origin_coordinates: [0, 0, 0, 0, 0, 0] + redis_host: "wei_redis" + server_host: "wei_server" + server_port: 8000 + +#List of all components accessible in this workcell +modules: + - name: webcam + model: webcam_module + interface: wei_rest_node + config: + rest_node_address: "http://webcam_module:2000" + rest_node_auth: "" + workcell_coordinates: [0, 0, 0, 0, 0, 0] + +locations: {} diff --git a/tests/workflow_defs/test_workflow.yaml b/tests/workflow_defs/test_workflow.yaml new file mode 100644 index 0000000..4407247 --- /dev/null +++ b/tests/workflow_defs/test_workflow.yaml @@ -0,0 +1,15 @@ +name: Test Webcam Module +metadata: + author: Ryan D. Lewis + info: Tests the functionality of the Webcam Module + version: 0.1 + +modules: + - name: webcam + +flowdef: + - name: Take Picture + module: webcam + action: take_picture + args: + file_name: "snapshot.jpg" From c48a88ff150390dac836912b98644f70f0aa1f8e Mon Sep 17 00:00:00 2001 From: Ryan Lewis Date: Mon, 8 Jan 2024 14:01:42 -0600 Subject: [PATCH 2/2] Add tests github action --- .github/workflows/tests.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .github/workflows/tests.yml diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..e810496 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,22 @@ +name: Pytest Unit Tests + +on: [push] + +jobs: + build_and_test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + name: Checkout code + - name: Set up Python 3.9 + uses: actions/setup-python@v4 + with: + python-version: 3.9 + - name: Build the Project + run: make init build + - name: Start WEI + run: make start + - name: Test with pytest + run: make test + - name: Cleanup + run: make remove