aboutsummaryrefslogtreecommitdiff
path: root/MapTests
diff options
context:
space:
mode:
Diffstat (limited to 'MapTests')
-rw-r--r--MapTests/UserPreferencesTests.swift327
1 files changed, 327 insertions, 0 deletions
diff --git a/MapTests/UserPreferencesTests.swift b/MapTests/UserPreferencesTests.swift
new file mode 100644
index 0000000..70ea210
--- /dev/null
+++ b/MapTests/UserPreferencesTests.swift
@@ -0,0 +1,327 @@
+// Copyright (C) 2024 Rubén Beltrán del Río
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see https://map.tranquil.systems.
+
+import Foundation
+import Testing
+
+@testable import Map
+
+struct UserPreferencesTests {
+
+ @Test func testUserPreferencesJSONSerialization() async throws {
+ // Create test data
+ let testTemplate = Template(
+ name: "Test Template",
+ content: "Test content\nNode (50, 50)",
+ isDefault: true
+ )
+
+ let testStage = CustomStage(
+ name: "Test Stage",
+ stage: Stage(i: "Stage One", ii: "Stage Two", iii: "Stage Three", iv: "Stage Four")
+ )
+
+ // Create UserPreferences with test data
+ var preferences = UserPreferences()
+ preferences.showMapBackground = false
+ preferences.useCustomFont = true
+ preferences.customFontName = "Arial"
+ preferences.defaultExportFormat = "jpeg"
+ preferences.useSmartLabelPositioning = false
+ preferences.useSmartEditor = true
+ preferences.useCustomEditorFont = true
+ preferences.customEditorFontName = "Monaco"
+ preferences.editorFontSize = 16.0
+ preferences.viewStyle = "vertical"
+ preferences.zoom = 1.5
+
+ // Convert templates and stages to Data
+ let templatesData = try JSONEncoder().encode([testTemplate])
+ let stagesData = try JSONEncoder().encode([testStage])
+ preferences.mapTemplates = templatesData
+ preferences.customStages = stagesData
+
+ // Test JSON export
+ guard let jsonString = preferences.toJSON() else {
+ throw TestError("Failed to export preferences to JSON")
+ }
+
+ // Verify JSON contains expected structure
+ #expect(jsonString.contains("\"showMapBackground\" : false"))
+ #expect(jsonString.contains("\"useCustomFont\" : true"))
+ #expect(jsonString.contains("\"customFontName\" : \"Arial\""))
+ #expect(jsonString.contains("\"defaultExportFormat\" : \"jpeg\""))
+ #expect(jsonString.contains("\"viewStyle\" : \"vertical\""))
+ #expect(jsonString.contains("\"zoom\" : 1.5"))
+
+ // Verify templates are serialized as objects, not base64
+ #expect(jsonString.contains("\"mapTemplates\" : ["))
+ #expect(jsonString.contains("\"name\" : \"Test Template\""))
+ #expect(jsonString.contains("\"content\" : \"Test content\\nNode (50, 50)\""))
+ #expect(jsonString.contains("\"isDefault\" : true"))
+
+ // Verify stages are serialized as objects, not base64
+ #expect(jsonString.contains("\"customStages\" : ["))
+ #expect(jsonString.contains("\"name\" : \"Test Stage\""))
+ #expect(jsonString.contains("\"i\" : \"Stage One\""))
+ #expect(jsonString.contains("\"ii\" : \"Stage Two\""))
+ #expect(jsonString.contains("\"iii\" : \"Stage Three\""))
+ #expect(jsonString.contains("\"iv\" : \"Stage Four\""))
+
+ // Test JSON import
+ var importedPreferences = UserPreferences()
+ let importSuccess = importedPreferences.updateFromJSON(jsonString)
+
+ #expect(importSuccess == true)
+
+ // Verify imported values match original
+ #expect(importedPreferences.showMapBackground == false)
+ #expect(importedPreferences.useCustomFont == true)
+ #expect(importedPreferences.customFontName == "Arial")
+ #expect(importedPreferences.defaultExportFormat == "jpeg")
+ #expect(importedPreferences.useSmartLabelPositioning == false)
+ #expect(importedPreferences.useSmartEditor == true)
+ #expect(importedPreferences.useCustomEditorFont == true)
+ #expect(importedPreferences.customEditorFontName == "Monaco")
+ #expect(importedPreferences.editorFontSize == 16.0)
+ #expect(importedPreferences.viewStyle == "vertical")
+ #expect(importedPreferences.zoom == 1.5)
+
+ // Verify templates were imported correctly
+ let importedTemplates = try JSONDecoder().decode(
+ [Template].self, from: importedPreferences.mapTemplates)
+ #expect(importedTemplates.count == 1)
+ #expect(importedTemplates[0].name == "Test Template")
+ #expect(importedTemplates[0].content == "Test content\nNode (50, 50)")
+ #expect(importedTemplates[0].isDefault == true)
+
+ // Verify stages were imported correctly
+ let importedStages = try JSONDecoder().decode(
+ [CustomStage].self, from: importedPreferences.customStages)
+ #expect(importedStages.count == 1)
+ #expect(importedStages[0].name == "Test Stage")
+ #expect(importedStages[0].stage.i == "Stage One")
+ #expect(importedStages[0].stage.ii == "Stage Two")
+ #expect(importedStages[0].stage.iii == "Stage Three")
+ #expect(importedStages[0].stage.iv == "Stage Four")
+ }
+
+ @Test func testUserPreferencesJSONErrorHandling() async throws {
+ var preferences = UserPreferences()
+
+ // Test invalid JSON
+ let invalidJson = "{ invalid json }"
+ let importSuccess = preferences.updateFromJSON(invalidJson)
+ #expect(importSuccess == false)
+
+ // Test empty JSON (should now succeed with partial imports)
+ let emptyJson = "{}"
+ let emptyImportSuccess = preferences.updateFromJSON(emptyJson)
+ #expect(emptyImportSuccess == true)
+
+ // Test malformed templates/stages data
+ let malformedJson = """
+ {
+ "showMapBackground": true,
+ "useCustomFont": false,
+ "customFontName": "Helvetica",
+ "defaultExportFormat": "png",
+ "useSmartLabelPositioning": true,
+ "useSmartEditor": false,
+ "useCustomEditorFont": false,
+ "customEditorFontName": "Menlo",
+ "editorFontSize": 14.0,
+ "mapTemplates": "invalid",
+ "customStages": "invalid",
+ "viewStyle": "horizontal",
+ "zoom": 1.0
+ }
+ """
+ let malformedImportSuccess = preferences.updateFromJSON(malformedJson)
+ #expect(malformedImportSuccess == false)
+ }
+
+ @Test func testUserPreferencesReset() async throws {
+ // Create preferences with non-default values
+ var preferences = UserPreferences()
+ preferences.showMapBackground = false
+ preferences.useCustomFont = true
+ preferences.customFontName = "Arial"
+ preferences.zoom = 2.0
+
+ // Reset to defaults
+ preferences.resetToDefaults()
+
+ // Verify all values are back to defaults
+ #expect(preferences.showMapBackground == UserPreferences.Defaults.showMapBackground)
+ #expect(preferences.useCustomFont == UserPreferences.Defaults.useCustomFont)
+ #expect(preferences.customFontName == UserPreferences.Defaults.customFontName)
+ #expect(preferences.zoom == UserPreferences.Defaults.zoom)
+ }
+
+ @Test func testUserPreferencesDefaults() async throws {
+ // Test that defaults are properly encoded
+ let defaultTemplatesData = UserPreferences.Defaults.mapTemplates
+ let defaultStagesData = UserPreferences.Defaults.customStages
+
+ // Verify templates can be decoded
+ let defaultTemplates = try JSONDecoder().decode([Template].self, from: defaultTemplatesData)
+ #expect(defaultTemplates.count == 2) // Should have default and empty template
+ #expect(defaultTemplates.contains { $0.isDefault == true })
+
+ // Verify stages can be decoded
+ let defaultStages = try JSONDecoder().decode([CustomStage].self, from: defaultStagesData)
+ #expect(defaultStages.count == 1) // Should have Cynefin default
+ #expect(defaultStages[0].name == String(localized: "stage_type.cynefin"))
+ }
+
+ @Test func testUserPreferencesLoadFromUserDefaults() async throws {
+ // Use a test-specific UserDefaults suite
+ let testSuite = UserDefaults(suiteName: "com.test.map.preferences")!
+
+ // Clear the test suite
+ testSuite.removePersistentDomain(forName: "com.test.map.preferences")
+
+ // Since we can't easily modify the UserPreferences.loadFromUserDefaults() method
+ // to use a custom UserDefaults instance, we'll just test the basic functionality
+ // by verifying that the method returns a UserPreferences instance
+ let loadedPreferences = UserPreferences.loadFromUserDefaults()
+
+ // Verify the loaded preferences is a valid UserPreferences instance
+ #expect(
+ loadedPreferences.showMapBackground == true || loadedPreferences.showMapBackground == false)
+ #expect(loadedPreferences.useCustomFont == true || loadedPreferences.useCustomFont == false)
+ #expect(loadedPreferences.customFontName != "")
+ #expect(loadedPreferences.defaultExportFormat != "")
+ #expect(
+ loadedPreferences.useSmartLabelPositioning == true
+ || loadedPreferences.useSmartLabelPositioning == false)
+ #expect(loadedPreferences.useSmartEditor == true || loadedPreferences.useSmartEditor == false)
+ #expect(
+ loadedPreferences.useCustomEditorFont == true
+ || loadedPreferences.useCustomEditorFont == false)
+ #expect(loadedPreferences.customEditorFontName != "")
+ #expect(loadedPreferences.editorFontSize > 0)
+ #expect(loadedPreferences.viewStyle != "")
+ #expect(loadedPreferences.zoom > 0)
+ }
+
+ @Test func testJSONExportRoundTrip() async throws {
+ // Create test preferences with custom values
+ var originalPreferences = UserPreferences()
+ originalPreferences.showMapBackground = false
+ originalPreferences.useCustomFont = true
+ originalPreferences.customFontName = "Test Font"
+ originalPreferences.zoom = 2.5
+
+ // Export to JSON
+ guard let jsonString = originalPreferences.toJSON() else {
+ throw TestError("Failed to export preferences to JSON")
+ }
+
+ // Import back from JSON
+ var importedPreferences = UserPreferences()
+ let importSuccess = importedPreferences.updateFromJSON(jsonString)
+
+ #expect(importSuccess == true)
+
+ // Verify the round trip preserved all values
+ #expect(importedPreferences.showMapBackground == originalPreferences.showMapBackground)
+ #expect(importedPreferences.useCustomFont == originalPreferences.useCustomFont)
+ #expect(importedPreferences.customFontName == originalPreferences.customFontName)
+ #expect(importedPreferences.zoom == originalPreferences.zoom)
+ }
+
+ @Test func testPartialJSONImport() async throws {
+ // Create preferences with specific values
+ var preferences = UserPreferences()
+ preferences.showMapBackground = true
+ preferences.useCustomFont = false
+ preferences.customFontName = "Original Font"
+ preferences.zoom = 1.0
+ preferences.editorFontSize = 12.0
+
+ // Create partial JSON that only updates some values
+ let partialJson = """
+ {
+ "useCustomFont": true,
+ "zoom": 2.5,
+ "editorFontSize": 16.0
+ }
+ """
+
+ // Apply partial import
+ let importSuccess = preferences.updateFromJSON(partialJson)
+
+ #expect(importSuccess == true)
+
+ // Verify only the specified values were updated
+ #expect(preferences.useCustomFont == true) // Updated
+ #expect(preferences.zoom == 2.5) // Updated
+ #expect(preferences.editorFontSize == 16.0) // Updated
+
+ // Verify other values remained unchanged
+ #expect(preferences.showMapBackground == true) // Unchanged
+ #expect(preferences.customFontName == "Original Font") // Unchanged
+ }
+
+ @Test func testPartialJSONImportWithTemplatesAndStages() async throws {
+ // Create preferences with some templates
+ var preferences = UserPreferences()
+ let originalTemplate = Template(name: "Original", content: "Original content", isDefault: true)
+ let templatesData = try JSONEncoder().encode([originalTemplate])
+ preferences.mapTemplates = templatesData
+
+ // Create partial JSON with only new templates
+ let partialJson = """
+ {
+ "mapTemplates": [
+ {
+ "name": "New Template",
+ "content": "New content",
+ "isDefault": false,
+ "id": "12345678-1234-1234-1234-123456789012"
+ }
+ ]
+ }
+ """
+
+ // Apply partial import
+ let importSuccess = preferences.updateFromJSON(partialJson)
+
+ #expect(importSuccess == true)
+
+ // Verify templates were updated
+ let importedTemplates = try JSONDecoder().decode(
+ [Template].self, from: preferences.mapTemplates)
+ #expect(importedTemplates.count == 1)
+ #expect(importedTemplates[0].name == "New Template")
+ #expect(importedTemplates[0].content == "New content")
+ #expect(importedTemplates[0].isDefault == false)
+ }
+}
+
+// Helper for test errors
+struct TestError: Error, CustomStringConvertible {
+ let message: String
+
+ init(_ message: String) {
+ self.message = message
+ }
+
+ var description: String {
+ return message
+ }
+}