-
Notifications
You must be signed in to change notification settings - Fork 1
Adds end to end functional tests #45
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
43 commits
Select commit
Hold shift + click to select a range
7e8b43b
feat: openCV resizing
dde2da1
fix: linting and enum for selecting openCV resampling algos
13c490c
chore: remove irrelevant comment
8936a6e
refactor: imagemagick is no longer a dependency for the functional tests
0540544
feat: entirely remove PIL from this project
0452ebd
chore: unify image_generation files into once place
a67d553
chore: remove defunct file
81dd1b1
style: lint
703090f
style: standardise opencv2 imports to be import cv2 as cv
f040807
doc: remove reference to PIL in docs
a6634af
chore: capitalisation
anna-singleton-resolver 6e1e1ca
chore: grammar
anna-singleton-resolver a95a7c5
refactor: split out image_generation file into separate module
d7921ca
feature: add functional end to end tests
6338189
fix: fix ruff linting
d0327e2
fix: fix ruff format check
fae867f
fix: fix type checks
71df79c
feat: add test cases for live model
ec83696
test: Improve e2e functional tests for classify_single
f569486
chore: Add VSCode workspace settings for Athena tests
1167de4
more configuration options for the client (#92)
anna-singleton-resolver 45f00dc
test: add color channel functional test
781dec8
fix: streaming tests hang - add explicit aclose() to terminate gRPC s…
2e5206a
fix: close stream on error to prevent hanging
33c4aea
Fix functional tests: session-scoped auth, skip hanging test, fix col…
47531fc
test: reduce default streaming test image count to 50
5e91045
test: add integrator sample test set with 10 safe images
1918ac9
refactor: move e2e testcases to athena-protobufs for shared use
aed699b
chore: update athena-protobufs with testcases documentation
45e9976
chore: update athena-protobufs with Pexels license correction
ca751cf
chore: update athena-protobufs with safety clarifications
1321f3a
chore: update athena-protobufs with filename fix
b3aea37
fix: remove missing lakeside images and clean up exclusions list
d5f862a
chore: update athena-protobufs with testcases release workflow
6db0fc0
chore: update athena-protobufs with workflow fix
ca23957
Merge branch 'main' into feature/e2e-func-tests
iwillspeak b0ece7c
fix: resolve E501 line-too-long linting errors
fc8f9c1
chore: ruff
294df0e
fix: remove invalid aclose() calls on AsyncIterator
df885b6
refactor: clean up EXCLUDED_FILENAMES constant by removing obsolete c…
ab62517
test: Remove Redundant Assertions.
iwillspeak 4bedcac
refactor: clean up EXCLUDED_FILENAMES constant by removing obsolete c…
823c3db
Apply suggestions from code review
iwillspeak File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| { | ||
| "python.testing.pytestArgs": [ | ||
| "tests" | ||
| ], | ||
| "python.testing.unittestEnabled": false, | ||
| "python.testing.pytestEnabled": true | ||
| } |
Submodule athena-protobufs
updated
117 files
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| from pathlib import Path | ||
|
|
||
| import pytest | ||
|
|
||
| from resolver_athena_client.client.athena_client import AthenaClient | ||
| from resolver_athena_client.client.athena_options import AthenaOptions | ||
| from resolver_athena_client.client.channel import ( | ||
| CredentialHelper, | ||
| create_channel_with_credentials, | ||
| ) | ||
| from resolver_athena_client.client.models import ImageData | ||
| from tests.functional.e2e.testcases.parser import ( | ||
| AthenaTestCase, | ||
| load_test_cases, | ||
| ) | ||
|
|
||
| TEST_CASES = load_test_cases("integrator_sample") | ||
|
|
||
| FP_ERROR_TOLERANCE = 1e-4 | ||
|
|
||
|
|
||
| @pytest.mark.asyncio | ||
| @pytest.mark.functional | ||
| @pytest.mark.parametrize("test_case", TEST_CASES, ids=lambda tc: tc.id) | ||
| async def test_classify_single( | ||
| athena_options: AthenaOptions, | ||
| credential_helper: CredentialHelper, | ||
| test_case: AthenaTestCase, | ||
| ) -> None: | ||
| """Functional test for ClassifySingle endpoint and API methods. | ||
|
|
||
| This test creates a unique test image for each iteration and classifies it. | ||
|
|
||
| """ | ||
|
|
||
| # Create gRPC channel with credentials | ||
| channel = await create_channel_with_credentials( | ||
| athena_options.host, credential_helper | ||
| ) | ||
| with Path.open(Path(test_case.filepath), "rb") as f: | ||
| image_bytes = f.read() | ||
|
|
||
| async with AthenaClient(channel, athena_options) as client: | ||
| image_data = ImageData(image_bytes) | ||
|
|
||
| # Classify with auto-generated correlation ID | ||
| result = await client.classify_single(image_data) | ||
|
|
||
| if result.error.code: | ||
| msg = f"Image Result Error: {result.error.message}" | ||
| pytest.fail(msg) | ||
|
|
||
| actual_output = {c.label: c.weight for c in result.classifications} | ||
| assert set(test_case.expected_output.keys()).issubset( | ||
| set(actual_output.keys()) | ||
| ), ( | ||
| "Expected output to contain labels: ", | ||
| f"{test_case.expected_output.keys() - actual_output.keys()}", | ||
| ) | ||
| actual_output = {k: actual_output[k] for k in test_case.expected_output} | ||
|
|
||
| for label in test_case.expected_output: | ||
| expected = test_case.expected_output[label] | ||
| actual = actual_output[label] | ||
| diff = abs(expected - actual) | ||
| assert diff < FP_ERROR_TOLERANCE, ( | ||
| f"Weight for label '{label}' differs by more than " | ||
| f"{FP_ERROR_TOLERANCE}: expected={expected}, actual={actual}, " | ||
| f"diff={diff}" | ||
| ) | ||
Empty file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| import json | ||
| from pathlib import Path | ||
|
|
||
| # Path to the shared testcases directory in athena-protobufs | ||
| _REPO_ROOT = Path(__file__).parent.parent.parent.parent.parent | ||
| TESTCASES_DIR = _REPO_ROOT / "athena-protobufs" / "testcases" | ||
|
|
||
|
|
||
| class AthenaTestCase: | ||
| def __init__( | ||
| self, | ||
| filepath: str, | ||
| expected_output: list[float], | ||
| classification_labels: list[str], | ||
| ) -> None: | ||
| self.id: str = "/".join( | ||
| Path(filepath).parts[-2:] | ||
| ) # e.g. "ducks/duck1.jpg" | ||
| self.filepath: str = filepath | ||
| self.expected_output: dict[str, float] = dict( | ||
| zip(classification_labels, expected_output, strict=True) | ||
| ) | ||
| self.classification_labels: list[str] = classification_labels | ||
|
|
||
|
|
||
| def load_test_cases(dirname: str = "benign_model") -> list[AthenaTestCase]: | ||
| with Path.open( | ||
| Path(TESTCASES_DIR / dirname / "expected_outputs.json"), | ||
| ) as f: | ||
| test_cases = json.load(f) | ||
| return [ | ||
| AthenaTestCase( | ||
| str(Path(TESTCASES_DIR / dirname / "images" / item[0])), | ||
| item[1], | ||
| test_cases["classification_labels"], | ||
| ) | ||
| for item in test_cases["images"] | ||
| ] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,110 @@ | ||
| """Functional test for classifying images with specific color channels.""" | ||
|
|
||
| import numpy as np | ||
| import pytest | ||
|
|
||
| from resolver_athena_client.client.athena_client import AthenaClient | ||
| from resolver_athena_client.client.athena_options import AthenaOptions | ||
| from resolver_athena_client.client.channel import ( | ||
| CredentialHelper, | ||
| create_channel_with_credentials, | ||
| ) | ||
| from resolver_athena_client.client.consts import EXPECTED_HEIGHT, EXPECTED_WIDTH | ||
| from resolver_athena_client.client.models import ImageData | ||
|
|
||
|
|
||
| def create_color_channel_image( | ||
| channel: str, width: int = EXPECTED_WIDTH, height: int = EXPECTED_HEIGHT | ||
| ) -> bytes: | ||
| """Create a raw BGR image with only one channel set to 255. | ||
|
|
||
| Args: | ||
| channel: Color channel to set - 'red', 'green', or 'blue' | ||
| width: Image width in pixels | ||
| height: Image height in pixels | ||
|
|
||
| Returns: | ||
| Raw BGR uint8 image bytes | ||
|
|
||
| """ | ||
| # Create BGR image (3 channels) | ||
| img = np.zeros((height, width, 3), dtype=np.uint8) | ||
|
|
||
| # Set the specified channel to 255 | ||
| if channel == "red": | ||
| img[:, :, 2] = 255 # Red is channel 2 in BGR | ||
| elif channel == "green": | ||
| img[:, :, 1] = 255 # Green is channel 1 in BGR | ||
| elif channel == "blue": | ||
| img[:, :, 0] = 255 # Blue is channel 0 in BGR | ||
| else: | ||
| msg = f"Invalid channel: {channel}. Must be 'red', 'green', or 'blue'" | ||
| raise ValueError(msg) | ||
|
|
||
| return img.tobytes() | ||
|
|
||
|
|
||
| @pytest.mark.asyncio | ||
| @pytest.mark.functional | ||
| async def test_classify_color_channels( | ||
| athena_options: AthenaOptions, credential_helper: CredentialHelper | ||
| ) -> None: | ||
| """Test classification of three images with distinct color channels. | ||
|
|
||
| Creates and classifies three 448x448x3 images: | ||
| - Red image: R=255, G=0, B=0 | ||
| - Green image: R=0, G=255, B=0 | ||
| - Blue image: R=0, G=0, B=255 | ||
| """ | ||
| # Create gRPC channel with credentials | ||
| channel = await create_channel_with_credentials( | ||
| athena_options.host, credential_helper | ||
| ) | ||
|
|
||
| async with AthenaClient(channel, athena_options) as client: | ||
| # Test red channel image | ||
| red_image_bytes = create_color_channel_image("red") | ||
| red_image_data = ImageData(red_image_bytes) | ||
|
|
||
| red_result = await client.classify_single(red_image_data) | ||
|
|
||
| if red_result.error.code: | ||
| msg = f"Red image classification error: {red_result.error.message}" | ||
| pytest.fail(msg) | ||
|
|
||
| assert len(red_result.classifications) > 0, ( | ||
| "No classifications for red image" | ||
| ) | ||
|
|
||
| # Test green channel image | ||
| green_image_bytes = create_color_channel_image("green") | ||
| green_image_data = ImageData(green_image_bytes) | ||
|
|
||
| green_result = await client.classify_single(green_image_data) | ||
|
|
||
| if green_result.error.code: | ||
| msg = ( | ||
| "Green image classification error: " | ||
| f"{green_result.error.message}" | ||
| ) | ||
| pytest.fail(msg) | ||
|
|
||
| assert len(green_result.classifications) > 0, ( | ||
| "No classifications for green image" | ||
| ) | ||
|
|
||
| # Test blue channel image | ||
| blue_image_bytes = create_color_channel_image("blue") | ||
| blue_image_data = ImageData(blue_image_bytes) | ||
|
|
||
| blue_result = await client.classify_single(blue_image_data) | ||
|
|
||
| if blue_result.error.code: | ||
| msg = ( | ||
| f"Blue image classification error: {blue_result.error.message}" | ||
| ) | ||
| pytest.fail(msg) | ||
|
|
||
| assert len(blue_result.classifications) > 0, ( | ||
| "No classifications for blue image" | ||
| ) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.