@hybridlabor-api/bdb-hardware-pcb 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +116 -0
- package/config/mcp/antigravity.json +22 -0
- package/config/mcp/claude.json +22 -0
- package/config/mcp/codex.toml +25 -0
- package/docs/adr/ADR-001-KICAD-OPENSCAD-MCP-STRATEGY.md +334 -0
- package/docs/review_documentation.md +121 -0
- package/installer.js +358 -0
- package/mcp_servers/kicad-mcp-server/.env.example +22 -0
- package/mcp_servers/kicad-mcp-server/.github/workflows/ci.yml +36 -0
- package/mcp_servers/kicad-mcp-server/CLAUDE.md +487 -0
- package/mcp_servers/kicad-mcp-server/README.md +316 -0
- package/mcp_servers/kicad-mcp-server/docs/DEVICE_TREE.md +416 -0
- package/mcp_servers/kicad-mcp-server/docs/INSTALLATION.md +332 -0
- package/mcp_servers/kicad-mcp-server/docs/PIN_ANALYSIS.md +332 -0
- package/mcp_servers/kicad-mcp-server/docs/README.md +240 -0
- package/mcp_servers/kicad-mcp-server/docs/TESTING.md +613 -0
- package/mcp_servers/kicad-mcp-server/docs/VALIDATION.md +268 -0
- package/mcp_servers/kicad-mcp-server/pyproject.toml +96 -0
- package/mcp_servers/kicad-mcp-server/requirements-dev.txt +16 -0
- package/mcp_servers/kicad-mcp-server/requirements-test.txt +24 -0
- package/mcp_servers/kicad-mcp-server/requirements.txt +13 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/__init__.py +3 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/__main__.py +17 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/config.py +46 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/models/__init__.py +1 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/models/types.py +87 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/parsers/__init__.py +1 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/parsers/netlist_parser.py +234 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/parsers/pcb_parser.py +375 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/parsers/pcb_parser_kicad.py +327 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/parsers/schematic_parser.py +902 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/server.py +71 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/__init__.py +1 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/arduino/connectivity_test.cpp.j2 +189 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/device_tree/atmega.dts.j2 +77 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/device_tree/esp32.dts.j2 +77 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/device_tree/nrf52.dts.j2 +77 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/device_tree/stm32f4.dts.j2 +89 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/esp_idf/test_suite.c.j2 +340 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/pytest/test_connectivity.py.j2 +147 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/st_hal/hal_test.c.j2 +313 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/tests/pytest_gpio_test.py.j2 +99 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/tests/pytest_i2c_test.py.j2 +117 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/tests/pytest_pinmux_test.py.j2 +43 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/tests/pytest_spi_test.py.j2 +94 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/tests/unity_gpio_test.c.j2 +113 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/tests/unity_i2c_test.c.j2 +101 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/tests/unity_spi_test.c.j2 +94 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/unittest/test_schematic.py.j2 +172 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/__init__.py +36 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/device_tree.py +1187 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/hierarchical_analysis.py +211 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/netlist.py +320 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/parts_registry.py +142 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/pcb.py +955 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/pcb_layout.py +308 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/pin_analysis.py +765 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/project.py +196 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/schematic.py +319 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/schematic_editor.py +674 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/schematic_search.py +158 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/validation.py +866 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/visualization.py +225 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/utils/__init__.py +1 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/utils/file_handlers.py +65 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/utils/kicad_cli.py +103 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/utils/kicad_version.py +103 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/utils/parts_registry.py +197 -0
- package/mcp_servers/kicad-mcp-server/tests/__init__.py +1 -0
- package/mcp_servers/kicad-mcp-server/tests/examples/ESP32S3_TEST.md +219 -0
- package/mcp_servers/kicad-mcp-server/tests/fixtures/README.md +65 -0
- package/mcp_servers/kicad-mcp-server/tests/fixtures/__init__.py +1 -0
- package/mcp_servers/kicad-mcp-server/tests/fixtures/example_pcb.kicad_pcb +177 -0
- package/mcp_servers/kicad-mcp-server/tests/fixtures/example_schematic.kicad_sch +145 -0
- package/mcp_servers/kicad-mcp-server/tests/fixtures/hier/child.kicad_sch +24 -0
- package/mcp_servers/kicad-mcp-server/tests/fixtures/hier/root.kicad_sch +38 -0
- package/mcp_servers/kicad-mcp-server/tests/test_tools/__init__.py +1 -0
- package/mcp_servers/kicad-mcp-server/tests/test_tools/test_hierarchical_labels.py +195 -0
- package/mcp_servers/kicad-mcp-server/tests/test_tools/test_kicad_cli.py +116 -0
- package/mcp_servers/kicad-mcp-server/tests/test_tools/test_netlist_cache_path.py +29 -0
- package/mcp_servers/kicad-mcp-server/tests/test_tools/test_schematic.py +189 -0
- package/mcp_servers/kicad-mcp-server/tests/test_tools/test_schematic_hierarchy.py +69 -0
- package/mcp_servers/kicad-mcp-server/tests/test_tools/test_visualization.py +136 -0
- package/mcp_servers/kicad-mcp-server/uv.lock +2873 -0
- package/mcp_servers/openscad-mcp-server/.dockerignore +9 -0
- package/mcp_servers/openscad-mcp-server/.github/workflows/test.yml +40 -0
- package/mcp_servers/openscad-mcp-server/Dockerfile +29 -0
- package/mcp_servers/openscad-mcp-server/LICENSE +21 -0
- package/mcp_servers/openscad-mcp-server/README.md +154 -0
- package/mcp_servers/openscad-mcp-server/docs/audit.md +56 -0
- package/mcp_servers/openscad-mcp-server/docs/docker.md +66 -0
- package/mcp_servers/openscad-mcp-server/docs/issue-followup.md +19 -0
- package/mcp_servers/openscad-mcp-server/docs/jetson.md +17 -0
- package/mcp_servers/openscad-mcp-server/glama.json +4 -0
- package/mcp_servers/openscad-mcp-server/legacy/README.md +15 -0
- package/mcp_servers/openscad-mcp-server/legacy/README.original.md +294 -0
- package/mcp_servers/openscad-mcp-server/legacy/implementation_plan.md +100 -0
- package/mcp_servers/openscad-mcp-server/legacy/old/download_sam2_checkpoint.py +115 -0
- package/mcp_servers/openscad-mcp-server/legacy/old/src/ai/sam_segmentation.py +209 -0
- package/mcp_servers/openscad-mcp-server/legacy/old/src/models/threestudio_generator.py +231 -0
- package/mcp_servers/openscad-mcp-server/legacy/old/src/workflow/image_to_model_pipeline.py +260 -0
- package/mcp_servers/openscad-mcp-server/legacy/old/test_sam2_segmentation.py +96 -0
- package/mcp_servers/openscad-mcp-server/legacy/requirements.txt +57 -0
- package/mcp_servers/openscad-mcp-server/legacy/rtfmd/README.md +39 -0
- package/mcp_servers/openscad-mcp-server/legacy/rtfmd/decisions/ai-driven-code-generation.md +122 -0
- package/mcp_servers/openscad-mcp-server/legacy/rtfmd/decisions/export-formats.md +76 -0
- package/mcp_servers/openscad-mcp-server/legacy/rtfmd/files/src/ai/ai_service.py.md +51 -0
- package/mcp_servers/openscad-mcp-server/legacy/rtfmd/files/src/main.py.md +63 -0
- package/mcp_servers/openscad-mcp-server/legacy/rtfmd/files/src/models/code_generator.py.md +63 -0
- package/mcp_servers/openscad-mcp-server/legacy/rtfmd/files/src/nlp/parameter_extractor.py.md +63 -0
- package/mcp_servers/openscad-mcp-server/legacy/rtfmd/knowledge/ai/natural-language-processing.md +78 -0
- package/mcp_servers/openscad-mcp-server/legacy/rtfmd/knowledge/nlp/parameter-extraction.md +173 -0
- package/mcp_servers/openscad-mcp-server/legacy/rtfmd/knowledge/openscad/export-formats.md +91 -0
- package/mcp_servers/openscad-mcp-server/legacy/rtfmd/knowledge/openscad/openscad-basics.md +66 -0
- package/mcp_servers/openscad-mcp-server/legacy/rtfmd/knowledge/openscad/primitive-testing.md +79 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/__init__.py +0 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/ai/ai_service.py +257 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/ai/gemini_api.py +161 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/ai/venice_api.py +203 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/config.py +121 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/main.py +1456 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/main.py.new +404 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/main_remote.py +401 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/models/__init__.py +0 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/models/code_generator.py +321 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/models/cuda_mvs.py +209 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/models/scad_templates/basic_shapes.scad +144 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/nlp/__init__.py +0 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/nlp/parameter_extractor.py +388 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/openscad_wrapper/__init__.py +0 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/openscad_wrapper/wrapper.py +418 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/printer_discovery/__init__.py +1 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/printer_discovery/printer_discovery.py +471 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/remote/connection_manager.py +537 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/remote/cuda_mvs_client.py +435 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/remote/cuda_mvs_server.py +787 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/remote/error_handling.py +415 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/testing/__init__.py +0 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/testing/primitive_tester.py +203 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/testing/test_primitives.py +98 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/utils/__init__.py +1 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/utils/cad_exporter.py +241 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/utils/format_validator.py +206 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/utils/stl_exporter.py +140 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/utils/stl_repair.py +91 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/utils/stl_validator.py +123 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/visualization/__init__.py +0 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/visualization/headless_renderer.py +52 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/visualization/renderer.py +177 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/visualization/web_interface.py +639 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/workflow/image_approval.py +148 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/workflow/multi_view_to_model_pipeline.py +338 -0
- package/mcp_servers/openscad-mcp-server/legacy/test_complete_workflow.py +374 -0
- package/mcp_servers/openscad-mcp-server/legacy/test_cuda_mvs.py +191 -0
- package/mcp_servers/openscad-mcp-server/legacy/test_gemini_api.py +168 -0
- package/mcp_servers/openscad-mcp-server/legacy/test_image_approval.py +192 -0
- package/mcp_servers/openscad-mcp-server/legacy/test_image_approval_workflow.py +251 -0
- package/mcp_servers/openscad-mcp-server/legacy/test_image_to_model_pipeline.py +145 -0
- package/mcp_servers/openscad-mcp-server/legacy/test_model_selection.py +41 -0
- package/mcp_servers/openscad-mcp-server/legacy/test_multi_view_pipeline.py +290 -0
- package/mcp_servers/openscad-mcp-server/legacy/test_primitives.sh +13 -0
- package/mcp_servers/openscad-mcp-server/legacy/test_rabbit_direct.py +71 -0
- package/mcp_servers/openscad-mcp-server/legacy/test_remote_cuda_mvs.py +283 -0
- package/mcp_servers/openscad-mcp-server/legacy/test_venice_example.py +69 -0
- package/mcp_servers/openscad-mcp-server/pyproject.toml +33 -0
- package/mcp_servers/openscad-mcp-server/requirements.txt +2 -0
- package/mcp_servers/openscad-mcp-server/scad/simple_cube.scad +2 -0
- package/mcp_servers/openscad-mcp-server/scripts/test_docker.py +220 -0
- package/mcp_servers/openscad-mcp-server/src/openscad_mcp/__init__.py +3 -0
- package/mcp_servers/openscad-mcp-server/src/openscad_mcp/__main__.py +3 -0
- package/mcp_servers/openscad-mcp-server/src/openscad_mcp/engine.py +94 -0
- package/mcp_servers/openscad-mcp-server/src/openscad_mcp/geometry.py +242 -0
- package/mcp_servers/openscad-mcp-server/src/openscad_mcp/server.py +245 -0
- package/mcp_servers/openscad-mcp-server/src/openscad_mcp/service.py +190 -0
- package/mcp_servers/openscad-mcp-server/tests/conftest.py +17 -0
- package/mcp_servers/openscad-mcp-server/tests/test_engine.py +37 -0
- package/mcp_servers/openscad-mcp-server/tests/test_geometry.py +106 -0
- package/mcp_servers/openscad-mcp-server/tests/test_integration.py +172 -0
- package/mcp_servers/openscad-mcp-server/tests/test_transports.py +314 -0
- package/mcp_servers/openscad-mcp-server/uv.lock +1123 -0
- package/package.json +44 -0
- package/scripts/install_mcps.sh +86 -0
- package/scripts/openscad_wrapper.sh +62 -0
- package/scripts/run_kicad_mcp.sh +25 -0
- package/scripts/run_openscad_mcp.sh +41 -0
- package/scripts/test_mcp_connection.py +626 -0
- package/scripts/test_mcp_connection.sh +168 -0
- package/skills/code-first-hardware-design/SKILL.md +260 -0
- package/skills/pcb-constraint-definition/SKILL.md +236 -0
- package/skills/pcb-layout-routing-automation/SKILL.md +153 -0
- package/skills/pcb-validation-dfm-signoff/SKILL.md +194 -0
- package/skills/schematic-datasheet-analysis/SKILL.md +202 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Test script for Google Gemini API integration.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import sys
|
|
7
|
+
import logging
|
|
8
|
+
import unittest
|
|
9
|
+
from unittest.mock import patch, MagicMock
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
# Add the src directory to the path
|
|
13
|
+
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
14
|
+
|
|
15
|
+
from src.ai.gemini_api import GeminiImageGenerator
|
|
16
|
+
|
|
17
|
+
# Configure logging
|
|
18
|
+
logging.basicConfig(level=logging.INFO)
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
class TestGeminiAPI(unittest.TestCase):
|
|
22
|
+
"""
|
|
23
|
+
Test cases for Google Gemini API integration.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def setUp(self):
|
|
27
|
+
"""
|
|
28
|
+
Set up test environment.
|
|
29
|
+
"""
|
|
30
|
+
# Create a test output directory
|
|
31
|
+
self.test_output_dir = "output/test_gemini"
|
|
32
|
+
os.makedirs(self.test_output_dir, exist_ok=True)
|
|
33
|
+
|
|
34
|
+
# Mock API key
|
|
35
|
+
self.api_key = "test_api_key"
|
|
36
|
+
|
|
37
|
+
# Create the generator with the mock API key
|
|
38
|
+
self.gemini_generator = GeminiImageGenerator(
|
|
39
|
+
api_key=self.api_key,
|
|
40
|
+
output_dir=self.test_output_dir
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
@patch('requests.post')
|
|
44
|
+
def test_generate_image(self, mock_post):
|
|
45
|
+
"""
|
|
46
|
+
Test generating a single image with Gemini API.
|
|
47
|
+
"""
|
|
48
|
+
# Mock response
|
|
49
|
+
mock_response = MagicMock()
|
|
50
|
+
mock_response.status_code = 200
|
|
51
|
+
mock_response.json.return_value = {
|
|
52
|
+
"candidates": [
|
|
53
|
+
{
|
|
54
|
+
"content": {
|
|
55
|
+
"parts": [
|
|
56
|
+
{
|
|
57
|
+
"text": "Generated image description"
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"inlineData": {
|
|
61
|
+
"mimeType": "image/png",
|
|
62
|
+
"data": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg=="
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
]
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
]
|
|
69
|
+
}
|
|
70
|
+
mock_post.return_value = mock_response
|
|
71
|
+
|
|
72
|
+
# Test parameters
|
|
73
|
+
prompt = "A low-poly rabbit with black background"
|
|
74
|
+
model = "gemini-2.0-flash-exp-image-generation"
|
|
75
|
+
|
|
76
|
+
# Call the method
|
|
77
|
+
result = self.gemini_generator.generate_image(prompt, model)
|
|
78
|
+
|
|
79
|
+
# Verify the result
|
|
80
|
+
self.assertIsNotNone(result)
|
|
81
|
+
self.assertEqual(result["prompt"], prompt)
|
|
82
|
+
self.assertEqual(result["model"], model)
|
|
83
|
+
self.assertTrue("local_path" in result)
|
|
84
|
+
self.assertTrue(os.path.exists(result["local_path"]))
|
|
85
|
+
|
|
86
|
+
# Verify the API call
|
|
87
|
+
mock_post.assert_called_once()
|
|
88
|
+
args, kwargs = mock_post.call_args
|
|
89
|
+
self.assertTrue("generativelanguage.googleapis.com" in args[0])
|
|
90
|
+
self.assertEqual(kwargs["headers"]["x-goog-api-key"], self.api_key)
|
|
91
|
+
self.assertTrue("prompt" in str(kwargs["json"]))
|
|
92
|
+
|
|
93
|
+
@patch('requests.post')
|
|
94
|
+
def test_generate_multiple_views(self, mock_post):
|
|
95
|
+
"""
|
|
96
|
+
Test generating multiple views of an object with Gemini API.
|
|
97
|
+
"""
|
|
98
|
+
# Mock response
|
|
99
|
+
mock_response = MagicMock()
|
|
100
|
+
mock_response.status_code = 200
|
|
101
|
+
mock_response.json.return_value = {
|
|
102
|
+
"candidates": [
|
|
103
|
+
{
|
|
104
|
+
"content": {
|
|
105
|
+
"parts": [
|
|
106
|
+
{
|
|
107
|
+
"text": "Generated image description"
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"inlineData": {
|
|
111
|
+
"mimeType": "image/png",
|
|
112
|
+
"data": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg=="
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
]
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
]
|
|
119
|
+
}
|
|
120
|
+
mock_post.return_value = mock_response
|
|
121
|
+
|
|
122
|
+
# Test parameters
|
|
123
|
+
prompt = "A low-poly rabbit"
|
|
124
|
+
num_views = 3
|
|
125
|
+
|
|
126
|
+
# Call the method
|
|
127
|
+
results = self.gemini_generator.generate_multiple_views(prompt, num_views)
|
|
128
|
+
|
|
129
|
+
# Verify the results
|
|
130
|
+
self.assertEqual(len(results), num_views)
|
|
131
|
+
for i, result in enumerate(results):
|
|
132
|
+
self.assertTrue("view_direction" in result)
|
|
133
|
+
self.assertEqual(result["view_index"], i + 1)
|
|
134
|
+
self.assertTrue("local_path" in result)
|
|
135
|
+
self.assertTrue(os.path.exists(result["local_path"]))
|
|
136
|
+
|
|
137
|
+
# Verify the API calls
|
|
138
|
+
self.assertEqual(mock_post.call_count, num_views)
|
|
139
|
+
|
|
140
|
+
@patch('requests.post')
|
|
141
|
+
def test_error_handling(self, mock_post):
|
|
142
|
+
"""
|
|
143
|
+
Test error handling in the Gemini API client.
|
|
144
|
+
"""
|
|
145
|
+
# Mock error response
|
|
146
|
+
mock_response = MagicMock()
|
|
147
|
+
mock_response.status_code = 400
|
|
148
|
+
mock_response.raise_for_status.side_effect = Exception("API Error")
|
|
149
|
+
mock_post.return_value = mock_response
|
|
150
|
+
|
|
151
|
+
# Test parameters
|
|
152
|
+
prompt = "A low-poly rabbit"
|
|
153
|
+
|
|
154
|
+
# Call the method and expect an exception
|
|
155
|
+
with self.assertRaises(Exception):
|
|
156
|
+
self.gemini_generator.generate_image(prompt)
|
|
157
|
+
|
|
158
|
+
def tearDown(self):
|
|
159
|
+
"""
|
|
160
|
+
Clean up after tests.
|
|
161
|
+
"""
|
|
162
|
+
# Clean up test output directory
|
|
163
|
+
import shutil
|
|
164
|
+
if os.path.exists(self.test_output_dir):
|
|
165
|
+
shutil.rmtree(self.test_output_dir)
|
|
166
|
+
|
|
167
|
+
if __name__ == "__main__":
|
|
168
|
+
unittest.main()
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Test script for image approval tool.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import sys
|
|
7
|
+
import logging
|
|
8
|
+
import unittest
|
|
9
|
+
from unittest.mock import patch, MagicMock
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
# Add the src directory to the path
|
|
13
|
+
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
14
|
+
|
|
15
|
+
from src.workflow.image_approval import ImageApprovalTool
|
|
16
|
+
|
|
17
|
+
# Configure logging
|
|
18
|
+
logging.basicConfig(level=logging.INFO)
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
class TestImageApproval(unittest.TestCase):
|
|
22
|
+
"""
|
|
23
|
+
Test cases for image approval tool.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def setUp(self):
|
|
27
|
+
"""
|
|
28
|
+
Set up test environment.
|
|
29
|
+
"""
|
|
30
|
+
# Create a test output directory
|
|
31
|
+
self.test_output_dir = "output/test_approval"
|
|
32
|
+
os.makedirs(self.test_output_dir, exist_ok=True)
|
|
33
|
+
|
|
34
|
+
# Create test images directory
|
|
35
|
+
self.test_images_dir = "output/test_approval/images"
|
|
36
|
+
os.makedirs(self.test_images_dir, exist_ok=True)
|
|
37
|
+
|
|
38
|
+
# Create test images
|
|
39
|
+
self.test_images = []
|
|
40
|
+
for i in range(3):
|
|
41
|
+
image_path = os.path.join(self.test_images_dir, f"view_{i}.png")
|
|
42
|
+
with open(image_path, "w") as f:
|
|
43
|
+
f.write(f"Mock image {i}")
|
|
44
|
+
self.test_images.append(image_path)
|
|
45
|
+
|
|
46
|
+
# Create the approval tool
|
|
47
|
+
self.approval_tool = ImageApprovalTool(
|
|
48
|
+
output_dir=os.path.join(self.test_output_dir, "approved")
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
def test_present_image_for_approval(self):
|
|
52
|
+
"""
|
|
53
|
+
Test presenting an image for approval.
|
|
54
|
+
"""
|
|
55
|
+
# Test parameters
|
|
56
|
+
image_path = self.test_images[0]
|
|
57
|
+
metadata = {
|
|
58
|
+
"prompt": "A test image",
|
|
59
|
+
"view_direction": "front view",
|
|
60
|
+
"view_index": 1
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
# Call the method
|
|
64
|
+
result = self.approval_tool.present_image_for_approval(image_path, metadata)
|
|
65
|
+
|
|
66
|
+
# Verify the result
|
|
67
|
+
self.assertIsNotNone(result)
|
|
68
|
+
self.assertTrue("approval_id" in result)
|
|
69
|
+
self.assertEqual(result["image_path"], image_path)
|
|
70
|
+
self.assertTrue("image_url" in result)
|
|
71
|
+
self.assertEqual(result["metadata"], metadata)
|
|
72
|
+
|
|
73
|
+
def test_process_approval_approved(self):
|
|
74
|
+
"""
|
|
75
|
+
Test processing an approved image.
|
|
76
|
+
"""
|
|
77
|
+
# Test parameters
|
|
78
|
+
image_path = self.test_images[0]
|
|
79
|
+
approval_id = "test_approval_1"
|
|
80
|
+
|
|
81
|
+
# Call the method
|
|
82
|
+
result = self.approval_tool.process_approval(approval_id, True, image_path)
|
|
83
|
+
|
|
84
|
+
# Verify the result
|
|
85
|
+
self.assertIsNotNone(result)
|
|
86
|
+
self.assertEqual(result["approval_id"], approval_id)
|
|
87
|
+
self.assertTrue(result["approved"])
|
|
88
|
+
self.assertEqual(result["original_path"], image_path)
|
|
89
|
+
self.assertTrue("approved_path" in result)
|
|
90
|
+
|
|
91
|
+
# Verify the file was copied
|
|
92
|
+
self.assertTrue(os.path.exists(result["approved_path"]))
|
|
93
|
+
|
|
94
|
+
def test_process_approval_denied(self):
|
|
95
|
+
"""
|
|
96
|
+
Test processing a denied image.
|
|
97
|
+
"""
|
|
98
|
+
# Test parameters
|
|
99
|
+
image_path = self.test_images[1]
|
|
100
|
+
approval_id = "test_approval_2"
|
|
101
|
+
|
|
102
|
+
# Call the method
|
|
103
|
+
result = self.approval_tool.process_approval(approval_id, False, image_path)
|
|
104
|
+
|
|
105
|
+
# Verify the result
|
|
106
|
+
self.assertIsNotNone(result)
|
|
107
|
+
self.assertEqual(result["approval_id"], approval_id)
|
|
108
|
+
self.assertFalse(result["approved"])
|
|
109
|
+
self.assertEqual(result["original_path"], image_path)
|
|
110
|
+
self.assertFalse("approved_path" in result)
|
|
111
|
+
|
|
112
|
+
def test_get_approved_images(self):
|
|
113
|
+
"""
|
|
114
|
+
Test getting approved images.
|
|
115
|
+
"""
|
|
116
|
+
# Approve some images
|
|
117
|
+
for i, image_path in enumerate(self.test_images):
|
|
118
|
+
self.approval_tool.process_approval(f"test_approval_{i}", True, image_path)
|
|
119
|
+
|
|
120
|
+
# Call the method
|
|
121
|
+
approved_images = self.approval_tool.get_approved_images()
|
|
122
|
+
|
|
123
|
+
# Verify the result
|
|
124
|
+
self.assertEqual(len(approved_images), len(self.test_images))
|
|
125
|
+
|
|
126
|
+
def test_get_approval_status(self):
|
|
127
|
+
"""
|
|
128
|
+
Test getting approval status.
|
|
129
|
+
"""
|
|
130
|
+
# Approve an image
|
|
131
|
+
approval_id = "test_approval_status"
|
|
132
|
+
self.approval_tool.process_approval(approval_id, True, self.test_images[0])
|
|
133
|
+
|
|
134
|
+
# Call the method
|
|
135
|
+
status = self.approval_tool.get_approval_status(approval_id)
|
|
136
|
+
|
|
137
|
+
# Verify the result
|
|
138
|
+
self.assertIsNotNone(status)
|
|
139
|
+
self.assertEqual(status["approval_id"], approval_id)
|
|
140
|
+
self.assertTrue(status["approved"])
|
|
141
|
+
self.assertTrue("approved_path" in status)
|
|
142
|
+
|
|
143
|
+
# Test with non-existent approval ID
|
|
144
|
+
status = self.approval_tool.get_approval_status("non_existent")
|
|
145
|
+
self.assertIsNotNone(status)
|
|
146
|
+
self.assertEqual(status["approval_id"], "non_existent")
|
|
147
|
+
self.assertFalse(status["approved"])
|
|
148
|
+
|
|
149
|
+
def test_batch_process_approvals(self):
|
|
150
|
+
"""
|
|
151
|
+
Test batch processing of approvals.
|
|
152
|
+
"""
|
|
153
|
+
# Test parameters
|
|
154
|
+
approvals = [
|
|
155
|
+
{
|
|
156
|
+
"approval_id": "batch_1",
|
|
157
|
+
"approved": True,
|
|
158
|
+
"image_path": self.test_images[0]
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
"approval_id": "batch_2",
|
|
162
|
+
"approved": False,
|
|
163
|
+
"image_path": self.test_images[1]
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
"approval_id": "batch_3",
|
|
167
|
+
"approved": True,
|
|
168
|
+
"image_path": self.test_images[2]
|
|
169
|
+
}
|
|
170
|
+
]
|
|
171
|
+
|
|
172
|
+
# Call the method
|
|
173
|
+
results = self.approval_tool.batch_process_approvals(approvals)
|
|
174
|
+
|
|
175
|
+
# Verify the results
|
|
176
|
+
self.assertEqual(len(results), len(approvals))
|
|
177
|
+
|
|
178
|
+
# Check approved images
|
|
179
|
+
approved_images = self.approval_tool.get_approved_images()
|
|
180
|
+
self.assertEqual(len(approved_images), 2) # Two images were approved
|
|
181
|
+
|
|
182
|
+
def tearDown(self):
|
|
183
|
+
"""
|
|
184
|
+
Clean up after tests.
|
|
185
|
+
"""
|
|
186
|
+
# Clean up test output directory
|
|
187
|
+
import shutil
|
|
188
|
+
if os.path.exists(self.test_output_dir):
|
|
189
|
+
shutil.rmtree(self.test_output_dir)
|
|
190
|
+
|
|
191
|
+
if __name__ == "__main__":
|
|
192
|
+
unittest.main()
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Test script for the image approval workflow.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import sys
|
|
7
|
+
import json
|
|
8
|
+
import logging
|
|
9
|
+
import unittest
|
|
10
|
+
from unittest.mock import patch, MagicMock
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
# Add the src directory to the path
|
|
14
|
+
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
15
|
+
|
|
16
|
+
from src.workflow.image_approval import ImageApprovalManager
|
|
17
|
+
from src.config import IMAGE_APPROVAL
|
|
18
|
+
|
|
19
|
+
# Configure logging
|
|
20
|
+
logging.basicConfig(level=logging.INFO)
|
|
21
|
+
logger = logging.getLogger(__name__)
|
|
22
|
+
|
|
23
|
+
class TestImageApprovalWorkflow(unittest.TestCase):
|
|
24
|
+
"""
|
|
25
|
+
Test cases for the image approval workflow.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def setUp(self):
|
|
29
|
+
"""
|
|
30
|
+
Set up test environment.
|
|
31
|
+
"""
|
|
32
|
+
# Create test output directories
|
|
33
|
+
self.test_output_dir = "output/test_image_approval"
|
|
34
|
+
self.test_approved_dir = os.path.join(self.test_output_dir, "approved")
|
|
35
|
+
os.makedirs(self.test_output_dir, exist_ok=True)
|
|
36
|
+
os.makedirs(self.test_approved_dir, exist_ok=True)
|
|
37
|
+
|
|
38
|
+
# Create the approval manager
|
|
39
|
+
self.approval_manager = ImageApprovalManager(
|
|
40
|
+
output_dir=self.test_output_dir,
|
|
41
|
+
approved_dir=self.test_approved_dir,
|
|
42
|
+
min_approved_images=3,
|
|
43
|
+
auto_approve=False
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
# Create test images
|
|
47
|
+
self.test_images = []
|
|
48
|
+
for i in range(5):
|
|
49
|
+
image_path = os.path.join(self.test_output_dir, f"test_image_{i}.png")
|
|
50
|
+
with open(image_path, "w") as f:
|
|
51
|
+
f.write(f"test image data {i}")
|
|
52
|
+
self.test_images.append({
|
|
53
|
+
"id": f"image_{i}",
|
|
54
|
+
"local_path": image_path,
|
|
55
|
+
"view_index": i,
|
|
56
|
+
"view_direction": f"view_{i}"
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
def test_add_images(self):
|
|
60
|
+
"""
|
|
61
|
+
Test adding images to the approval manager.
|
|
62
|
+
"""
|
|
63
|
+
# Add images
|
|
64
|
+
self.approval_manager.add_images(self.test_images)
|
|
65
|
+
|
|
66
|
+
# Verify images were added
|
|
67
|
+
self.assertEqual(len(self.approval_manager.images), 5)
|
|
68
|
+
self.assertEqual(len(self.approval_manager.pending_images), 5)
|
|
69
|
+
self.assertEqual(len(self.approval_manager.approved_images), 0)
|
|
70
|
+
self.assertEqual(len(self.approval_manager.rejected_images), 0)
|
|
71
|
+
|
|
72
|
+
def test_approve_image(self):
|
|
73
|
+
"""
|
|
74
|
+
Test approving an image.
|
|
75
|
+
"""
|
|
76
|
+
# Add images
|
|
77
|
+
self.approval_manager.add_images(self.test_images)
|
|
78
|
+
|
|
79
|
+
# Approve an image
|
|
80
|
+
result = self.approval_manager.approve_image("image_0")
|
|
81
|
+
|
|
82
|
+
# Verify the result
|
|
83
|
+
self.assertTrue(result["success"])
|
|
84
|
+
self.assertEqual(result["image_id"], "image_0")
|
|
85
|
+
self.assertEqual(result["status"], "approved")
|
|
86
|
+
|
|
87
|
+
# Verify the image was moved to approved
|
|
88
|
+
self.assertEqual(len(self.approval_manager.pending_images), 4)
|
|
89
|
+
self.assertEqual(len(self.approval_manager.approved_images), 1)
|
|
90
|
+
self.assertEqual(len(self.approval_manager.rejected_images), 0)
|
|
91
|
+
|
|
92
|
+
# Verify the image was copied to the approved directory
|
|
93
|
+
approved_path = os.path.join(self.test_approved_dir, "image_0.png")
|
|
94
|
+
self.assertTrue(os.path.exists(approved_path))
|
|
95
|
+
|
|
96
|
+
def test_reject_image(self):
|
|
97
|
+
"""
|
|
98
|
+
Test rejecting an image.
|
|
99
|
+
"""
|
|
100
|
+
# Add images
|
|
101
|
+
self.approval_manager.add_images(self.test_images)
|
|
102
|
+
|
|
103
|
+
# Reject an image
|
|
104
|
+
result = self.approval_manager.reject_image("image_1")
|
|
105
|
+
|
|
106
|
+
# Verify the result
|
|
107
|
+
self.assertTrue(result["success"])
|
|
108
|
+
self.assertEqual(result["image_id"], "image_1")
|
|
109
|
+
self.assertEqual(result["status"], "rejected")
|
|
110
|
+
|
|
111
|
+
# Verify the image was moved to rejected
|
|
112
|
+
self.assertEqual(len(self.approval_manager.pending_images), 4)
|
|
113
|
+
self.assertEqual(len(self.approval_manager.approved_images), 0)
|
|
114
|
+
self.assertEqual(len(self.approval_manager.rejected_images), 1)
|
|
115
|
+
|
|
116
|
+
def test_get_approval_status(self):
|
|
117
|
+
"""
|
|
118
|
+
Test getting the approval status.
|
|
119
|
+
"""
|
|
120
|
+
# Add images
|
|
121
|
+
self.approval_manager.add_images(self.test_images)
|
|
122
|
+
|
|
123
|
+
# Approve some images
|
|
124
|
+
self.approval_manager.approve_image("image_0")
|
|
125
|
+
self.approval_manager.approve_image("image_1")
|
|
126
|
+
self.approval_manager.approve_image("image_2")
|
|
127
|
+
|
|
128
|
+
# Reject an image
|
|
129
|
+
self.approval_manager.reject_image("image_3")
|
|
130
|
+
|
|
131
|
+
# Get the status
|
|
132
|
+
status = self.approval_manager.get_status()
|
|
133
|
+
|
|
134
|
+
# Verify the status
|
|
135
|
+
self.assertEqual(status["total_images"], 5)
|
|
136
|
+
self.assertEqual(status["pending_count"], 1)
|
|
137
|
+
self.assertEqual(status["approved_count"], 3)
|
|
138
|
+
self.assertEqual(status["rejected_count"], 1)
|
|
139
|
+
self.assertTrue(status["has_minimum_approved"])
|
|
140
|
+
self.assertEqual(len(status["approved_images"]), 3)
|
|
141
|
+
self.assertEqual(len(status["pending_images"]), 1)
|
|
142
|
+
self.assertEqual(len(status["rejected_images"]), 1)
|
|
143
|
+
|
|
144
|
+
def test_get_approved_images(self):
|
|
145
|
+
"""
|
|
146
|
+
Test getting approved images.
|
|
147
|
+
"""
|
|
148
|
+
# Add images
|
|
149
|
+
self.approval_manager.add_images(self.test_images)
|
|
150
|
+
|
|
151
|
+
# Approve some images
|
|
152
|
+
self.approval_manager.approve_image("image_0")
|
|
153
|
+
self.approval_manager.approve_image("image_2")
|
|
154
|
+
self.approval_manager.approve_image("image_4")
|
|
155
|
+
|
|
156
|
+
# Get approved images
|
|
157
|
+
approved = self.approval_manager.get_approved_images()
|
|
158
|
+
|
|
159
|
+
# Verify approved images
|
|
160
|
+
self.assertEqual(len(approved), 3)
|
|
161
|
+
self.assertEqual(approved[0]["id"], "image_0")
|
|
162
|
+
self.assertEqual(approved[1]["id"], "image_2")
|
|
163
|
+
self.assertEqual(approved[2]["id"], "image_4")
|
|
164
|
+
|
|
165
|
+
def test_auto_approve(self):
|
|
166
|
+
"""
|
|
167
|
+
Test auto-approval mode.
|
|
168
|
+
"""
|
|
169
|
+
# Create an auto-approve manager
|
|
170
|
+
auto_manager = ImageApprovalManager(
|
|
171
|
+
output_dir=self.test_output_dir,
|
|
172
|
+
approved_dir=self.test_approved_dir,
|
|
173
|
+
min_approved_images=3,
|
|
174
|
+
auto_approve=True
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
# Add images
|
|
178
|
+
auto_manager.add_images(self.test_images)
|
|
179
|
+
|
|
180
|
+
# Verify all images were auto-approved
|
|
181
|
+
self.assertEqual(len(auto_manager.pending_images), 0)
|
|
182
|
+
self.assertEqual(len(auto_manager.approved_images), 5)
|
|
183
|
+
self.assertEqual(len(auto_manager.rejected_images), 0)
|
|
184
|
+
|
|
185
|
+
def test_has_minimum_approved(self):
|
|
186
|
+
"""
|
|
187
|
+
Test checking if minimum approved images are met.
|
|
188
|
+
"""
|
|
189
|
+
# Add images
|
|
190
|
+
self.approval_manager.add_images(self.test_images)
|
|
191
|
+
|
|
192
|
+
# Initially should not have minimum
|
|
193
|
+
self.assertFalse(self.approval_manager.has_minimum_approved())
|
|
194
|
+
|
|
195
|
+
# Approve two images
|
|
196
|
+
self.approval_manager.approve_image("image_0")
|
|
197
|
+
self.approval_manager.approve_image("image_1")
|
|
198
|
+
|
|
199
|
+
# Still should not have minimum
|
|
200
|
+
self.assertFalse(self.approval_manager.has_minimum_approved())
|
|
201
|
+
|
|
202
|
+
# Approve one more image
|
|
203
|
+
self.approval_manager.approve_image("image_2")
|
|
204
|
+
|
|
205
|
+
# Now should have minimum
|
|
206
|
+
self.assertTrue(self.approval_manager.has_minimum_approved())
|
|
207
|
+
|
|
208
|
+
def test_save_and_load_state(self):
|
|
209
|
+
"""
|
|
210
|
+
Test saving and loading the approval state.
|
|
211
|
+
"""
|
|
212
|
+
# Add images
|
|
213
|
+
self.approval_manager.add_images(self.test_images)
|
|
214
|
+
|
|
215
|
+
# Approve and reject some images
|
|
216
|
+
self.approval_manager.approve_image("image_0")
|
|
217
|
+
self.approval_manager.approve_image("image_2")
|
|
218
|
+
self.approval_manager.reject_image("image_3")
|
|
219
|
+
|
|
220
|
+
# Save the state
|
|
221
|
+
state_file = os.path.join(self.test_output_dir, "approval_state.json")
|
|
222
|
+
self.approval_manager.save_state(state_file)
|
|
223
|
+
|
|
224
|
+
# Create a new manager
|
|
225
|
+
new_manager = ImageApprovalManager(
|
|
226
|
+
output_dir=self.test_output_dir,
|
|
227
|
+
approved_dir=self.test_approved_dir,
|
|
228
|
+
min_approved_images=3,
|
|
229
|
+
auto_approve=False
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
# Load the state
|
|
233
|
+
new_manager.load_state(state_file)
|
|
234
|
+
|
|
235
|
+
# Verify the state was loaded correctly
|
|
236
|
+
self.assertEqual(len(new_manager.images), 5)
|
|
237
|
+
self.assertEqual(len(new_manager.pending_images), 2)
|
|
238
|
+
self.assertEqual(len(new_manager.approved_images), 2)
|
|
239
|
+
self.assertEqual(len(new_manager.rejected_images), 1)
|
|
240
|
+
|
|
241
|
+
def tearDown(self):
|
|
242
|
+
"""
|
|
243
|
+
Clean up after tests.
|
|
244
|
+
"""
|
|
245
|
+
# Clean up test output directory
|
|
246
|
+
import shutil
|
|
247
|
+
if os.path.exists(self.test_output_dir):
|
|
248
|
+
shutil.rmtree(self.test_output_dir)
|
|
249
|
+
|
|
250
|
+
if __name__ == "__main__":
|
|
251
|
+
unittest.main()
|