Skip to content

Commit b4e3496

Browse files
committed
feat: refactor image captioning tests and add ESLint configuration
1 parent 7dfaec1 commit b4e3496

File tree

7 files changed

+162
-103
lines changed

7 files changed

+162
-103
lines changed

.eslintrc.json

Lines changed: 0 additions & 16 deletions
This file was deleted.

.github/workflows/build.yml

Lines changed: 20 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Build
1+
name: Build & Release
22

33
on:
44
push:
@@ -7,57 +7,51 @@ on:
77

88
jobs:
99
build:
10-
name: Build ${{ matrix.os }}
10+
name: Build (${{ matrix.os }})
1111
runs-on: ${{ matrix.os }}
1212
strategy:
1313
matrix:
14-
os: [ubuntu-latest, windows-latest, macos-latest]
14+
include:
15+
- os: ubuntu-latest
16+
artifact: linux
17+
- os: windows-latest
18+
artifact: windows
19+
- os: macos-latest
20+
artifact: macos
1521

1622
steps:
17-
- name: Checkout code
18-
uses: actions/checkout@v5
23+
- uses: actions/checkout@v5
1924

20-
- name: Setup Node.js
21-
uses: actions/setup-node@v5
25+
- uses: actions/setup-node@v5
2226
with:
2327
node-version: 20
2428
cache: "npm"
2529

26-
- name: Install dependencies
27-
run: npm ci
30+
- run: npm ci
31+
- run: npm run make
2832

29-
- name: Build application
30-
run: npm run make
31-
32-
- name: Upload artifacts
33-
uses: actions/upload-artifact@v4
33+
- uses: actions/upload-artifact@v4
3434
with:
35-
name: TrainKit-${{ matrix.os }}
36-
path: |
37-
out/make/**/*
35+
name: TrainKit-${{ matrix.artifact }}
36+
path: out/make/**/*
3837
retention-days: 30
3938

4039
release:
41-
name: Create Release
40+
name: Release
4241
needs: build
4342
runs-on: ubuntu-latest
4443
if: startsWith(github.ref, 'refs/tags/v')
4544

4645
steps:
47-
- name: Checkout code
48-
uses: actions/checkout@v5
46+
- uses: actions/checkout@v5
4947

50-
- name: Download all artifacts
51-
uses: actions/download-artifact@v4
48+
- uses: actions/download-artifact@v4
5249
with:
5350
path: artifacts
5451

55-
- name: Create Release
56-
uses: softprops/action-gh-release@v1
52+
- uses: softprops/action-gh-release@v1
5753
with:
5854
files: artifacts/**/*
59-
draft: false
60-
prerelease: false
6155
generate_release_notes: true
6256
env:
6357
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.github/workflows/ci.yml

Lines changed: 18 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2,64 +2,44 @@ name: CI
22

33
on:
44
push:
5+
branches: [master, main]
56
pull_request:
7+
branches: [master, main]
68

79
jobs:
810
frontend:
9-
name: Frontend Tests
10-
runs-on: ${{ matrix.os }}
11-
strategy:
12-
matrix:
13-
os: [ubuntu-latest, windows-latest, macos-latest]
14-
node-version: [18, 20]
11+
name: Frontend
12+
runs-on: ubuntu-latest
1513

1614
steps:
17-
- name: Checkout code
18-
uses: actions/checkout@v5
15+
- uses: actions/checkout@v5
1916

20-
- name: Setup Node.js ${{ matrix.node-version }}
21-
uses: actions/setup-node@v5
17+
- uses: actions/setup-node@v5
2218
with:
23-
node-version: ${{ matrix.node-version }}
19+
node-version: 20
2420
cache: "npm"
2521

26-
- name: Install dependencies
27-
run: npm ci
28-
29-
- name: Run ESLint
30-
run: npm run lint
31-
32-
- name: Build application
33-
run: npm run package
22+
- run: npm ci
23+
- run: npm run lint
24+
- run: npm run package
3425

3526
backend:
36-
name: Backend Tests
37-
runs-on: ${{ matrix.os }}
38-
strategy:
39-
matrix:
40-
os: [ubuntu-latest, windows-latest, macos-latest]
41-
python-version: ["3.10", "3.11", "3.12"]
27+
name: Backend
28+
runs-on: ubuntu-latest
4229

4330
steps:
44-
- name: Checkout code
45-
uses: actions/checkout@v5
31+
- uses: actions/checkout@v5
4632

47-
- name: Install uv
48-
uses: astral-sh/setup-uv@v6
33+
- uses: astral-sh/setup-uv@v6
4934
with:
5035
version: "0.8.19"
5136

52-
- name: Set up Python ${{ matrix.python-version }}
53-
run: uv python install ${{ matrix.python-version }}
54-
55-
- name: Install dependencies
37+
- run: uv python install 3.10
38+
- run: uv sync --group dev
5639
working-directory: ./backend
57-
run: uv sync --group dev
5840

59-
- name: Run Python linting
41+
- run: uv run ruff check .
6042
working-directory: ./backend
61-
run: uv run ruff check .
6243

63-
- name: Run Python tests
44+
- run: uv run pytest
6445
working-directory: ./backend
65-
run: uv run pytest
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Subproject commit ebf414ea497a020da0f82df3913e5b6cb8e9663a

backend/tests/test_image_captioning.py

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,28 @@
11
import pytest
22
from unittest.mock import MagicMock, patch
3-
from service.image_captioning import ImageCaptioning
3+
from pathlib import Path
4+
from service.image_captioning import ImageCaptioningService
45

56
@pytest.fixture
6-
def dummy_captioner():
7+
def dummy_captioner(tmp_path):
78
with patch('service.image_captioning.LlavaForConditionalGeneration.from_pretrained') as mock_model, \
89
patch('service.image_captioning.AutoProcessor.from_pretrained') as mock_processor:
910
mock_model.return_value = MagicMock()
1011
processor_instance = MagicMock()
11-
# Set the processor mock to return our processor_instance
1212
mock_processor.return_value = processor_instance
13-
captioner = ImageCaptioning("dummy-model")
13+
14+
load_path = tmp_path / "load"
15+
save_path = tmp_path / "save"
16+
load_path.mkdir()
17+
save_path.mkdir()
18+
19+
captioner = ImageCaptioningService(
20+
model=Path("dummy-model"),
21+
load_path=load_path,
22+
save_path=save_path,
23+
prompt="Describe this image."
24+
)
25+
1426
# Mock methods used in the pipeline
1527
captioner._model.generate = MagicMock(return_value=[[1, 2, 3, 4]])
1628
processor_instance.apply_chat_template = MagicMock(return_value="prompt")
@@ -19,7 +31,6 @@ def dummy_captioner():
1931
"pixel_values": MagicMock()
2032
}
2133
processor_instance.tokenizer.decode = MagicMock(return_value="A cat on a mat.")
22-
# Make sure the processor mock is used for __call__
2334
captioner._processor = processor_instance
2435
return captioner
2536

@@ -28,7 +39,7 @@ def test_image_inputs(dummy_captioner, tmp_path):
2839
img_path = tmp_path / "test.jpg"
2940
from PIL import Image
3041
Image.new("RGB", (10, 10)).save(img_path)
31-
inputs = dummy_captioner._image_inputs(img_path, "Describe this image.")
42+
inputs = dummy_captioner._image_inputs(img_path)
3243
assert "input_ids" in inputs
3344

3445
def test_generate_caption(dummy_captioner):
@@ -44,7 +55,7 @@ def test_save_caption(dummy_captioner, tmp_path):
4455
img_path = tmp_path / "test.jpg"
4556
img_path.touch()
4657
caption = "A cat on a mat."
47-
output_file = dummy_captioner.save_caption(caption, img_path)
58+
output_file = dummy_captioner.save_caption(caption, img_path, tmp_path)
4859
assert output_file.exists()
4960
with open(output_file, "r", encoding="utf-8") as f:
5061
assert f.read() == caption

backend/tests/test_image_upscale.py

Lines changed: 55 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import shutil
44
from pathlib import Path
55
from PIL import Image
6-
from unittest.mock import Mock, patch
6+
from unittest.mock import Mock, patch, MagicMock
77
from service.image_upscaling import ImageUpscaleService
88
import torch
99

@@ -24,36 +24,75 @@ def temp_dirs():
2424

2525

2626
@pytest.fixture
27-
def image_service():
27+
def image_service(temp_dirs):
28+
input_dir, output_dir = temp_dirs
29+
2830
with patch('service.image_upscaling.ModelLoader') as mock_loader:
29-
# Create a mock model that returns the same tensor (no actual upscaling)
31+
# Create a mock model descriptor
3032
mock_model = Mock()
3133
mock_model.to.return_value = mock_model
3234
mock_model.eval.return_value = mock_model
33-
mock_model.device = "cpu"
34-
mock_model.return_value = torch.rand(1, 3, 100, 100)
35+
mock_model.return_value = torch.rand(1, 3, 100, 100)
36+
37+
mock_descriptor = Mock()
38+
mock_descriptor.model = mock_model
39+
mock_descriptor.scale = 2
40+
mock_descriptor.tiling = True
3541

36-
mock_loader.return_value.load_from_file.return_value = mock_model
42+
mock_loader.return_value.load_from_file.return_value = mock_descriptor
3743

38-
return ImageUpscaleService("dummy_model_path.pth", "cpu")
44+
service = ImageUpscaleService(
45+
device="cpu",
46+
model_path=Path("dummy_model.pth"),
47+
load_path=input_dir,
48+
save_path=output_dir,
49+
format="jpg"
50+
)
51+
return service
3952

4053

4154
def test_upscale_default_format(image_service, temp_dirs):
4255
"""Test upscaling with default JPG format."""
4356
input_dir, output_dir = temp_dirs
4457

45-
image_service.upscale(str(input_dir), str(output_dir))
58+
# Mock the upscale_with_tiler to avoid actual processing
59+
with patch.object(image_service, 'upscale_with_tiler') as mock_upscale:
60+
mock_upscale.return_value = Image.new('RGB', (200, 200), color='blue')
61+
image_service.upscale()
4662

4763
output_files = list(output_dir.glob("*.*"))
4864
assert len(output_files) == 1
4965
assert output_files[0].suffix == ".jpg"
5066

5167

52-
def test_upscale_format_conversion(image_service, temp_dirs):
68+
def test_upscale_format_conversion(temp_dirs):
5369
"""Test upscaling with format conversion."""
5470
input_dir, output_dir = temp_dirs
5571

56-
image_service.upscale(str(input_dir), str(output_dir), "png")
72+
with patch('service.image_upscaling.ModelLoader') as mock_loader:
73+
mock_model = Mock()
74+
mock_model.to.return_value = mock_model
75+
mock_model.eval.return_value = mock_model
76+
mock_model.return_value = torch.rand(1, 3, 100, 100)
77+
78+
mock_descriptor = Mock()
79+
mock_descriptor.model = mock_model
80+
mock_descriptor.scale = 2
81+
mock_descriptor.tiling = True
82+
83+
mock_loader.return_value.load_from_file.return_value = mock_descriptor
84+
85+
service = ImageUpscaleService(
86+
device="cpu",
87+
model_path=Path("dummy_model.pth"),
88+
load_path=input_dir,
89+
save_path=output_dir,
90+
format="png" # Convert to PNG
91+
)
92+
93+
with patch.object(service, 'upscale_with_tiler') as mock_upscale:
94+
mock_upscale.return_value = Image.new('RGB', (200, 200), color='blue')
95+
service.upscale()
5796

5897
output_files = list(output_dir.glob("*.png"))
5998
assert len(output_files) == 1
@@ -62,10 +101,10 @@ def test_upscale_format_conversion(image_service, temp_dirs):
62101
assert img.format == "PNG"
63102

64103

65-
def test_get_supported_formats():
66-
"""Test getting supported output formats."""
67-
formats = ImageUpscaleService.get_supported_output_formats()
104+
def test_supported_formats():
105+
"""Test that supported formats are defined."""
106+
from config.image_formats import SUPPORTED_OUTPUT_FORMATS
68107

69-
assert isinstance(formats, list)
70-
assert len(formats) > 0
71-
assert all("value" in fmt and "label" in fmt for fmt in formats)
108+
assert isinstance(SUPPORTED_OUTPUT_FORMATS, dict)
109+
assert len(SUPPORTED_OUTPUT_FORMATS) > 0
110+
assert "jpg" in SUPPORTED_OUTPUT_FORMATS or "jpeg" in SUPPORTED_OUTPUT_FORMATS

0 commit comments

Comments
 (0)