mirror of
https://github.com/azaion/gps-denied-onboard.git
synced 2026-04-23 00:26:36 +00:00
Initial commit
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
import pytest
|
||||
pytest.skip("Obsolete test file replaced by component-specific unit tests", allow_module_level=True)
|
||||
|
||||
import numpy as np
|
||||
import cv2
|
||||
from unittest.mock import Mock, MagicMock
|
||||
from datetime import datetime
|
||||
|
||||
# Import components
|
||||
from f05_image_input_pipeline import ImageInputPipeline, ImageBatch
|
||||
from f11_failure_recovery_coordinator import (
|
||||
FailureRecoveryCoordinator,
|
||||
GPSPoint,
|
||||
TileCandidate,
|
||||
UserAnchor,
|
||||
ChunkHandle
|
||||
)
|
||||
|
||||
@pytest.fixture
|
||||
def mock_dependencies():
|
||||
"""Mocks the external dependencies for the FailureRecoveryCoordinator."""
|
||||
return {
|
||||
"satellite_data_manager": Mock(),
|
||||
"image_rotation_manager": Mock(),
|
||||
"global_place_recognition": Mock(),
|
||||
"metric_refinement": Mock(),
|
||||
"factor_graph_optimizer": Mock(),
|
||||
"route_chunk_manager": Mock()
|
||||
}
|
||||
|
||||
@pytest.fixture
|
||||
def coordinator(mock_dependencies):
|
||||
return FailureRecoveryCoordinator(mock_dependencies)
|
||||
|
||||
@pytest.fixture
|
||||
def pipeline(tmp_path):
|
||||
"""Creates a temporary image pipeline."""
|
||||
return ImageInputPipeline(storage_dir=str(tmp_path))
|
||||
|
||||
def create_dummy_image_bytes():
|
||||
"""Helper to create valid JPEG byte data for the F05 pipeline."""
|
||||
img = np.zeros((480, 640, 3), dtype=np.uint8)
|
||||
cv2.putText(img, "Test", (50, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2)
|
||||
_, buffer = cv2.imencode('.jpg', img)
|
||||
return buffer.tobytes()
|
||||
|
||||
def test_zero_overlap_triggers_user_recovery(pipeline, coordinator, mock_dependencies):
|
||||
"""
|
||||
Tests the integration pipeline:
|
||||
1. Ingest images via F05.
|
||||
2. Simulate VO tracking loss (0% overlap).
|
||||
3. Simulate automatic chunk recovery failure.
|
||||
4. Verify the system successfully engages the User Input hooks.
|
||||
"""
|
||||
flight_id = "test_flight_001"
|
||||
|
||||
# 1. Ingest dummy batch of images
|
||||
dummy_bytes = create_dummy_image_bytes()
|
||||
batch = ImageBatch(
|
||||
images=[dummy_bytes, dummy_bytes],
|
||||
filenames=["AD000001.jpg", "AD000002.jpg"],
|
||||
start_sequence=1,
|
||||
end_sequence=2,
|
||||
batch_number=1
|
||||
)
|
||||
|
||||
assert pipeline.queue_batch(flight_id, batch) == True
|
||||
processed_images = pipeline.process_next_batch(flight_id)
|
||||
assert len(processed_images) == 2
|
||||
|
||||
# Simulate Frame 1 (Success)
|
||||
frame_1 = processed_images[0]
|
||||
|
||||
# 2. Simulate Frame 2 (0% Overlap -> VO Tracking Loss)
|
||||
frame_2 = processed_images[1]
|
||||
|
||||
# The Engine would normally call create_chunk_on_tracking_loss here
|
||||
mock_chunk = ChunkHandle(
|
||||
chunk_id="chunk_test_1", flight_id=flight_id, start_frame_id=2,
|
||||
end_frame_id=None, frames=[2], is_active=True, has_anchor=False,
|
||||
anchor_frame_id=None, anchor_gps=None, matching_status="unanchored"
|
||||
)
|
||||
mock_dependencies["route_chunk_manager"].create_chunk.return_value = mock_chunk
|
||||
|
||||
created_chunk = coordinator.create_chunk_on_tracking_loss(flight_id, frame_2.sequence)
|
||||
assert created_chunk.chunk_id == "chunk_test_1"
|
||||
|
||||
# 3. Simulate automatic chunk recovery failure
|
||||
# (e.g. over a featureless lake, DINOv2 returns no candidates)
|
||||
mock_dependencies["global_place_recognition"].retrieve_candidate_tiles_for_chunk.return_value = []
|
||||
candidates = coordinator.try_chunk_semantic_matching(created_chunk.chunk_id, [frame_2.image])
|
||||
|
||||
assert candidates is None # Automatic recovery failed
|
||||
|
||||
# 4. Verify User Input hooks correctly engage
|
||||
# Because candidates is None, the engine triggers the UI request
|
||||
mock_candidate_tiles = [
|
||||
TileCandidate(tile_id="tile_A", score=0.8, gps=GPSPoint(lat=48.1, lon=37.1))
|
||||
]
|
||||
|
||||
user_request = coordinator.create_user_input_request(
|
||||
flight_id=flight_id,
|
||||
frame_id=frame_2.sequence,
|
||||
uav_image=frame_2.image,
|
||||
candidate_tiles=mock_candidate_tiles
|
||||
)
|
||||
|
||||
assert user_request.flight_id == flight_id
|
||||
assert user_request.frame_id == 2
|
||||
assert "Tracking lost and automatic recovery failed" in user_request.message
|
||||
assert len(user_request.candidate_tiles) == 1
|
||||
|
||||
def test_apply_user_anchor_resumes_processing(coordinator, mock_dependencies):
|
||||
"""Tests that a provided user anchor correctly bounds to the factor graph."""
|
||||
flight_id = "test_flight_001"
|
||||
frame_id = 2
|
||||
|
||||
# Setup mock to return a valid chunk ID for the frame
|
||||
mock_dependencies["route_chunk_manager"].get_chunk_for_frame.return_value = "chunk_test_1"
|
||||
|
||||
user_anchor = UserAnchor(
|
||||
uav_pixel=(512.0, 384.0),
|
||||
satellite_gps=GPSPoint(lat=48.123, lon=37.123, altitude_m=400.0)
|
||||
)
|
||||
|
||||
assert coordinator.apply_user_anchor(flight_id, frame_id, user_anchor) == True
|
||||
mock_dependencies["factor_graph_optimizer"].add_chunk_anchor.assert_called_once()
|
||||
Reference in New Issue
Block a user