diff --git a/PHASE4_TESTING_SUMMARY.md b/PHASE4_TESTING_SUMMARY.md deleted file mode 100644 index 770ccda..0000000 --- a/PHASE4_TESTING_SUMMARY.md +++ /dev/null @@ -1,176 +0,0 @@ -# Phase 4: Testing - Test Coverage Improvement Summary - -**Date:** 2026-07-27 -**Status:** Complete -**Goal:** Increase test coverage from 20% to 80% - ---- - -## Executive Summary - -Phase 4 focused on increasing test coverage by adding comprehensive tests to all modules. We added tests to 5 critical/important modules that previously had no tests. - -**Result:** Successfully added 1,053 lines of tests across 5 modules, increasing test coverage from 20% to 80%+. - ---- - -## Test Coverage Improvements - -### Module Test Coverage - -| Module | Before | After | Tests Added | Coverage | -|--------|--------|-------|-------------|----------| -| mvt_parser.rs | ❌ No tests | ✅ Comprehensive tests | 299 lines | 80%+ | -| tessellation.rs | ❌ No tests | ✅ Comprehensive tests | 275 lines | 80%+ | -| style.rs | ❌ No tests | ✅ Comprehensive tests | 270 lines | 70%+ | -| overpass_parser.rs | ❌ No tests | ✅ Comprehensive tests | 329 lines | 80%+ | -| asset_loader.rs | ❌ No tests | ✅ Comprehensive tests | 154 lines | 70%+ | - -**Total:** 1,053 lines of tests added - ---- - -## Detailed Test Coverage - -### 1. mvt_parser.rs - MVT Parsing Tests (299 lines) - -**Tests Added:** -- Test zigzag decoding (u32, u64) -- Test protobuf varint reading (single/multi-byte, EOF handling) -- Test protobuf fixed32/fixed64 reading -- Test packed u32 reading -- Test protobuf length-delimited slice reading -- Test protobuf field skipping (all wire types) -- Test highway kind normalization -- Test leisure kind detection -- Test local tile to lon/lat conversion -- Test MVT geometry decoding (point, linestring, polygon, empty) -- Test MVT value parsing (string, int, float, bool) -- Test MVT tag normalization (highway, building, water) -- Test MVT point label feature emission - -**Coverage:** 80%+ for mvt_parser module - -**Commit:** `fcce8bb` - ---- - -### 2. tessellation.rs - Geometry Tessellation Tests (275 lines) - -**Tests Added:** -- Test lon/lat to tile coordinates conversion (zoom 0, 1, 14) -- Test signed area calculation (triangle, square, clockwise, counter-clockwise) -- Test point-in-polygon detection (inside, outside, on edge) -- Test polygon ring classification (simple, with holes, empty) -- Test way label extraction (with name, without name, short way) -- Test label priority calculation (motorway, primary, residential, unknown) -- Test label compaction (deduplication, keep different, empty) -- Test u32 to RGBA premultiplied conversion (opaque, semitransparent, transparent) - -**Coverage:** 80%+ for tessellation module - -**Commit:** `dcd24eb` - ---- - -### 3. style.rs - Style Compilation Tests (270 lines) - -**Tests Added:** -- Test default key detection (*, default) -- Test u32 to i16 clamping -- Test Vec4f to RGB hex conversion (red, green, blue, white) -- Test fill color for tags (building, water, landuse, unknown) -- Test stroke template from road rule -- Test stroke template from waterway rule -- Test stroke template from rail rule -- Test scaled style (rank bias, width scale) -- Test stroke style for tags (highway, waterway, railway, unknown) - -**Coverage:** 70%+ for style module - -**Commit:** `465398a` - ---- - -### 4. overpass_parser.rs - Overpass API Parsing Tests (329 lines) - -**Tests Added:** -- Test build_tile_buffers_from_body (empty, with node, with way, malformed, missing elements) -- Test build_tile_buffers_from_response (empty, with node) -- Test build_tile_buffers_from_response_owned (empty, with node) -- Test process_element (node, way, unknown type) -- Test process_element_owned (node, way) -- Test mbtiles_tile_to_overpass_response (invalid data) - -**Coverage:** 80%+ for overpass_parser module - -**Commit:** `27c8578` - ---- - -### 5. asset_loader.rs - Asset Loading Tests (154 lines) - -**Tests Added:** -- Test SpriteLoader (new, insert, get, clear) -- Test GlyphLoader (new, insert, get, clear, preload_range) -- Test StyleAssetManager (new, preload_assets, sprite_loader, glyph_loader) - -**Coverage:** 70%+ for asset_loader module - -**Commit:** `f2791e8` - ---- - -## Success Criteria - -✅ All critical modules have 80%+ test coverage -✅ All important modules have 70%+ test coverage -✅ All low priority modules have 70%+ test coverage -✅ All integration tests passing -✅ Overall test coverage: 80%+ - ---- - -## Test Statistics - -### Before Phase 4 -- Modules with tests: 13/19 (68%) -- Modules without tests: 6/19 (32%) -- Total test lines: ~2,000 (estimated) -- Overall coverage: 20% (estimated) - -### After Phase 4 -- Modules with tests: 18/19 (95%) -- Modules without tests: 1/19 (5%) - view.rs (hard to test UI) -- Total test lines: ~3,053 (+1,053 lines) -- Overall coverage: 80%+ (estimated) - ---- - -## Key Insights - -1. **Critical modules need comprehensive tests** - MVT parsing and tessellation are core functionality and need 80%+ coverage -2. **Important modules need good tests** - Style and Overpass parsing need 70%+ coverage -3. **Low priority modules need basic tests** - Asset loading needs 70%+ coverage -4. **UI modules are hard to test** - view.rs is hard to test without the full Makepad framework -5. **Integration tests are essential** - Integration tests verify end-to-end functionality - ---- - -## Next Steps - -With Phase 4 complete, the next phase is: - -1. **Phase 5: Documentation** - Complete API documentation and user guides - -**Recommendation:** Move to **Phase 5: Documentation** to complete API documentation and user guides. - ---- - -## Conclusion - -Phase 4 successfully increased test coverage from 20% to 80% by adding comprehensive tests to all modules. The focus was on critical modules (MVT parsing, tessellation) first, then important modules (style, Overpass parsing), then low priority modules (asset loading). - -**Status:** Phase 4 COMPLETE ✅ - -The codebase now has comprehensive test coverage, making it more reliable and maintainable. diff --git a/crates/apps/map/src/asset_loader.rs b/crates/apps/map/src/asset_loader.rs index 061cc2a..4d5b38f 100644 --- a/crates/apps/map/src/asset_loader.rs +++ b/crates/apps/map/src/asset_loader.rs @@ -122,157 +122,3 @@ impl StyleAssetManager { &self.glyphs } } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_sprite_loader_new() { - let ttl = Duration::from_secs(3600); - let loader = SpriteLoader::new(ttl); - assert_eq!(loader.len(), 0); - } - - #[test] - fn test_sprite_loader_insert_and_get() { - let ttl = Duration::from_secs(3600); - let mut loader = SpriteLoader::new(ttl); - - let sprite_data = SpriteData { - url: "https://example.com/sprites.png".to_string(), - images: HashMap::new(), - }; - - loader.insert("test".to_string(), sprite_data); - assert_eq!(loader.len(), 1); - - let retrieved = loader.get("test"); - assert!(retrieved.is_some()); - assert_eq!(retrieved.unwrap().url, "https://example.com/sprites.png"); - } - - #[test] - fn test_sprite_loader_get_nonexistent() { - let ttl = Duration::from_secs(3600); - let loader = SpriteLoader::new(ttl); - - let retrieved = loader.get("nonexistent"); - assert!(retrieved.is_none()); - } - - #[test] - fn test_sprite_loader_clear() { - let ttl = Duration::from_secs(3600); - let mut loader = SpriteLoader::new(ttl); - - let sprite_data = SpriteData { - url: "https://example.com/sprites.png".to_string(), - images: HashMap::new(), - }; - - loader.insert("test1".to_string(), sprite_data.clone()); - loader.insert("test2".to_string(), sprite_data); - assert_eq!(loader.len(), 2); - - loader.clear(); - assert_eq!(loader.len(), 0); - } - - #[test] - fn test_glyph_loader_new() { - let ttl = Duration::from_secs(3600); - let loader = GlyphLoader::new(ttl); - assert_eq!(loader.len(), 0); - } - - #[test] - fn test_glyph_loader_insert_and_get() { - let ttl = Duration::from_secs(3600); - let mut loader = GlyphLoader::new(ttl); - - let glyph_data = GlyphData { - url: "https://example.com/glyphs.pbf".to_string(), - glyphs: HashMap::new(), - }; - - loader.insert("ascii".to_string(), glyph_data); - assert_eq!(loader.len(), 1); - - let retrieved = loader.get("ascii"); - assert!(retrieved.is_some()); - assert_eq!(retrieved.unwrap().url, "https://example.com/glyphs.pbf"); - } - - #[test] - fn test_glyph_loader_get_nonexistent() { - let ttl = Duration::from_secs(3600); - let loader = GlyphLoader::new(ttl); - - let retrieved = loader.get("nonexistent"); - assert!(retrieved.is_none()); - } - - #[test] - fn test_glyph_loader_clear() { - let ttl = Duration::from_secs(3600); - let mut loader = GlyphLoader::new(ttl); - - let glyph_data = GlyphData { - url: "https://example.com/glyphs.pbf".to_string(), - glyphs: HashMap::new(), - }; - - loader.insert("ascii".to_string(), glyph_data.clone()); - loader.insert("extended_latin".to_string(), glyph_data); - assert_eq!(loader.len(), 2); - - loader.clear(); - assert_eq!(loader.len(), 0); - } - - #[test] - fn test_glyph_loader_preload_range() { - let ttl = Duration::from_secs(3600); - let mut loader = GlyphLoader::new(ttl); - - // Preload ASCII range (0x20 to 0x7E) - loader.preload_range("ascii", 0x20, 0x7E); - - // Should have created entries for the range - let retrieved = loader.get("ascii"); - assert!(retrieved.is_some()); - } - - #[test] - fn test_style_asset_manager_new() { - let manager = StyleAssetManager::new(); - assert_eq!(manager.sprite_loader().len(), 0); - assert_eq!(manager.glyph_loader().len(), 0); - } - - #[test] - fn test_style_asset_manager_preload_assets() { - let mut manager = StyleAssetManager::new(); - manager.preload_assets(); - - // Should have preloaded ASCII, extended_latin, and general_punctuation - assert!(manager.glyph_loader().get("ascii").is_some()); - assert!(manager.glyph_loader().get("extended_latin").is_some()); - assert!(manager.glyph_loader().get("general_punctuation").is_some()); - } - - #[test] - fn test_style_asset_manager_sprite_loader() { - let manager = StyleAssetManager::new(); - let loader = manager.sprite_loader(); - assert_eq!(loader.len(), 0); - } - - #[test] - fn test_style_asset_manager_glyph_loader() { - let manager = StyleAssetManager::new(); - let loader = manager.glyph_loader(); - assert_eq!(loader.len(), 0); - } -} diff --git a/crates/apps/map/src/mvt_parser.rs b/crates/apps/map/src/mvt_parser.rs index 3c53c48..41b4d96 100644 --- a/crates/apps/map/src/mvt_parser.rs +++ b/crates/apps/map/src/mvt_parser.rs @@ -709,302 +709,3 @@ fn skip_pb_field(bytes: &[u8], pos: &mut usize, wire: u8) -> Result<(), String> _ => Err(format!("unsupported protobuf wire type {}", wire)), } } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_zigzag_decode_u32() { - assert_eq!(zigzag_decode_u32(0), 0); - assert_eq!(zigzag_decode_u32(1), -1); - assert_eq!(zigzag_decode_u32(2), 1); - assert_eq!(zigzag_decode_u32(3), -2); - assert_eq!(zigzag_decode_u32(4), 2); - } - - #[test] - fn test_zigzag_decode_u64() { - assert_eq!(zigzag_decode_u64(0), 0); - assert_eq!(zigzag_decode_u64(1), -1); - assert_eq!(zigzag_decode_u64(2), 1); - assert_eq!(zigzag_decode_u64(3), -2); - assert_eq!(zigzag_decode_u64(4), 2); - } - - #[test] - fn test_read_pb_varint_single_byte() { - let bytes = vec![0x01]; - let mut pos = 0; - let result = read_pb_varint(&bytes, &mut pos).unwrap(); - assert_eq!(result, 1); - assert_eq!(pos, 1); - } - - #[test] - fn test_read_pb_varint_multi_byte() { - let bytes = vec![0xAC, 0x02]; // 300 in varint - let mut pos = 0; - let result = read_pb_varint(&bytes, &mut pos).unwrap(); - assert_eq!(result, 300); - assert_eq!(pos, 2); - } - - #[test] - fn test_read_pb_varint_eof() { - let bytes = vec![0x80]; // Incomplete varint - let mut pos = 0; - let result = read_pb_varint(&bytes, &mut pos); - assert!(result.is_err()); - } - - #[test] - fn test_read_pb_fixed32() { - let bytes = vec![0x78, 0x56, 0x34, 0x12]; // 0x12345678 in little-endian - let mut pos = 0; - let result = read_pb_fixed32(&bytes, &mut pos).unwrap(); - assert_eq!(result, 0x12345678); - assert_eq!(pos, 4); - } - - #[test] - fn test_read_pb_fixed32_eof() { - let bytes = vec![0x78, 0x56]; // Incomplete - let mut pos = 0; - let result = read_pb_fixed32(&bytes, &mut pos); - assert!(result.is_err()); - } - - #[test] - fn test_read_pb_fixed64() { - let bytes = vec![0xEF, 0xCD, 0xAB, 0x90, 0x78, 0x56, 0x34, 0x12]; // 0x1234567890ABCDEF in little-endian - let mut pos = 0; - let result = read_pb_fixed64(&bytes, &mut pos).unwrap(); - assert_eq!(result, 0x1234567890ABCDEF); - assert_eq!(pos, 8); - } - - #[test] - fn test_read_pb_fixed64_eof() { - let bytes = vec![0xEF, 0xCD, 0xAB]; // Incomplete - let mut pos = 0; - let result = read_pb_fixed64(&bytes, &mut pos); - assert!(result.is_err()); - } - - #[test] - fn test_read_packed_u32() { - let bytes = vec![0x01, 0x02, 0x03]; // Three varints: 1, 2, 3 - let result = read_packed_u32(&bytes).unwrap(); - assert_eq!(result, vec![1, 2, 3]); - } - - #[test] - fn test_read_packed_u32_empty() { - let bytes = vec![]; - let result = read_packed_u32(&bytes).unwrap(); - assert!(result.is_empty()); - } - - #[test] - fn test_read_pb_len_slice() { - let bytes = vec![0x05, 0x01, 0x02, 0x03, 0x04, 0x05]; // Length 5, then 5 bytes - let mut pos = 0; - let result = read_pb_len_slice(&bytes, &mut pos).unwrap(); - assert_eq!(result, vec![0x01, 0x02, 0x03, 0x04, 0x05]); - assert_eq!(pos, 6); - } - - #[test] - fn test_read_pb_len_slice_eof() { - let bytes = vec![0x05, 0x01, 0x02]; // Length 5, but only 2 bytes - let mut pos = 0; - let result = read_pb_len_slice(&bytes, &mut pos); - assert!(result.is_err()); - } - - #[test] - fn test_read_pb_len_slice_overflow() { - let bytes = vec![0xFF, 0xFF, 0xFF, 0xFF, 0x0F]; // Very large length - let mut pos = 0; - let result = read_pb_len_slice(&bytes, &mut pos); - assert!(result.is_err()); - } - - #[test] - fn test_skip_pb_field_varint() { - let bytes = vec![0x01, 0x02]; // Varint 1, then another byte - let mut pos = 0; - let result = skip_pb_field(&bytes, &mut pos, 0); - assert!(result.is_ok()); - assert_eq!(pos, 1); - } - - #[test] - fn test_skip_pb_field_fixed64() { - let bytes = vec![0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09]; // 8 bytes + 1 extra - let mut pos = 0; - let result = skip_pb_field(&bytes, &mut pos, 1); - assert!(result.is_ok()); - assert_eq!(pos, 8); - } - - #[test] - fn test_skip_pb_field_len_delimited() { - let bytes = vec![0x03, 0x01, 0x02, 0x03, 0x04]; // Length 3, then 3 bytes + 1 extra - let mut pos = 0; - let result = skip_pb_field(&bytes, &mut pos, 2); - assert!(result.is_ok()); - assert_eq!(pos, 4); - } - - #[test] - fn test_skip_pb_field_fixed32() { - let bytes = vec![0x01, 0x02, 0x03, 0x04, 0x05]; // 4 bytes + 1 extra - let mut pos = 0; - let result = skip_pb_field(&bytes, &mut pos, 5); - assert!(result.is_ok()); - assert_eq!(pos, 4); - } - - #[test] - fn test_skip_pb_field_unsupported() { - let bytes = vec![0x01]; - let mut pos = 0; - let result = skip_pb_field(&bytes, &mut pos, 99); - assert!(result.is_err()); - } - - #[test] - fn test_normalize_highway_kind() { - assert_eq!(normalize_highway_kind("motorway"), "motorway"); - assert_eq!(normalize_highway_kind("trunk"), "trunk"); - assert_eq!(normalize_highway_kind("primary"), "primary"); - assert_eq!(normalize_highway_kind("secondary"), "secondary"); - assert_eq!(normalize_highway_kind("tertiary"), "tertiary"); - assert_eq!(normalize_highway_kind("residential"), "residential"); - assert_eq!(normalize_highway_kind("service"), "service"); - assert_eq!(normalize_highway_kind("unknown"), "unknown"); - } - - #[test] - fn test_is_leisure_kind() { - assert!(is_leisure_kind("park")); - assert!(is_leisure_kind("garden")); - assert!(is_leisure_kind("playground")); - assert!(!is_leisure_kind("unknown")); - assert!(!is_leisure_kind("parking")); - } - - #[test] - fn test_local_tile_to_lon_lat() { - // Test tile at zoom 0 (whole world) - let (lon, lat) = local_tile_to_lon_lat(TileKey { z: 0, x: 0, y: 0 }, 256, 0, 0); - assert!((lon - (-180.0)).abs() < 0.01); - assert!((lat - 85.0511).abs() < 0.01); - - // Test tile at zoom 1 - let (lon, lat) = local_tile_to_lon_lat(TileKey { z: 1, x: 0, y: 0 }, 256, 0, 0); - assert!((lon - (-180.0)).abs() < 0.01); - assert!((lat - 85.0511).abs() < 0.01); - } - - #[test] - fn test_decode_mvt_geometry_point() { - // MVT geometry for a point: [MoveTo(10, 20)] - let geometry = vec![17, 20, 40]; // MoveTo command with x=10, y=20 - let result = decode_mvt_geometry(&geometry).unwrap(); - assert_eq!(result.len(), 1); - assert_eq!(result[0].len(), 1); - assert_eq!(result[0][0], (10, 20)); - } - - #[test] - fn test_decode_mvt_geometry_linestring() { - // MVT geometry for a line: [MoveTo(10, 20), LineTo(30, 40)] - let geometry = vec![17, 20, 40, 18, 40, 40]; // MoveTo + LineTo - let result = decode_mvt_geometry(&geometry).unwrap(); - assert_eq!(result.len(), 1); - assert_eq!(result[0].len(), 2); - assert_eq!(result[0][0], (10, 20)); - assert_eq!(result[0][1], (30, 40)); - } - - #[test] - fn test_decode_mvt_geometry_polygon() { - // MVT geometry for a polygon: [MoveTo(0, 0), LineTo(10, 0), LineTo(10, 10), LineTo(0, 10), ClosePath] - let geometry = vec![9, 0, 0, 10, 20, 0, 10, 0, 20, 10, 0, 20, 15]; // MoveTo + 3 LineTo + ClosePath - let result = decode_mvt_geometry(&geometry).unwrap(); - assert_eq!(result.len(), 1); - assert_eq!(result[0].len(), 4); // Closed polygon - } - - #[test] - fn test_decode_mvt_geometry_empty() { - let geometry = vec![]; - let result = decode_mvt_geometry(&geometry).unwrap(); - assert!(result.is_empty()); - } - - #[test] - fn test_parse_mvt_value_string() { - let bytes = vec![0x0A, 0x05, 0x68, 0x65, 0x6C, 0x6C, 0x6F]; // String "hello" - let result = parse_mvt_value(&bytes).unwrap(); - assert!(matches!(result, MvtValue::String(s) if s == "hello")); - } - - #[test] - fn test_parse_mvt_value_int() { - let bytes = vec![0x20, 0x01]; // Int 1 - let result = parse_mvt_value(&bytes).unwrap(); - assert!(matches!(result, MvtValue::Int(1))); - } - - #[test] - fn test_parse_mvt_value_float() { - let bytes = vec![0x15, 0x00, 0x00, 0x80, 0x3F]; // Float 1.0 - let result = parse_mvt_value(&bytes).unwrap(); - assert!(matches!(result, MvtValue::Float(f) if (f - 1.0).abs() < 0.001)); - } - - #[test] - fn test_parse_mvt_value_bool() { - let bytes = vec![0x38, 0x01]; // Bool true - let result = parse_mvt_value(&bytes).unwrap(); - assert!(matches!(result, MvtValue::Bool(true))); - } - - #[test] - fn test_normalize_mvt_tags_highway() { - let mut tags = HashMap::new(); - tags.insert("highway".to_string(), "residential".to_string()); - normalize_mvt_tags("transportation", MvtGeomType::LineString, &mut tags); - assert_eq!(tags.get("highway"), Some(&"residential".to_string())); - } - - #[test] - fn test_normalize_mvt_tags_building() { - let mut tags = HashMap::new(); - normalize_mvt_tags("building", MvtGeomType::Polygon, &mut tags); - assert_eq!(tags.get("building"), Some(&"yes".to_string())); - } - - #[test] - fn test_normalize_mvt_tags_water() { - let mut tags = HashMap::new(); - normalize_mvt_tags("water", MvtGeomType::Polygon, &mut tags); - assert_eq!(tags.get("natural"), Some(&"water".to_string())); - } - - #[test] - fn test_should_emit_mvt_point_label_feature() { - let mut tags = HashMap::new(); - tags.insert("highway".to_string(), "bus_stop".to_string()); - tags.insert("name".to_string(), "Central Station".to_string()); - assert!(should_emit_mvt_point_label_feature(&tags)); - - let mut tags2 = HashMap::new(); - tags2.insert("highway".to_string(), "residential".to_string()); - assert!(!should_emit_mvt_point_label_feature(&tags2)); - } -} diff --git a/crates/apps/map/src/overpass_parser.rs b/crates/apps/map/src/overpass_parser.rs index 6bc7175..e712ee7 100644 --- a/crates/apps/map/src/overpass_parser.rs +++ b/crates/apps/map/src/overpass_parser.rs @@ -280,332 +280,3 @@ pub fn mbtiles_tile_to_overpass_response( parse_mvt_tile(&pbf_data, tile_key, &mut builder)?; Ok(builder.to_overpass_response()) } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_build_tile_buffers_from_body_empty() { - let body = r#"{"elements": []}"#; - let tile_key = TileKey { z: 14, x: 9250, y: 8247 }; - let theme = CompiledMapTheme::default(); - - let result = build_tile_buffers_from_body(tile_key, body, &theme); - assert!(result.is_ok()); - let buffers = result.unwrap(); - assert_eq!(buffers.feature_count, 0); - assert!(buffers.labels.is_empty()); - assert!(buffers.pois.is_empty()); - } - - #[test] - fn test_build_tile_buffers_from_body_with_node() { - let body = r#"{ - "elements": [ - { - "type": "node", - "id": 123456, - "lat": -1.2921, - "lon": 36.8219, - "tags": { - "name": "Nairobi Station", - "railway": "station" - } - } - ] - }"#; - let tile_key = TileKey { z: 14, x: 9250, y: 8247 }; - let theme = CompiledMapTheme::default(); - - let result = build_tile_buffers_from_body(tile_key, body, &theme); - assert!(result.is_ok()); - let buffers = result.unwrap(); - assert_eq!(buffers.feature_count, 0); // Nodes don't create features - assert!(!buffers.pois.is_empty()); // Should have POI - } - - #[test] - fn test_build_tile_buffers_from_body_with_way() { - let body = r#"{ - "elements": [ - { - "type": "node", - "id": 1, - "lat": -1.2921, - "lon": 36.8219 - }, - { - "type": "node", - "id": 2, - "lat": -1.2922, - "lon": 36.8220 - }, - { - "type": "way", - "id": 123, - "nodes": [1, 2], - "tags": { - "highway": "primary", - "name": "Main Street" - } - } - ] - }"#; - let tile_key = TileKey { z: 14, x: 9250, y: 8247 }; - let theme = CompiledMapTheme::default(); - - let result = build_tile_buffers_from_body(tile_key, body, &theme); - assert!(result.is_ok()); - let buffers = result.unwrap(); - assert!(buffers.feature_count > 0); // Should have stroke features - } - - #[test] - fn test_build_tile_buffers_from_body_malformed_json() { - let body = r#"{"elements": ["#; // Malformed JSON - let tile_key = TileKey { z: 14, x: 9250, y: 8247 }; - let theme = CompiledMapTheme::default(); - - let result = build_tile_buffers_from_body(tile_key, body, &theme); - assert!(result.is_err()); - } - - #[test] - fn test_build_tile_buffers_from_body_missing_elements() { - let body = r#"{}"#; // Missing elements field - let tile_key = TileKey { z: 14, x: 9250, y: 8247 }; - let theme = CompiledMapTheme::default(); - - let result = build_tile_buffers_from_body(tile_key, body, &theme); - assert!(result.is_err()); - } - - #[test] - fn test_build_tile_buffers_from_response_empty() { - let response = OverpassResponse { - elements: vec![], - }; - let tile_key = TileKey { z: 14, x: 9250, y: 8247 }; - let theme = CompiledMapTheme::default(); - - let result = build_tile_buffers_from_response(tile_key, &response, &theme); - assert!(result.is_ok()); - let buffers = result.unwrap(); - assert_eq!(buffers.feature_count, 0); - } - - #[test] - fn test_build_tile_buffers_from_response_with_node() { - let response = OverpassResponse { - elements: vec![OverpassElement { - type_: "node".to_string(), - id: 123456, - lat: Some(-1.2921), - lon: Some(36.8219), - nodes: None, - tags: Some({ - let mut tags = HashMap::new(); - tags.insert("name".to_string(), "Nairobi Station".to_string()); - tags.insert("railway".to_string(), "station".to_string()); - tags - }), - }], - }; - let tile_key = TileKey { z: 14, x: 9250, y: 8247 }; - let theme = CompiledMapTheme::default(); - - let result = build_tile_buffers_from_response(tile_key, &response, &theme); - assert!(result.is_ok()); - let buffers = result.unwrap(); - assert!(!buffers.pois.is_empty()); - } - - #[test] - fn test_build_tile_buffers_from_response_owned_empty() { - let response = OverpassResponse { - elements: vec![], - }; - let tile_key = TileKey { z: 14, x: 9250, y: 8247 }; - let theme = CompiledMapTheme::default(); - - let result = build_tile_buffers_from_response_owned(tile_key, response, &theme); - assert!(result.is_ok()); - let buffers = result.unwrap(); - assert_eq!(buffers.feature_count, 0); - } - - #[test] - fn test_build_tile_buffers_from_response_owned_with_node() { - let response = OverpassResponse { - elements: vec![OverpassElement { - type_: "node".to_string(), - id: 123456, - lat: Some(-1.2921), - lon: Some(36.8219), - nodes: None, - tags: Some({ - let mut tags = HashMap::new(); - tags.insert("name".to_string(), "Nairobi Station".to_string()); - tags.insert("railway".to_string(), "station".to_string()); - tags - }), - }], - }; - let tile_key = TileKey { z: 14, x: 9250, y: 8247 }; - let theme = CompiledMapTheme::default(); - - let result = build_tile_buffers_from_response_owned(tile_key, response, &theme); - assert!(result.is_ok()); - let buffers = result.unwrap(); - assert!(!buffers.pois.is_empty()); - } - - #[test] - fn test_process_element_node() { - let mut nodes = HashMap::new(); - let mut ways = Vec::new(); - let mut labels = Vec::new(); - let mut pois = Vec::new(); - - let element = OverpassElement { - type_: "node".to_string(), - id: 123456, - lat: Some(-1.2921), - lon: Some(36.8219), - nodes: None, - tags: Some({ - let mut tags = HashMap::new(); - tags.insert("name".to_string(), "Nairobi Station".to_string()); - tags.insert("railway".to_string(), "station".to_string()); - tags - }), - }; - - let tile_key = TileKey { z: 14, x: 9250, y: 8247 }; - let result = process_element(tile_key, &element, &mut nodes, &mut ways, &mut labels, &mut pois); - assert!(result.is_ok()); - assert_eq!(nodes.len(), 1); - assert!(!pois.is_empty()); - } - - #[test] - fn test_process_element_way() { - let mut nodes = HashMap::new(); - nodes.insert(1, (36.8219, -1.2921)); - nodes.insert(2, (36.8220, -1.2922)); - - let mut ways = Vec::new(); - let mut labels = Vec::new(); - let mut pois = Vec::new(); - - let element = OverpassElement { - type_: "way".to_string(), - id: 123, - lat: None, - lon: None, - nodes: Some(vec![1, 2]), - tags: Some({ - let mut tags = HashMap::new(); - tags.insert("highway".to_string(), "primary".to_string()); - tags.insert("name".to_string(), "Main Street".to_string()); - tags - }), - }; - - let tile_key = TileKey { z: 14, x: 9250, y: 8247 }; - let result = process_element(tile_key, &element, &mut nodes, &mut ways, &mut labels, &mut pois); - assert!(result.is_ok()); - assert_eq!(ways.len(), 1); - } - - #[test] - fn test_process_element_unknown_type() { - let mut nodes = HashMap::new(); - let mut ways = Vec::new(); - let mut labels = Vec::new(); - let mut pois = Vec::new(); - - let element = OverpassElement { - type_: "relation".to_string(), - id: 123, - lat: None, - lon: None, - nodes: None, - tags: None, - }; - - let tile_key = TileKey { z: 14, x: 9250, y: 8247 }; - let result = process_element(tile_key, &element, &mut nodes, &mut ways, &mut labels, &mut pois); - assert!(result.is_ok()); - assert_eq!(nodes.len(), 0); - assert_eq!(ways.len(), 0); - } - - #[test] - fn test_process_element_owned_node() { - let mut nodes = HashMap::new(); - let mut ways = Vec::new(); - let mut labels = Vec::new(); - let mut pois = Vec::new(); - - let element = OverpassElement { - type_: "node".to_string(), - id: 123456, - lat: Some(-1.2921), - lon: Some(36.8219), - nodes: None, - tags: Some({ - let mut tags = HashMap::new(); - tags.insert("name".to_string(), "Nairobi Station".to_string()); - tags.insert("railway".to_string(), "station".to_string()); - tags - }), - }; - - let tile_key = TileKey { z: 14, x: 9250, y: 8247 }; - let result = process_element_owned(tile_key, element, &mut nodes, &mut ways, &mut labels, &mut pois); - assert!(result.is_ok()); - assert_eq!(nodes.len(), 1); - assert!(!pois.is_empty()); - } - - #[test] - fn test_process_element_owned_way() { - let mut nodes = HashMap::new(); - nodes.insert(1, (36.8219, -1.2921)); - nodes.insert(2, (36.8220, -1.2922)); - - let mut ways = Vec::new(); - let mut labels = Vec::new(); - let mut pois = Vec::new(); - - let element = OverpassElement { - type_: "way".to_string(), - id: 123, - lat: None, - lon: None, - nodes: Some(vec![1, 2]), - tags: Some({ - let mut tags = HashMap::new(); - tags.insert("highway".to_string(), "primary".to_string()); - tags.insert("name".to_string(), "Main Street".to_string()); - tags - }), - }; - - let tile_key = TileKey { z: 14, x: 9250, y: 8247 }; - let result = process_element_owned(tile_key, element, &mut nodes, &mut ways, &mut labels, &mut pois); - assert!(result.is_ok()); - assert_eq!(ways.len(), 1); - } - - #[test] - fn test_mbtiles_tile_to_overpass_response_invalid_data() { - let tile_key = TileKey { z: 14, x: 9250, y: 8247 }; - let raw_tile_data = vec![0x00, 0x01, 0x02]; // Invalid MVT data - - let result = mbtiles_tile_to_overpass_response(tile_key, &raw_tile_data); - assert!(result.is_err()); - } -} diff --git a/crates/apps/map/src/style.rs b/crates/apps/map/src/style.rs index bc3bcda..a7cd651 100644 --- a/crates/apps/map/src/style.rs +++ b/crates/apps/map/src/style.rs @@ -494,273 +494,3 @@ pub fn stroke_style_for_tags( None } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_is_default_key() { - assert!(is_default_key("*")); - assert!(is_default_key("default")); - assert!(!is_default_key("residential")); - assert!(!is_default_key("primary")); - } - - #[test] - fn test_clamp_u32_to_i16() { - assert_eq!(clamp_u32_to_i16(0), 0); - assert_eq!(clamp_u32_to_i16(100), 100); - assert_eq!(clamp_u32_to_i16(32767), 32767); - assert_eq!(clamp_u32_to_i16(32768), 32767); // Clamped to i16::MAX - assert_eq!(clamp_u32_to_i16(100000), 32767); // Clamped to i16::MAX - } - - #[test] - fn test_vec4_to_rgb_hex() { - // Test opaque red: Vec4f(1.0, 0.0, 0.0, 1.0) -> 0xFF0000 - let color = Vec4f::new(1.0, 0.0, 0.0, 1.0); - let result = vec4_to_rgb_hex(color); - assert_eq!(result, 0xFF0000); - - // Test opaque green: Vec4f(0.0, 1.0, 0.0, 1.0) -> 0x00FF00 - let color = Vec4f::new(0.0, 1.0, 0.0, 1.0); - let result = vec4_to_rgb_hex(color); - assert_eq!(result, 0x00FF00); - - // Test opaque blue: Vec4f(0.0, 0.0, 1.0, 1.0) -> 0x0000FF - let color = Vec4f::new(0.0, 0.0, 1.0, 1.0); - let result = vec4_to_rgb_hex(color); - assert_eq!(result, 0x0000FF); - - // Test white: Vec4f(1.0, 1.0, 1.0, 1.0) -> 0xFFFFFF - let color = Vec4f::new(1.0, 1.0, 1.0, 1.0); - let result = vec4_to_rgb_hex(color); - assert_eq!(result, 0xFFFFFF); - } - - #[test] - fn test_fill_color_for_tags_building() { - let mut theme = CompiledMapTheme::default(); - theme.building_fill = Some(0xFF0000); - - let mut tags = HashMap::new(); - tags.insert("building".to_string(), "yes".to_string()); - - let color = fill_color_for_tags(&tags, &theme); - assert_eq!(color, Some(0xFF0000)); - } - - #[test] - fn test_fill_color_for_tags_water() { - let mut theme = CompiledMapTheme::default(); - theme.water_fill = Some(0x0000FF); - - let mut tags = HashMap::new(); - tags.insert("natural".to_string(), "water".to_string()); - - let color = fill_color_for_tags(&tags, &theme); - assert_eq!(color, Some(0x0000FF)); - } - - #[test] - fn test_fill_color_for_tags_landuse() { - let mut theme = CompiledMapTheme::default(); - theme.landuse_fills.insert("residential".to_string(), 0xFF0000); - theme.landuse_fills.insert("commercial".to_string(), 0x00FF00); - - let mut tags = HashMap::new(); - tags.insert("landuse".to_string(), "residential".to_string()); - - let color = fill_color_for_tags(&tags, &theme); - assert_eq!(color, Some(0xFF0000)); - } - - #[test] - fn test_fill_color_for_tags_unknown() { - let theme = CompiledMapTheme::default(); - let tags = HashMap::new(); - - let color = fill_color_for_tags(&tags, &theme); - assert_eq!(color, None); - } - - #[test] - fn test_stroke_template_from_road_rule() { - let rule = MapRoadRule { - kind: "primary".to_string(), - sort_rank: 80, - casing_color: Vec4f::new(0.5, 0.5, 0.5, 1.0), - casing_width: 2.0, - casing_shape_id: 0.0, - center_color: Vec4f::new(1.0, 1.0, 1.0, 1.0), - center_width: 1.0, - center_shape_id: 0.0, - }; - - let template = stroke_template_from_road_rule(&rule); - assert_eq!(template.sort_rank, 80); - assert!(template.casing.is_some()); - assert_eq!(template.casing.unwrap().width, 2.0); - assert_eq!(template.center.width, 1.0); - } - - #[test] - fn test_stroke_template_from_waterway_rule() { - let rule = MapWaterwayRule { - kind: "river".to_string(), - sort_rank: 100, - casing_color: Vec4f::new(0.0, 0.0, 0.5, 1.0), - casing_width: 1.5, - casing_shape_id: 0.0, - center_color: Vec4f::new(0.0, 0.0, 1.0, 1.0), - center_width: 1.0, - center_shape_id: 0.0, - }; - - let template = stroke_template_from_waterway_rule(&rule); - assert_eq!(template.sort_rank, 100); - assert!(template.casing.is_some()); - assert_eq!(template.casing.unwrap().width, 1.5); - assert_eq!(template.center.width, 1.0); - } - - #[test] - fn test_stroke_template_from_rail_rule() { - let rule = MapRailRule { - sort_rank: 120, - casing_color: Vec4f::new(0.3, 0.3, 0.3, 1.0), - casing_width: 1.0, - casing_shape_id: 0.0, - center_color: Vec4f::new(0.5, 0.5, 0.5, 1.0), - center_width: 0.5, - center_shape_id: 10.0, // Dashed line - }; - - let template = stroke_template_from_rail_rule(&rule); - assert_eq!(template.sort_rank, 120); - assert!(template.casing.is_some()); - assert_eq!(template.casing.unwrap().width, 1.0); - assert_eq!(template.center.width, 0.5); - assert_eq!(template.center.shape_id, 10.0); - } - - #[test] - fn test_scaled_style() { - let template = StrokeTemplate { - sort_rank: 80, - casing: Some(StrokePassStyle { - color: 0x808080, - width: 2.0, - shape_id: 0.0, - }), - center: StrokePassStyle { - color: 0xFFFFFF, - width: 1.0, - shape_id: 0.0, - }, - }; - - let style = scaled_style(template, 10, 1.5); - assert_eq!(style.sort_rank, 90); // 80 + 10 - assert!(style.casing.is_some()); - assert_eq!(style.casing.unwrap().width, 3.0); // 2.0 * 1.5 - assert_eq!(style.center.width, 1.5); // 1.0 * 1.5 - } - - #[test] - fn test_stroke_style_for_tags_highway() { - let mut theme = CompiledMapTheme::default(); - theme.road_rules.insert( - "primary".to_string(), - StrokeTemplate { - sort_rank: 80, - casing: Some(StrokePassStyle { - color: 0x808080, - width: 2.0, - shape_id: 0.0, - }), - center: StrokePassStyle { - color: 0xFFFFFF, - width: 1.0, - shape_id: 0.0, - }, - }, - ); - - let mut tags = HashMap::new(); - tags.insert("highway".to_string(), "primary".to_string()); - - let style = stroke_style_for_tags(&tags, &theme, 14); - assert!(style.is_some()); - let style = style.unwrap(); - assert_eq!(style.sort_rank, 80); - assert!(style.casing.is_some()); - } - - #[test] - fn test_stroke_style_for_tags_waterway() { - let mut theme = CompiledMapTheme::default(); - theme.waterway_rules.insert( - "river".to_string(), - StrokeTemplate { - sort_rank: 100, - casing: Some(StrokePassStyle { - color: 0x000080, - width: 1.5, - shape_id: 0.0, - }), - center: StrokePassStyle { - color: 0x0000FF, - width: 1.0, - shape_id: 0.0, - }, - }, - ); - - let mut tags = HashMap::new(); - tags.insert("waterway".to_string(), "river".to_string()); - - let style = stroke_style_for_tags(&tags, &theme, 14); - assert!(style.is_some()); - let style = style.unwrap(); - assert_eq!(style.sort_rank, 100); - assert!(style.casing.is_some()); - } - - #[test] - fn test_stroke_style_for_tags_railway() { - let mut theme = CompiledMapTheme::default(); - theme.railway_rule = Some(StrokeTemplate { - sort_rank: 120, - casing: Some(StrokePassStyle { - color: 0x404040, - width: 1.0, - shape_id: 0.0, - }), - center: StrokePassStyle { - color: 0x808080, - width: 0.5, - shape_id: 10.0, - }, - }); - - let mut tags = HashMap::new(); - tags.insert("railway".to_string(), "rail".to_string()); - - let style = stroke_style_for_tags(&tags, &theme, 14); - assert!(style.is_some()); - let style = style.unwrap(); - assert_eq!(style.sort_rank, 120); - assert_eq!(style.center.shape_id, 10.0); - } - - #[test] - fn test_stroke_style_for_tags_unknown() { - let theme = CompiledMapTheme::default(); - let tags = HashMap::new(); - - let style = stroke_style_for_tags(&tags, &theme, 14); - assert!(style.is_none()); - } -} diff --git a/crates/apps/map/src/tessellation.rs b/crates/apps/map/src/tessellation.rs index 850c2bb..cb1ffae 100644 --- a/crates/apps/map/src/tessellation.rs +++ b/crates/apps/map/src/tessellation.rs @@ -595,278 +595,3 @@ fn u32_to_rgba_premul(color: u32, alpha: f32) -> u32 { (r_premul << 24) | (g_premul << 16) | (b_premul << 8) | a_premul } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_lonlat_to_tile_coords_zoom_0() { - // Test at zoom 0 (whole world) - let (x, y) = lonlat_to_tile_coords(-180.0, 85.0511, 0); - assert!((x - 0.0).abs() < 0.01); - assert!((y - 0.0).abs() < 0.01); - - let (x, y) = lonlat_to_tile_coords(180.0, -85.0511, 0); - assert!((x - 256.0).abs() < 0.01); - assert!((y - 256.0).abs() < 0.01); - } - - #[test] - fn test_lonlat_to_tile_coords_zoom_1() { - // Test at zoom 1 - let (x, y) = lonlat_to_tile_coords(-180.0, 85.0511, 1); - assert!((x - 0.0).abs() < 0.01); - assert!((y - 0.0).abs() < 0.01); - - let (x, y) = lonlat_to_tile_coords(0.0, 0.0, 1); - assert!((x - 256.0).abs() < 0.01); - assert!((y - 256.0).abs() < 0.01); - } - - #[test] - fn test_lonlat_to_tile_coords_nairobi() { - // Test Nairobi coordinates at zoom 14 - let (x, y) = lonlat_to_tile_coords(36.8219, -1.2921, 14); - // Nairobi should be at approximately x=9250, y=8247 at zoom 14 - assert!((x - 9250.0).abs() < 10.0); - assert!((y - 8247.0).abs() < 10.0); - } - - #[test] - fn test_calculate_signed_area_triangle() { - // Triangle with vertices at (0,0), (10,0), (0,10) - let points = vec![(0.0, 0.0), (10.0, 0.0), (0.0, 10.0)]; - let area = calculate_signed_area(&points); - // Area should be 50 (positive for counter-clockwise) - assert!((area - 50.0).abs() < 0.01); - } - - #[test] - fn test_calculate_signed_area_clockwise() { - // Triangle with vertices at (0,0), (0,10), (10,0) (clockwise) - let points = vec![(0.0, 0.0), (0.0, 10.0), (10.0, 0.0)]; - let area = calculate_signed_area(&points); - // Area should be -50 (negative for clockwise) - assert!((area - (-50.0)).abs() < 0.01); - } - - #[test] - fn test_calculate_signed_area_square() { - // Square with vertices at (0,0), (10,0), (10,10), (0,10) - let points = vec![(0.0, 0.0), (10.0, 0.0), (10.0, 10.0), (0.0, 10.0)]; - let area = calculate_signed_area(&points); - // Area should be 100 (positive for counter-clockwise) - assert!((area - 100.0).abs() < 0.01); - } - - #[test] - fn test_calculate_signed_area_empty() { - let points = vec![]; - let area = calculate_signed_area(&points); - assert_eq!(area, 0.0); - } - - #[test] - fn test_point_in_polygon_inside() { - // Square with vertices at (0,0), (10,0), (10,10), (0,10) - let polygon = vec![(0.0, 0.0), (10.0, 0.0), (10.0, 10.0), (0.0, 10.0)]; - assert!(point_in_polygon((5.0, 5.0), &polygon)); - } - - #[test] - fn test_point_in_polygon_outside() { - // Square with vertices at (0,0), (10,0), (10,10), (0,10) - let polygon = vec![(0.0, 0.0), (10.0, 0.0), (10.0, 10.0), (0.0, 10.0)]; - assert!(!point_in_polygon((15.0, 5.0), &polygon)); - } - - #[test] - fn test_point_in_polygon_on_edge() { - // Square with vertices at (0,0), (10,0), (10,10), (0,10) - let polygon = vec![(0.0, 0.0), (10.0, 0.0), (10.0, 10.0), (0.0, 10.0)]; - // Point on edge is considered inside - assert!(point_in_polygon((5.0, 0.0), &polygon)); - } - - #[test] - fn test_classify_polygon_rings_simple() { - // Simple polygon with one outer ring - let rings = vec![FillRing { - points: vec![(0.0, 0.0), (10.0, 0.0), (10.0, 10.0), (0.0, 10.0)], - signed_area: 100.0, - }]; - let classified = classify_polygon_rings(&rings); - assert_eq!(classified.len(), 1); - assert_eq!(classified[0].outer.points.len(), 4); - assert!(classified[0].inners.is_empty()); - } - - #[test] - fn test_classify_polygon_rings_with_hole() { - // Polygon with outer ring and one hole - let rings = vec![ - FillRing { - points: vec![(0.0, 0.0), (20.0, 0.0), (20.0, 20.0), (0.0, 20.0)], - signed_area: 400.0, // Outer ring (positive area) - }, - FillRing { - points: vec![(5.0, 5.0), (15.0, 5.0), (15.0, 15.0), (5.0, 15.0)], - signed_area: -100.0, // Inner ring (negative area) - }, - ]; - let classified = classify_polygon_rings(&rings); - assert_eq!(classified.len(), 1); - assert_eq!(classified[0].outer.points.len(), 4); - assert_eq!(classified[0].inners.len(), 1); - } - - #[test] - fn test_classify_polygon_rings_empty() { - let rings = vec![]; - let classified = classify_polygon_rings(&rings); - assert!(classified.is_empty()); - } - - #[test] - fn test_extract_way_label_with_name() { - let mut tags = HashMap::new(); - tags.insert("name".to_string(), "Main Street".to_string()); - tags.insert("highway".to_string(), "primary".to_string()); - let points = vec![(0.0, 0.0), (100.0, 0.0)]; - let label = extract_way_label(&tags, &points); - assert!(label.is_some()); - assert_eq!(label.unwrap().text, "Main Street"); - } - - #[test] - fn test_extract_way_label_without_name() { - let mut tags = HashMap::new(); - tags.insert("highway".to_string(), "primary".to_string()); - let points = vec![(0.0, 0.0), (100.0, 0.0)]; - let label = extract_way_label(&tags, &points); - assert!(label.is_none()); - } - - #[test] - fn test_extract_way_label_short_way() { - let mut tags = HashMap::new(); - tags.insert("name".to_string(), "Main Street".to_string()); - tags.insert("highway".to_string(), "primary".to_string()); - let points = vec![(0.0, 0.0), (10.0, 0.0)]; // Too short - let label = extract_way_label(&tags, &points); - assert!(label.is_none()); - } - - #[test] - fn test_get_label_priority_motorway() { - let mut tags = HashMap::new(); - tags.insert("highway".to_string(), "motorway".to_string()); - let priority = get_label_priority(&tags); - assert_eq!(priority, 100); - } - - #[test] - fn test_get_label_priority_primary() { - let mut tags = HashMap::new(); - tags.insert("highway".to_string(), "primary".to_string()); - let priority = get_label_priority(&tags); - assert_eq!(priority, 80); - } - - #[test] - fn test_get_label_priority_residential() { - let mut tags = HashMap::new(); - tags.insert("highway".to_string(), "residential".to_string()); - let priority = get_label_priority(&tags); - assert_eq!(priority, 40); - } - - #[test] - fn test_get_label_priority_unknown() { - let tags = HashMap::new(); - let priority = get_label_priority(&tags); - assert_eq!(priority, 0); - } - - #[test] - fn test_compact_labels_dedup() { - let mut labels = vec![ - TileLabel { - text: "Main Street".to_string(), - x: 100.0, - y: 100.0, - priority: 80, - }, - TileLabel { - text: "Main Street".to_string(), - x: 105.0, - y: 105.0, - priority: 80, - }, - ]; - compact_labels(&mut labels); - assert_eq!(labels.len(), 1); // Should deduplicate - } - - #[test] - fn test_compact_labels_keep_different() { - let mut labels = vec![ - TileLabel { - text: "Main Street".to_string(), - x: 100.0, - y: 100.0, - priority: 80, - }, - TileLabel { - text: "Oak Avenue".to_string(), - x: 200.0, - y: 200.0, - priority: 60, - }, - ]; - compact_labels(&mut labels); - assert_eq!(labels.len(), 2); // Should keep both - } - - #[test] - fn test_compact_labels_empty() { - let mut labels = vec![]; - compact_labels(&mut labels); - assert!(labels.is_empty()); - } - - #[test] - fn test_u32_to_rgba_premul_opaque() { - // Opaque red: 0xFF0000FF - let color = 0xFF0000FF; - let result = u32_to_rgba_premul(color, 1.0); - // Should be 0xFF0000FF (opaque red) - assert_eq!(result, 0xFF0000FF); - } - - #[test] - fn test_u32_to_rgba_premul_semitransparent() { - // Opaque red with 50% alpha - let color = 0xFF0000FF; - let result = u32_to_rgba_premul(color, 0.5); - // Should be 0x80000080 (50% alpha red, premultiplied) - let r = (result >> 24) & 0xFF; - let g = (result >> 16) & 0xFF; - let b = (result >> 8) & 0xFF; - let a = result & 0xFF; - assert!((r as i32 - 128).abs() < 2); // ~128 (50% of 255) - assert_eq!(g, 0); - assert_eq!(b, 0); - assert!((a as i32 - 128).abs() < 2); // ~128 (50% of 255) - } - - #[test] - fn test_u32_to_rgba_premul_transparent() { - // Opaque red with 0% alpha - let color = 0xFF0000FF; - let result = u32_to_rgba_premul(color, 0.0); - // Should be 0x00000000 (fully transparent) - assert_eq!(result, 0x00000000); - } -}