From eaa49228da1d9a5f112c9bf5b70a69c82339f532 Mon Sep 17 00:00:00 2001 From: Dave Gallagher Date: Thu, 15 Mar 2018 15:29:28 -0700 Subject: [PATCH] QuizTrain 1.0.0 --- .gitignore | 4 + .swiftlint.yml | 12 + Entities.png | Bin 0 -> 933883 bytes LICENSE | 7 + QuizTrain.xcodeproj/project.pbxproj | 2882 +++++++++ .../xcschemes/QuizTrain-iOS.xcscheme | 102 + .../xcschemes/QuizTrain-macOS.xcscheme | 102 + .../xcschemes/QuizTrain-tvOS.xcscheme | 102 + .../xcschemes/QuizTrain-watchOS.xcscheme | 83 + .../xcschemes/QuizTrainTests-iOS.xcscheme | 59 + .../xcschemes/QuizTrainTests-macOS.xcscheme | 59 + .../xcschemes/QuizTrainTests-tvOS.xcscheme | 59 + QuizTrain/.swiftlint.yml | 0 QuizTrain/Info.plist | 24 + QuizTrain/Misc/Add/AddRequestJSON.swift | 16 + QuizTrain/Misc/Add/AddRequestJSONKeys.swift | 7 + .../Containers/CustomFieldsContainer.swift | 67 + .../Containers/ErrorContainer.swift | 44 + .../Containers/JSONDictionaryContainer.swift | 35 + .../Containment/Protocols/CustomFields.swift | 13 + .../Protocols/MutableCustomFields.swift | 18 + QuizTrain/Misc/Debug/DebugDescription.swift | 6 + QuizTrain/Misc/Debug/DebugDetails.swift | 6 + .../Misc/Errors/MultipleMatchError.swift | 34 + QuizTrain/Misc/Errors/SingleMatchError.swift | 29 + .../Extensions/Array+ContentComparison.swift | 47 + QuizTrain/Misc/Extensions/Date+Seconds.swift | 17 + .../Extensions/Equatable+OptionalArray.swift | 13 + QuizTrain/Misc/Identity/Identifiable.swift | 8 + QuizTrain/Misc/JSON/JSONDeserializable.swift | 35 + QuizTrain/Misc/JSON/JSONDictionary.swift | 1 + QuizTrain/Misc/JSON/JSONKey.swift | 1 + QuizTrain/Misc/JSON/JSONSerializable.swift | 30 + .../Misc/Operations/AsyncOperation.swift | 38 + QuizTrain/Misc/Outcome.swift | 9 + QuizTrain/Misc/QueryItemProvider.swift | 21 + QuizTrain/Misc/UniqueSelection.swift | 36 + QuizTrain/Misc/Update/UpdateRequestJSON.swift | 16 + .../Misc/Update/UpdateRequestJSONKeys.swift | 7 + QuizTrain/Misc/Validation/Validatable.swift | 7 + QuizTrain/Models/Case.swift | 188 + QuizTrain/Models/CaseField.swift | 113 + QuizTrain/Models/CaseType.swift | 57 + QuizTrain/Models/Config/Config.Context.swift | 56 + QuizTrain/Models/Config/Config.swift | 91 + QuizTrain/Models/Configuration.swift | 79 + QuizTrain/Models/ConfigurationGroup.swift | 88 + QuizTrain/Models/Milestone.swift | 178 + QuizTrain/Models/Plan.Entry.swift | 90 + QuizTrain/Models/Plan.swift | 216 + QuizTrain/Models/Priority.swift | 67 + QuizTrain/Models/Project.swift | 107 + QuizTrain/Models/Result.swift | 126 + QuizTrain/Models/ResultField.swift | 113 + QuizTrain/Models/Run.swift | 240 + QuizTrain/Models/Section.swift | 105 + QuizTrain/Models/Status.swift | 87 + QuizTrain/Models/Suite.swift | 118 + QuizTrain/Models/Template.swift | 57 + QuizTrain/Models/Test.swift | 156 + QuizTrain/Models/Types/CustomFieldType.swift | 48 + .../Models/Types/Project.SuiteMode.swift | 20 + QuizTrain/Models/User.swift | 62 + QuizTrain/Network/API.swift | 676 +++ .../API/API.RequestErrorDebug.swift | 71 + .../API/API.RequestResultDebug.swift | 29 + .../ObjectAPI.ClientErrorDebug.swift | 23 + .../ObjectAPI.DataProcessingErrorDebug.swift | 56 + .../ObjectAPI.DataRequestErrorDebug.swift | 33 + .../ObjectAPI/ObjectAPI.MatchErrorDebug.swift | 59 + ...ObjectAPI.ObjectConversionErrorDebug.swift | 56 + .../ObjectAPI.RequestErrorDebug.swift | 29 + .../ObjectAPI.ServerErrorDebug.swift | 23 + .../ObjectAPI.StatusCodeErrorDebug.swift | 33 + .../ObjectAPI.UpdateRequestErrorDebug.swift | 37 + .../Network/Extensions/URLRequestDebug.swift | 44 + QuizTrain/Network/Filters/Filter.Value.swift | 59 + QuizTrain/Network/Filters/Filter.swift | 54 + QuizTrain/Network/Models/Add/NewCase.swift | 102 + .../Models/Add/NewCaseResults.Result.swift | 120 + .../Network/Models/Add/NewCaseResults.swift | 62 + .../Network/Models/Add/NewConfiguration.swift | 49 + .../Models/Add/NewConfigurationGroup.swift | 49 + .../Network/Models/Add/NewMilestone.swift | 73 + .../Models/Add/NewPlan.Entry.Run.swift | 120 + .../Network/Models/Add/NewPlan.Entry.swift | 131 + QuizTrain/Network/Models/Add/NewPlan.swift | 75 + QuizTrain/Network/Models/Add/NewProject.swift | 67 + QuizTrain/Network/Models/Add/NewResult.swift | 113 + QuizTrain/Network/Models/Add/NewRun.swift | 85 + QuizTrain/Network/Models/Add/NewSection.swift | 67 + QuizTrain/Network/Models/Add/NewSuite.swift | 55 + .../Models/Add/NewTestResults.Result.swift | 120 + .../Network/Models/Add/NewTestResults.swift | 62 + .../Models/Update/UpdatePlanEntryRuns.swift | 70 + QuizTrain/Network/ObjectAPI.swift | 3032 ++++++++++ .../GetConfigurationGroupsOperation.swift | 46 + .../Operations/GetProjectOperation.swift | 46 + .../Operations/GetTemplatesOperation.swift | 46 + QuizTrain/QuizTrain.h | 37 + QuizTrainTests/.swiftlint.yml | 3 + QuizTrainTests/Info.plist | 22 + .../CustomFieldsContainerTests.swift | 228 + .../Containers/ErrorContainerTests.swift | 84 + .../JSONDictionaryContainerTests.swift | 141 + .../Array+ContentComparisonTests.swift | 42 + .../Misc/Extensions/Array+RandomTests.swift | 40 + .../Equatable+OptionalArrayTests.swift | 25 + .../Misc/Operations/AsyncOperationTests.swift | 26 + QuizTrainTests/Models/CaseFieldTests.swift | 190 + QuizTrainTests/Models/CaseTests.swift | 278 + QuizTrainTests/Models/CaseTypeTests.swift | 146 + .../Models/ConfigurationGroupTests.swift | 157 + .../Models/ConfigurationTests.swift | 152 + .../Custom Fields/Config.ContextTests.swift | 145 + .../Models/Custom Fields/ConfigTests.swift | 182 + QuizTrainTests/Models/MilestoneTests.swift | 239 + QuizTrainTests/Models/Plan.EntryTests.swift | 157 + QuizTrainTests/Models/PlanTests.swift | 278 + QuizTrainTests/Models/PriorityTests.swift | 156 + QuizTrainTests/Models/ProjectTests.swift | 198 + QuizTrainTests/Models/ResultFieldTests.swift | 190 + QuizTrainTests/Models/ResultTests.swift | 201 + QuizTrainTests/Models/RunTests.swift | 307 + QuizTrainTests/Models/SectionTests.swift | 186 + QuizTrainTests/Models/StatusTests.swift | 174 + QuizTrainTests/Models/SuiteTests.swift | 194 + QuizTrainTests/Models/TemplateTests.swift | 146 + QuizTrainTests/Models/TestTests.swift | 215 + .../Models/Testing Protocols/ModelTests.swift | 1 + .../Models/Types/CustomFieldTypeTests.swift | 48 + .../Models/Types/Project.SuiteModeTests.swift | 30 + .../Models/Types/UniqueSelectionTests.swift | 34 + QuizTrainTests/Models/UserTests.swift | 149 + .../Network/Filters/FilterTests.swift | 168 + .../Add/NewCaseResults.ResultTests.swift | 220 + .../Models/Add/NewCaseResultsTests.swift | 117 + .../Network/Models/Add/NewCaseTests.swift | 193 + .../Add/NewConfigurationGroupTests.swift | 88 + .../Models/Add/NewConfigurationTests.swift | 88 + .../Models/Add/NewMilestoneTests.swift | 136 + .../Models/Add/NewPlan.Entry.RunTests.swift | 161 + .../Models/Add/NewPlan.EntryTests.swift | 202 + .../Network/Models/Add/NewPlanTests.swift | 127 + .../Network/Models/Add/NewProjectTests.swift | 123 + .../Network/Models/Add/NewResultTests.swift | 211 + .../Network/Models/Add/NewRunTests.swift | 154 + .../Network/Models/Add/NewSectionTests.swift | 127 + .../Network/Models/Add/NewSuiteTests.swift | 109 + .../Add/NewTestResults.ResultTests.swift | 220 + .../Models/Add/NewTestResultsTests.swift | 117 + .../Testing Protocols/AddModelTests.swift | 1 + .../Testing Protocols/UpdateModelTests.swift | 1 + .../Update/UpdatePlanEntryRunsTests.swift | 127 + QuizTrainTests/Network/ObjectAPITests.swift | 5274 +++++++++++++++++ QuizTrainTests/README.md | 15 + .../Testing Misc/Array+Random.swift | 19 + .../Testing Misc/TestCredentials.json | 7 + .../Testing Misc/TestCredentials.swift | 59 + .../Asserts/AssertAddRequestJSON.swift | 18 + .../Asserts/AssertCustomFields.swift | 21 + .../Asserts/AssertEquatable.swift | 18 + .../Asserts/AssertJSONDeserializing.swift | 47 + .../Asserts/AssertJSONSerializing.swift | 24 + .../AssertJSONTwoWaySerialization.swift | 87 + .../Asserts/AssertProperties.swift | 8 + .../Asserts/AssertUpdateRequestJSON.swift | 19 + .../Asserts/AssertValidatable.swift | 19 + .../Providers/CustomFieldsDataProvider.swift | 141 + .../Providers/JSONDataProvider.swift | 46 + .../Providers/ObjectProvider.swift | 45 + .../Providers/ValidatableObjectProvider.swift | 7 + .../Tests/AddRequestJSONTests.swift | 18 + .../Tests/EquatableTests.swift | 19 + .../Testing Protocols/Tests/InitTests.swift | 46 + .../Tests/JSONDeserializingTests.swift | 82 + .../Tests/JSONSerializingTests.swift | 26 + .../Tests/JSONTwoWaySerializationTests.swift | 33 + .../Testing Protocols/Tests/README.md | 20 + .../Tests/UpdateRequestJSONTests.swift | 23 + .../Tests/ValidatableTests.swift | 22 + .../Tests/VariablePropertyTests.swift | 17 + README.md | 256 + 183 files changed, 26317 insertions(+) create mode 100644 .gitignore create mode 100644 .swiftlint.yml create mode 100755 Entities.png create mode 100644 LICENSE create mode 100644 QuizTrain.xcodeproj/project.pbxproj create mode 100644 QuizTrain.xcodeproj/xcshareddata/xcschemes/QuizTrain-iOS.xcscheme create mode 100644 QuizTrain.xcodeproj/xcshareddata/xcschemes/QuizTrain-macOS.xcscheme create mode 100644 QuizTrain.xcodeproj/xcshareddata/xcschemes/QuizTrain-tvOS.xcscheme create mode 100644 QuizTrain.xcodeproj/xcshareddata/xcschemes/QuizTrain-watchOS.xcscheme create mode 100644 QuizTrain.xcodeproj/xcshareddata/xcschemes/QuizTrainTests-iOS.xcscheme create mode 100644 QuizTrain.xcodeproj/xcshareddata/xcschemes/QuizTrainTests-macOS.xcscheme create mode 100644 QuizTrain.xcodeproj/xcshareddata/xcschemes/QuizTrainTests-tvOS.xcscheme create mode 100644 QuizTrain/.swiftlint.yml create mode 100644 QuizTrain/Info.plist create mode 100644 QuizTrain/Misc/Add/AddRequestJSON.swift create mode 100644 QuizTrain/Misc/Add/AddRequestJSONKeys.swift create mode 100644 QuizTrain/Misc/Containment/Containers/CustomFieldsContainer.swift create mode 100644 QuizTrain/Misc/Containment/Containers/ErrorContainer.swift create mode 100644 QuizTrain/Misc/Containment/Containers/JSONDictionaryContainer.swift create mode 100644 QuizTrain/Misc/Containment/Protocols/CustomFields.swift create mode 100644 QuizTrain/Misc/Containment/Protocols/MutableCustomFields.swift create mode 100644 QuizTrain/Misc/Debug/DebugDescription.swift create mode 100644 QuizTrain/Misc/Debug/DebugDetails.swift create mode 100644 QuizTrain/Misc/Errors/MultipleMatchError.swift create mode 100644 QuizTrain/Misc/Errors/SingleMatchError.swift create mode 100644 QuizTrain/Misc/Extensions/Array+ContentComparison.swift create mode 100644 QuizTrain/Misc/Extensions/Date+Seconds.swift create mode 100644 QuizTrain/Misc/Extensions/Equatable+OptionalArray.swift create mode 100644 QuizTrain/Misc/Identity/Identifiable.swift create mode 100644 QuizTrain/Misc/JSON/JSONDeserializable.swift create mode 100644 QuizTrain/Misc/JSON/JSONDictionary.swift create mode 100644 QuizTrain/Misc/JSON/JSONKey.swift create mode 100644 QuizTrain/Misc/JSON/JSONSerializable.swift create mode 100644 QuizTrain/Misc/Operations/AsyncOperation.swift create mode 100644 QuizTrain/Misc/Outcome.swift create mode 100644 QuizTrain/Misc/QueryItemProvider.swift create mode 100644 QuizTrain/Misc/UniqueSelection.swift create mode 100644 QuizTrain/Misc/Update/UpdateRequestJSON.swift create mode 100644 QuizTrain/Misc/Update/UpdateRequestJSONKeys.swift create mode 100644 QuizTrain/Misc/Validation/Validatable.swift create mode 100644 QuizTrain/Models/Case.swift create mode 100644 QuizTrain/Models/CaseField.swift create mode 100644 QuizTrain/Models/CaseType.swift create mode 100644 QuizTrain/Models/Config/Config.Context.swift create mode 100644 QuizTrain/Models/Config/Config.swift create mode 100644 QuizTrain/Models/Configuration.swift create mode 100644 QuizTrain/Models/ConfigurationGroup.swift create mode 100644 QuizTrain/Models/Milestone.swift create mode 100644 QuizTrain/Models/Plan.Entry.swift create mode 100644 QuizTrain/Models/Plan.swift create mode 100644 QuizTrain/Models/Priority.swift create mode 100644 QuizTrain/Models/Project.swift create mode 100644 QuizTrain/Models/Result.swift create mode 100644 QuizTrain/Models/ResultField.swift create mode 100644 QuizTrain/Models/Run.swift create mode 100644 QuizTrain/Models/Section.swift create mode 100644 QuizTrain/Models/Status.swift create mode 100644 QuizTrain/Models/Suite.swift create mode 100644 QuizTrain/Models/Template.swift create mode 100644 QuizTrain/Models/Test.swift create mode 100644 QuizTrain/Models/Types/CustomFieldType.swift create mode 100644 QuizTrain/Models/Types/Project.SuiteMode.swift create mode 100644 QuizTrain/Models/User.swift create mode 100644 QuizTrain/Network/API.swift create mode 100644 QuizTrain/Network/Extensions/API/API.RequestErrorDebug.swift create mode 100644 QuizTrain/Network/Extensions/API/API.RequestResultDebug.swift create mode 100644 QuizTrain/Network/Extensions/ObjectAPI/ObjectAPI.ClientErrorDebug.swift create mode 100644 QuizTrain/Network/Extensions/ObjectAPI/ObjectAPI.DataProcessingErrorDebug.swift create mode 100644 QuizTrain/Network/Extensions/ObjectAPI/ObjectAPI.DataRequestErrorDebug.swift create mode 100644 QuizTrain/Network/Extensions/ObjectAPI/ObjectAPI.MatchErrorDebug.swift create mode 100644 QuizTrain/Network/Extensions/ObjectAPI/ObjectAPI.ObjectConversionErrorDebug.swift create mode 100644 QuizTrain/Network/Extensions/ObjectAPI/ObjectAPI.RequestErrorDebug.swift create mode 100644 QuizTrain/Network/Extensions/ObjectAPI/ObjectAPI.ServerErrorDebug.swift create mode 100644 QuizTrain/Network/Extensions/ObjectAPI/ObjectAPI.StatusCodeErrorDebug.swift create mode 100644 QuizTrain/Network/Extensions/ObjectAPI/ObjectAPI.UpdateRequestErrorDebug.swift create mode 100644 QuizTrain/Network/Extensions/URLRequestDebug.swift create mode 100644 QuizTrain/Network/Filters/Filter.Value.swift create mode 100644 QuizTrain/Network/Filters/Filter.swift create mode 100644 QuizTrain/Network/Models/Add/NewCase.swift create mode 100644 QuizTrain/Network/Models/Add/NewCaseResults.Result.swift create mode 100644 QuizTrain/Network/Models/Add/NewCaseResults.swift create mode 100644 QuizTrain/Network/Models/Add/NewConfiguration.swift create mode 100644 QuizTrain/Network/Models/Add/NewConfigurationGroup.swift create mode 100644 QuizTrain/Network/Models/Add/NewMilestone.swift create mode 100644 QuizTrain/Network/Models/Add/NewPlan.Entry.Run.swift create mode 100644 QuizTrain/Network/Models/Add/NewPlan.Entry.swift create mode 100644 QuizTrain/Network/Models/Add/NewPlan.swift create mode 100644 QuizTrain/Network/Models/Add/NewProject.swift create mode 100644 QuizTrain/Network/Models/Add/NewResult.swift create mode 100644 QuizTrain/Network/Models/Add/NewRun.swift create mode 100644 QuizTrain/Network/Models/Add/NewSection.swift create mode 100644 QuizTrain/Network/Models/Add/NewSuite.swift create mode 100644 QuizTrain/Network/Models/Add/NewTestResults.Result.swift create mode 100644 QuizTrain/Network/Models/Add/NewTestResults.swift create mode 100644 QuizTrain/Network/Models/Update/UpdatePlanEntryRuns.swift create mode 100644 QuizTrain/Network/ObjectAPI.swift create mode 100644 QuizTrain/Network/Operations/GetConfigurationGroupsOperation.swift create mode 100644 QuizTrain/Network/Operations/GetProjectOperation.swift create mode 100644 QuizTrain/Network/Operations/GetTemplatesOperation.swift create mode 100644 QuizTrain/QuizTrain.h create mode 100644 QuizTrainTests/.swiftlint.yml create mode 100644 QuizTrainTests/Info.plist create mode 100644 QuizTrainTests/Misc/Containment/Containers/CustomFieldsContainerTests.swift create mode 100644 QuizTrainTests/Misc/Containment/Containers/ErrorContainerTests.swift create mode 100644 QuizTrainTests/Misc/Containment/Containers/JSONDictionaryContainerTests.swift create mode 100644 QuizTrainTests/Misc/Extensions/Array+ContentComparisonTests.swift create mode 100644 QuizTrainTests/Misc/Extensions/Array+RandomTests.swift create mode 100644 QuizTrainTests/Misc/Extensions/Equatable+OptionalArrayTests.swift create mode 100644 QuizTrainTests/Misc/Operations/AsyncOperationTests.swift create mode 100644 QuizTrainTests/Models/CaseFieldTests.swift create mode 100644 QuizTrainTests/Models/CaseTests.swift create mode 100644 QuizTrainTests/Models/CaseTypeTests.swift create mode 100644 QuizTrainTests/Models/ConfigurationGroupTests.swift create mode 100644 QuizTrainTests/Models/ConfigurationTests.swift create mode 100644 QuizTrainTests/Models/Custom Fields/Config.ContextTests.swift create mode 100644 QuizTrainTests/Models/Custom Fields/ConfigTests.swift create mode 100644 QuizTrainTests/Models/MilestoneTests.swift create mode 100644 QuizTrainTests/Models/Plan.EntryTests.swift create mode 100644 QuizTrainTests/Models/PlanTests.swift create mode 100644 QuizTrainTests/Models/PriorityTests.swift create mode 100644 QuizTrainTests/Models/ProjectTests.swift create mode 100644 QuizTrainTests/Models/ResultFieldTests.swift create mode 100644 QuizTrainTests/Models/ResultTests.swift create mode 100644 QuizTrainTests/Models/RunTests.swift create mode 100644 QuizTrainTests/Models/SectionTests.swift create mode 100644 QuizTrainTests/Models/StatusTests.swift create mode 100644 QuizTrainTests/Models/SuiteTests.swift create mode 100644 QuizTrainTests/Models/TemplateTests.swift create mode 100644 QuizTrainTests/Models/TestTests.swift create mode 100644 QuizTrainTests/Models/Testing Protocols/ModelTests.swift create mode 100644 QuizTrainTests/Models/Types/CustomFieldTypeTests.swift create mode 100644 QuizTrainTests/Models/Types/Project.SuiteModeTests.swift create mode 100644 QuizTrainTests/Models/Types/UniqueSelectionTests.swift create mode 100644 QuizTrainTests/Models/UserTests.swift create mode 100644 QuizTrainTests/Network/Filters/FilterTests.swift create mode 100644 QuizTrainTests/Network/Models/Add/NewCaseResults.ResultTests.swift create mode 100644 QuizTrainTests/Network/Models/Add/NewCaseResultsTests.swift create mode 100644 QuizTrainTests/Network/Models/Add/NewCaseTests.swift create mode 100644 QuizTrainTests/Network/Models/Add/NewConfigurationGroupTests.swift create mode 100644 QuizTrainTests/Network/Models/Add/NewConfigurationTests.swift create mode 100644 QuizTrainTests/Network/Models/Add/NewMilestoneTests.swift create mode 100644 QuizTrainTests/Network/Models/Add/NewPlan.Entry.RunTests.swift create mode 100644 QuizTrainTests/Network/Models/Add/NewPlan.EntryTests.swift create mode 100644 QuizTrainTests/Network/Models/Add/NewPlanTests.swift create mode 100644 QuizTrainTests/Network/Models/Add/NewProjectTests.swift create mode 100644 QuizTrainTests/Network/Models/Add/NewResultTests.swift create mode 100644 QuizTrainTests/Network/Models/Add/NewRunTests.swift create mode 100644 QuizTrainTests/Network/Models/Add/NewSectionTests.swift create mode 100644 QuizTrainTests/Network/Models/Add/NewSuiteTests.swift create mode 100644 QuizTrainTests/Network/Models/Add/NewTestResults.ResultTests.swift create mode 100644 QuizTrainTests/Network/Models/Add/NewTestResultsTests.swift create mode 100644 QuizTrainTests/Network/Models/Testing Protocols/AddModelTests.swift create mode 100644 QuizTrainTests/Network/Models/Testing Protocols/UpdateModelTests.swift create mode 100644 QuizTrainTests/Network/Models/Update/UpdatePlanEntryRunsTests.swift create mode 100644 QuizTrainTests/Network/ObjectAPITests.swift create mode 100644 QuizTrainTests/README.md create mode 100644 QuizTrainTests/Testing Misc/Array+Random.swift create mode 100644 QuizTrainTests/Testing Misc/TestCredentials.json create mode 100644 QuizTrainTests/Testing Misc/TestCredentials.swift create mode 100644 QuizTrainTests/Testing Protocols/Asserts/AssertAddRequestJSON.swift create mode 100644 QuizTrainTests/Testing Protocols/Asserts/AssertCustomFields.swift create mode 100644 QuizTrainTests/Testing Protocols/Asserts/AssertEquatable.swift create mode 100644 QuizTrainTests/Testing Protocols/Asserts/AssertJSONDeserializing.swift create mode 100644 QuizTrainTests/Testing Protocols/Asserts/AssertJSONSerializing.swift create mode 100644 QuizTrainTests/Testing Protocols/Asserts/AssertJSONTwoWaySerialization.swift create mode 100644 QuizTrainTests/Testing Protocols/Asserts/AssertProperties.swift create mode 100644 QuizTrainTests/Testing Protocols/Asserts/AssertUpdateRequestJSON.swift create mode 100644 QuizTrainTests/Testing Protocols/Asserts/AssertValidatable.swift create mode 100644 QuizTrainTests/Testing Protocols/Providers/CustomFieldsDataProvider.swift create mode 100644 QuizTrainTests/Testing Protocols/Providers/JSONDataProvider.swift create mode 100644 QuizTrainTests/Testing Protocols/Providers/ObjectProvider.swift create mode 100644 QuizTrainTests/Testing Protocols/Providers/ValidatableObjectProvider.swift create mode 100644 QuizTrainTests/Testing Protocols/Tests/AddRequestJSONTests.swift create mode 100644 QuizTrainTests/Testing Protocols/Tests/EquatableTests.swift create mode 100644 QuizTrainTests/Testing Protocols/Tests/InitTests.swift create mode 100644 QuizTrainTests/Testing Protocols/Tests/JSONDeserializingTests.swift create mode 100644 QuizTrainTests/Testing Protocols/Tests/JSONSerializingTests.swift create mode 100644 QuizTrainTests/Testing Protocols/Tests/JSONTwoWaySerializationTests.swift create mode 100644 QuizTrainTests/Testing Protocols/Tests/README.md create mode 100644 QuizTrainTests/Testing Protocols/Tests/UpdateRequestJSONTests.swift create mode 100644 QuizTrainTests/Testing Protocols/Tests/ValidatableTests.swift create mode 100644 QuizTrainTests/Testing Protocols/Tests/VariablePropertyTests.swift create mode 100644 README.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1bcec53 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.DS_Store +xcuserdata/ +*.pbxuser +*.xcworkspace diff --git a/.swiftlint.yml b/.swiftlint.yml new file mode 100644 index 0000000..7f8fff0 --- /dev/null +++ b/.swiftlint.yml @@ -0,0 +1,12 @@ +included: + - QuizTrain + - QuizTrainTests +disabled_rules: + - empty_enum_arguments + - file_length + - function_body_length + - identifier_name + - line_length + - nesting + - type_body_length + - type_name diff --git a/Entities.png b/Entities.png new file mode 100755 index 0000000000000000000000000000000000000000..c5854ae324dc88aeac41de8b5a19d9aa1e537387 GIT binary patch literal 933883 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�RHN!z^GcrPphi`) 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Ȃ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


I(!Y#o2M3rusiy*sh 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%0A`NSOwYLnCaxAGFNrEVN~T^c{$eI( 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

8p&M}_YUeRA{qm*z?~o=F3m|`(8&yH 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

-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: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 J%#3B<{ujj^Q3n73 literal 0 HcmV?d00001 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c24ae68 --- /dev/null +++ b/LICENSE @@ -0,0 +1,7 @@ +Copyright 2018 Venmo + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/QuizTrain.xcodeproj/project.pbxproj b/QuizTrain.xcodeproj/project.pbxproj new file mode 100644 index 0000000..d87fe29 --- /dev/null +++ b/QuizTrain.xcodeproj/project.pbxproj @@ -0,0 +1,2882 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 48; + objects = { + +/* Begin PBXBuildFile section */ + FE018DC71F85708C001A2FEF /* ModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE018DC61F85708C001A2FEF /* ModelTests.swift */; }; + FE0B15471FED689F0009B570 /* ObjectAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D4F1F75956C00DF1039 /* ObjectAPITests.swift */; }; + FE0B15481FED68A00009B570 /* ObjectAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D4F1F75956C00DF1039 /* ObjectAPITests.swift */; }; + FE0B15491FED68A00009B570 /* ObjectAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D4F1F75956C00DF1039 /* ObjectAPITests.swift */; }; + FE11181C1F6C3A4B00D24A5F /* Config.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE11181B1F6C3A4B00D24A5F /* Config.swift */; }; + FE1474D61FAA475A0049DA84 /* Equatable+OptionalArrayTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE1474D51FAA475A0049DA84 /* Equatable+OptionalArrayTests.swift */; }; + FE1474DA1FAA698F0049DA84 /* ObjectAPI.RequestErrorDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE1474D91FAA698F0049DA84 /* ObjectAPI.RequestErrorDebug.swift */; }; + FE1474DC1FAA69AA0049DA84 /* ObjectAPI.DataRequestErrorDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE1474DB1FAA69AA0049DA84 /* ObjectAPI.DataRequestErrorDebug.swift */; }; + FE1474DE1FAA69B70049DA84 /* ObjectAPI.UpdateRequestErrorDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE1474DD1FAA69B70049DA84 /* ObjectAPI.UpdateRequestErrorDebug.swift */; }; + FE1474E21FAA69F70049DA84 /* ObjectAPI.ClientErrorDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE1474E11FAA69F70049DA84 /* ObjectAPI.ClientErrorDebug.swift */; }; + FE1474E51FAA6A0D0049DA84 /* ObjectAPI.ServerErrorDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE1474E41FAA6A0D0049DA84 /* ObjectAPI.ServerErrorDebug.swift */; }; + FE1474E81FAA6A270049DA84 /* ObjectAPI.StatusCodeErrorDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE1474E71FAA6A270049DA84 /* ObjectAPI.StatusCodeErrorDebug.swift */; }; + FE1474EA1FAA6A340049DA84 /* ObjectAPI.DataProcessingErrorDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE1474E91FAA6A340049DA84 /* ObjectAPI.DataProcessingErrorDebug.swift */; }; + FE1474EC1FAA6A5B0049DA84 /* ObjectAPI.ObjectConversionErrorDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE1474EB1FAA6A5B0049DA84 /* ObjectAPI.ObjectConversionErrorDebug.swift */; }; + FE1FB1041F9131B200383724 /* UpdateRequestJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE1FB1031F9131B200383724 /* UpdateRequestJSON.swift */; }; + FE208DEB1FBB69B80065BE88 /* NewTestResults.Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE208DEA1FBB69B80065BE88 /* NewTestResults.Result.swift */; }; + FE208DED1FBB69C60065BE88 /* NewCaseResults.Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE208DEC1FBB69C60065BE88 /* NewCaseResults.Result.swift */; }; + FE208DEF1FBB6B920065BE88 /* NewTestResults.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE208DEE1FBB6B920065BE88 /* NewTestResults.swift */; }; + FE208DF11FBB6BA10065BE88 /* NewCaseResults.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE208DF01FBB6BA10065BE88 /* NewCaseResults.swift */; }; + FE208DF51FBB716B0065BE88 /* NewCaseResults.ResultTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE208DF41FBB716B0065BE88 /* NewCaseResults.ResultTests.swift */; }; + FE208DFA1FBB95150065BE88 /* Validatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE208DF91FBB95150065BE88 /* Validatable.swift */; }; + FE20D03F1FD876820057A45C /* Identifiable.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE20D03E1FD876820057A45C /* Identifiable.swift */; }; + FE2245851F75AC82009F2B2B /* CustomFieldsContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2245841F75AC82009F2B2B /* CustomFieldsContainer.swift */; }; + FE23A9261F59F3A0007E946D /* QuizTrain.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FE23A91C1F59F3A0007E946D /* QuizTrain.framework */; }; + FE23A92D1F59F3A0007E946D /* QuizTrain.h in Headers */ = {isa = PBXBuildFile; fileRef = FE23A91F1F59F3A0007E946D /* QuizTrain.h */; settings = {ATTRIBUTES = (Public, ); }; }; + FE23A9671F59F4B3007E946D /* ResultField.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A9661F59F4B3007E946D /* ResultField.swift */; }; + FE23A9691F59F4BA007E946D /* Run.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A9681F59F4BA007E946D /* Run.swift */; }; + FE23A96B1F59F4C3007E946D /* Section.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A96A1F59F4C3007E946D /* Section.swift */; }; + FE23A96D1F59F4CE007E946D /* Suite.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A96C1F59F4CE007E946D /* Suite.swift */; }; + FE23A96F1F59F4D9007E946D /* Template.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A96E1F59F4D9007E946D /* Template.swift */; }; + FE23A9711F59F4E2007E946D /* Test.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A9701F59F4E2007E946D /* Test.swift */; }; + FE23A9731F59F4EB007E946D /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A9721F59F4EB007E946D /* User.swift */; }; + FE2877EC1FBB979C004503FB /* ValidatableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2877EB1FBB979C004503FB /* ValidatableTests.swift */; }; + FE2877EE1FBB9803004503FB /* AssertValidatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2877ED1FBB9803004503FB /* AssertValidatable.swift */; }; + FE2877F01FBB9A80004503FB /* ValidatableObjectProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2877EF1FBB9A80004503FB /* ValidatableObjectProvider.swift */; }; + FE2877F41FBBBC50004503FB /* NewCaseResultsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE208DF21FBB714B0065BE88 /* NewCaseResultsTests.swift */; }; + FE2877F61FBBBD37004503FB /* NewTestResultsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795BF1FA799A90030C395 /* NewTestResultsTests.swift */; }; + FE2877FA1FBBC47B004503FB /* FilterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2877F91FBBC47B004503FB /* FilterTests.swift */; }; + FE2D018B1FBCE70B00473B84 /* NewPlan.Entry.Run.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2D018A1FBCE70B00473B84 /* NewPlan.Entry.Run.swift */; }; + FE2F1AD51F843AEE00FF9E0C /* AssertJSONDeserializing.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE58F54B1F7EBF24009A1B4E /* AssertJSONDeserializing.swift */; }; + FE2F1AD71F844FCD00FF9E0C /* JSONSerializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2F1AD61F844FCD00FF9E0C /* JSONSerializable.swift */; }; + FE30F11D1F6AE4A300AA7761 /* Milestone.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A95C1F59F450007E946D /* Milestone.swift */; }; + FE30F11E1F6AED8100AA7761 /* ConfigurationGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A9741F5A042D007E946D /* ConfigurationGroup.swift */; }; + FE30F11F1F6AEDF600AA7761 /* Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A95A1F59F443007E946D /* Configuration.swift */; }; + FE331BDE1F6AF98F00F9A653 /* CaseType.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A9581F59F437007E946D /* CaseType.swift */; }; + FE331BDF1F6AFE5D00F9A653 /* CaseField.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A9561F59F42B007E946D /* CaseField.swift */; }; + FE331C1A1F6B3C0200F9A653 /* Case.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A9541F59F415007E946D /* Case.swift */; }; + FE331C1C1F6B406300F9A653 /* CustomFieldType.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE331C1B1F6B406300F9A653 /* CustomFieldType.swift */; }; + FE3795AA1FA7915C0030C395 /* AssertUpdateRequestJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795A91FA7915C0030C395 /* AssertUpdateRequestJSON.swift */; }; + FE3795B21FA799270030C395 /* NewConfigurationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795B11FA799270030C395 /* NewConfigurationTests.swift */; }; + FE3795B41FA799330030C395 /* NewConfigurationGroupTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795B31FA799330030C395 /* NewConfigurationGroupTests.swift */; }; + FE3795B61FA799400030C395 /* NewMilestoneTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795B51FA799400030C395 /* NewMilestoneTests.swift */; }; + FE3795BC1FA7998F0030C395 /* NewProjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795BB1FA7998F0030C395 /* NewProjectTests.swift */; }; + FE3795BE1FA7999C0030C395 /* NewResultTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795BD1FA7999C0030C395 /* NewResultTests.swift */; }; + FE3795C21FA799B70030C395 /* NewTestResults.ResultTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795C11FA799B70030C395 /* NewTestResults.ResultTests.swift */; }; + FE3795C61FA799E30030C395 /* NewSectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795C51FA799E30030C395 /* NewSectionTests.swift */; }; + FE3795C81FA799F50030C395 /* NewSuiteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795C71FA799F50030C395 /* NewSuiteTests.swift */; }; + FE3795CA1FA79A070030C395 /* UpdatePlanEntryRunsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795C91FA79A070030C395 /* UpdatePlanEntryRunsTests.swift */; }; + FE3795CC1FA7A3AD0030C395 /* ObjectProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795CB1FA7A3AD0030C395 /* ObjectProvider.swift */; }; + FE3795CE1FA7A4540030C395 /* AddModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795CD1FA7A4530030C395 /* AddModelTests.swift */; }; + FE3795D21FA7C7960030C395 /* InitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795D11FA7C7960030C395 /* InitTests.swift */; }; + FE3795D41FA7C7A50030C395 /* JSONDeserializingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795D31FA7C7A50030C395 /* JSONDeserializingTests.swift */; }; + FE3795D61FA7C7B60030C395 /* JSONSerializingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795D51FA7C7B60030C395 /* JSONSerializingTests.swift */; }; + FE3795D81FA7C7D00030C395 /* JSONTwoWaySerializationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795D71FA7C7D00030C395 /* JSONTwoWaySerializationTests.swift */; }; + FE3795DA1FA7C7E30030C395 /* VariablePropertyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795D91FA7C7E30030C395 /* VariablePropertyTests.swift */; }; + FE3795DC1FA7C7FA0030C395 /* UpdateRequestJSONTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795DB1FA7C7FA0030C395 /* UpdateRequestJSONTests.swift */; }; + FE3795E11FA7D8360030C395 /* UpdateModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795E01FA7D8360030C395 /* UpdateModelTests.swift */; }; + FE3795E41FA7E1770030C395 /* NewCaseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795AF1FA799150030C395 /* NewCaseTests.swift */; }; + FE3795E61FA7EA6C0030C395 /* AssertAddRequestJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795E51FA7EA6C0030C395 /* AssertAddRequestJSON.swift */; }; + FE3795E81FA7EB380030C395 /* AddRequestJSONTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795E71FA7EB380030C395 /* AddRequestJSONTests.swift */; }; + FE3899171FCF2BDE0032E265 /* GetTemplatesOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3899161FCF2BDE0032E265 /* GetTemplatesOperation.swift */; }; + FE496B5F1F7D9BCF00AE9454 /* ResultFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D4A1F75914E00DF1039 /* ResultFieldTests.swift */; }; + FE4E11D71F7C4112004A315E /* Plan.Entry.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE4E11D61F7C4112004A315E /* Plan.Entry.swift */; }; + FE4F8F391F84361F00447F9E /* NewCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE4F8F381F84361F00447F9E /* NewCase.swift */; }; + FE4F8F3B1F8437DE00447F9E /* JSONDeserializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE4F8F3A1F8437DE00447F9E /* JSONDeserializable.swift */; }; + FE51EFA21F882454007012E0 /* JSONDictionaryContainerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE51EFA11F882454007012E0 /* JSONDictionaryContainerTests.swift */; }; + FE5259121F82D1AD00E0DDB7 /* JSONKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE5259111F82D1AD00E0DDB7 /* JSONKey.swift */; }; + FE549C421FBE5147008CDFCE /* NewPlanTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795B71FA799520030C395 /* NewPlanTests.swift */; }; + FE549C431FBE514A008CDFCE /* NewPlan.EntryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795B91FA7996F0030C395 /* NewPlan.EntryTests.swift */; }; + FE549C441FBE514C008CDFCE /* NewPlan.Entry.RunTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE64D1571FBD0C3700ABA133 /* NewPlan.Entry.RunTests.swift */; }; + FE549C461FBE60F3008CDFCE /* Array+ContentComparisonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE549C451FBE60F3008CDFCE /* Array+ContentComparisonTests.swift */; }; + FE5869D71F7478D600BE5C5C /* CustomFields.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE5869D61F7478D600BE5C5C /* CustomFields.swift */; }; + FE58F5481F7EBF04009A1B4E /* AssertProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE58F5471F7EBF04009A1B4E /* AssertProperties.swift */; }; + FE58F54A1F7EBF12009A1B4E /* AssertCustomFields.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE58F5491F7EBF12009A1B4E /* AssertCustomFields.swift */; }; + FE58F5611F7EE91D009A1B4E /* JSONDataProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE58F5601F7EE91D009A1B4E /* JSONDataProvider.swift */; }; + FE58F5661F7EEA29009A1B4E /* CustomFieldsDataProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE58F5651F7EEA29009A1B4E /* CustomFieldsDataProvider.swift */; }; + FE5A24F21FE092D300198848 /* UniqueSelection.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE5A24F11FE092D300198848 /* UniqueSelection.swift */; }; + FE5A24F41FE099BD00198848 /* Array+Random.swift in Sources */ = {isa = PBXBuildFile; fileRef = FECFE7FC1FD9E13500968EA3 /* Array+Random.swift */; }; + FE5E1A301F8804E3001E479B /* JSONDictionaryContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE5E1A2F1F8804E3001E479B /* JSONDictionaryContainer.swift */; }; + FE63715F1FD61CA500192CED /* GetConfigurationGroupsOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE63715E1FD61CA500192CED /* GetConfigurationGroupsOperation.swift */; }; + FE6989211FA39B99006CC783 /* UpdatePlanEntryRuns.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6989201FA39B99006CC783 /* UpdatePlanEntryRuns.swift */; }; + FE6A6D271F75908100DF1039 /* CaseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D261F75908100DF1039 /* CaseTests.swift */; }; + FE6A6D291F75909600DF1039 /* CaseTypeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D281F75909600DF1039 /* CaseTypeTests.swift */; }; + FE6A6D2B1F7590A100DF1039 /* ConfigurationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D2A1F7590A100DF1039 /* ConfigurationTests.swift */; }; + FE6A6D2D1F7590B000DF1039 /* ConfigurationGroupTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D2C1F7590B000DF1039 /* ConfigurationGroupTests.swift */; }; + FE6A6D2F1F7590BA00DF1039 /* MilestoneTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D2E1F7590BA00DF1039 /* MilestoneTests.swift */; }; + FE6A6D311F7590C700DF1039 /* PlanTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D301F7590C700DF1039 /* PlanTests.swift */; }; + FE6A6D331F7590D200DF1039 /* PriorityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D321F7590D200DF1039 /* PriorityTests.swift */; }; + FE6A6D351F7590DC00DF1039 /* ProjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D341F7590DC00DF1039 /* ProjectTests.swift */; }; + FE6A6D371F7590E500DF1039 /* ResultTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D361F7590E500DF1039 /* ResultTests.swift */; }; + FE6A6D391F7590F000DF1039 /* RunTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D381F7590F000DF1039 /* RunTests.swift */; }; + FE6A6D3B1F7590F900DF1039 /* SectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D3A1F7590F900DF1039 /* SectionTests.swift */; }; + FE6A6D3D1F75910500DF1039 /* SuiteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D3C1F75910500DF1039 /* SuiteTests.swift */; }; + FE6A6D3F1F75910E00DF1039 /* TemplateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D3E1F75910E00DF1039 /* TemplateTests.swift */; }; + FE6A6D411F75911A00DF1039 /* TestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D401F75911A00DF1039 /* TestTests.swift */; }; + FE6A6D431F75912400DF1039 /* UserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D421F75912400DF1039 /* UserTests.swift */; }; + FE6A6D451F75913000DF1039 /* CaseFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D441F75913000DF1039 /* CaseFieldTests.swift */; }; + FE6A6D471F75913900DF1039 /* ConfigTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D461F75913900DF1039 /* ConfigTests.swift */; }; + FE6A6D491F75914300DF1039 /* CustomFieldTypeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D481F75914300DF1039 /* CustomFieldTypeTests.swift */; }; + FE6C8AE21F8BEA2E00F45642 /* MutableCustomFields.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6C8AE11F8BEA2E00F45642 /* MutableCustomFields.swift */; }; + FE75B2E01F83078A00DF367A /* CustomFieldsContainerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE22458B1F75BECC009F2B2B /* CustomFieldsContainerTests.swift */; }; + FE77BB761FA9132800E23865 /* NewRunTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795C31FA799D10030C395 /* NewRunTests.swift */; }; + FE791A251F7D9FFA00D7E870 /* Project.SuiteModeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE791A241F7D9FFA00D7E870 /* Project.SuiteModeTests.swift */; }; + FE7B53931FBF5BB1003C26BD /* Array+ContentComparison.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE7B53921FBF5BB1003C26BD /* Array+ContentComparison.swift */; }; + FE7CD3E41F9ABDC300C6108E /* NewPlan.Entry.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE7CD3E31F9ABDC300C6108E /* NewPlan.Entry.swift */; }; + FE813A181F73126F00265569 /* JSONDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE813A171F73126F00265569 /* JSONDictionary.swift */; }; + FE8C2D5A1FBA391F005A4150 /* Date+Seconds.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE8C2D591FBA391F005A4150 /* Date+Seconds.swift */; }; + FE978CE91F9528320005D181 /* API.RequestResultDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE978CE81F9528320005D181 /* API.RequestResultDebug.swift */; }; + FE978CEB1F9528400005D181 /* API.RequestErrorDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE978CEA1F9528400005D181 /* API.RequestErrorDebug.swift */; }; + FE978CED1F9532BA0005D181 /* URLRequestDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE978CEC1F9532BA0005D181 /* URLRequestDebug.swift */; }; + FE9F38A61F69E5AD003BBA36 /* Plan.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A95E1F59F45C007E946D /* Plan.swift */; }; + FEA348781F5A141300C1E37A /* API.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEA348771F5A141300C1E37A /* API.swift */; }; + FEAE7F921F7C5ABE00906FE1 /* Config.Context.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEAE7F911F7C5ABE00906FE1 /* Config.Context.swift */; }; + FEAE7F961F7C5D0C00906FE1 /* Config.ContextTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEAE7F951F7C5D0C00906FE1 /* Config.ContextTests.swift */; }; + FEAE99591FEC38CE00B52CA9 /* QuizTrain.h in Headers */ = {isa = PBXBuildFile; fileRef = FE23A91F1F59F3A0007E946D /* QuizTrain.h */; settings = {ATTRIBUTES = (Public, ); }; }; + FEAE99681FEC3BCE00B52CA9 /* QuizTrain.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FEAE995F1FEC3BCD00B52CA9 /* QuizTrain.framework */; }; + FEAE99841FEC3BEB00B52CA9 /* QuizTrain.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FEAE997B1FEC3BEB00B52CA9 /* QuizTrain.framework */; }; + FEAE99921FEC42C600B52CA9 /* AddRequestJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEB546831F9135CB00AA6DA5 /* AddRequestJSON.swift */; }; + FEAE99931FEC42C600B52CA9 /* AddRequestJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEB546831F9135CB00AA6DA5 /* AddRequestJSON.swift */; }; + FEAE99941FEC42C600B52CA9 /* AddRequestJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEB546831F9135CB00AA6DA5 /* AddRequestJSON.swift */; }; + FEAE99951FEC42C900B52CA9 /* AddRequestJSONKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEB546851F9135D500AA6DA5 /* AddRequestJSONKeys.swift */; }; + FEAE99961FEC42CA00B52CA9 /* AddRequestJSONKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEB546851F9135D500AA6DA5 /* AddRequestJSONKeys.swift */; }; + FEAE99971FEC42CA00B52CA9 /* AddRequestJSONKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEB546851F9135D500AA6DA5 /* AddRequestJSONKeys.swift */; }; + FEAE99981FEC42CD00B52CA9 /* CustomFields.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE5869D61F7478D600BE5C5C /* CustomFields.swift */; }; + FEAE99991FEC42CD00B52CA9 /* CustomFields.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE5869D61F7478D600BE5C5C /* CustomFields.swift */; }; + FEAE999B1FEC42CE00B52CA9 /* CustomFields.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE5869D61F7478D600BE5C5C /* CustomFields.swift */; }; + FEAE999C1FEC42D000B52CA9 /* MutableCustomFields.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6C8AE11F8BEA2E00F45642 /* MutableCustomFields.swift */; }; + FEAE999D1FEC42D100B52CA9 /* MutableCustomFields.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6C8AE11F8BEA2E00F45642 /* MutableCustomFields.swift */; }; + FEAE999E1FEC42D200B52CA9 /* MutableCustomFields.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6C8AE11F8BEA2E00F45642 /* MutableCustomFields.swift */; }; + FEAE999F1FEC42D400B52CA9 /* CustomFieldsContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2245841F75AC82009F2B2B /* CustomFieldsContainer.swift */; }; + FEAE99A01FEC42D500B52CA9 /* CustomFieldsContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2245841F75AC82009F2B2B /* CustomFieldsContainer.swift */; }; + FEAE99A11FEC42D500B52CA9 /* CustomFieldsContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2245841F75AC82009F2B2B /* CustomFieldsContainer.swift */; }; + FEAE99A21FEC42D800B52CA9 /* ErrorContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEBDA3C01FD1C7F400124430 /* ErrorContainer.swift */; }; + FEAE99A31FEC42D800B52CA9 /* ErrorContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEBDA3C01FD1C7F400124430 /* ErrorContainer.swift */; }; + FEAE99A41FEC42D900B52CA9 /* ErrorContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEBDA3C01FD1C7F400124430 /* ErrorContainer.swift */; }; + FEAE99A51FEC42DB00B52CA9 /* JSONDictionaryContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE5E1A2F1F8804E3001E479B /* JSONDictionaryContainer.swift */; }; + FEAE99A61FEC42DB00B52CA9 /* JSONDictionaryContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE5E1A2F1F8804E3001E479B /* JSONDictionaryContainer.swift */; }; + FEAE99A71FEC42DC00B52CA9 /* JSONDictionaryContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE5E1A2F1F8804E3001E479B /* JSONDictionaryContainer.swift */; }; + FEAE99A81FEC42DE00B52CA9 /* DebugDescription.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEC214061FD741F700036B17 /* DebugDescription.swift */; }; + FEAE99A91FEC42DF00B52CA9 /* DebugDescription.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEC214061FD741F700036B17 /* DebugDescription.swift */; }; + FEAE99AB1FEC42E000B52CA9 /* DebugDescription.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEC214061FD741F700036B17 /* DebugDescription.swift */; }; + FEAE99AC1FEC42E400B52CA9 /* DebugDetails.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEC214081FD7420300036B17 /* DebugDetails.swift */; }; + FEAE99AD1FEC42E500B52CA9 /* DebugDetails.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEC214081FD7420300036B17 /* DebugDetails.swift */; }; + FEAE99AE1FEC42E500B52CA9 /* DebugDetails.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEC214081FD7420300036B17 /* DebugDetails.swift */; }; + FEAE99AF1FEC42E800B52CA9 /* SingleMatchError.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEDFF2741FE1B09000AEB3D6 /* SingleMatchError.swift */; }; + FEAE99B01FEC42E800B52CA9 /* SingleMatchError.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEDFF2741FE1B09000AEB3D6 /* SingleMatchError.swift */; }; + FEAE99B21FEC42E900B52CA9 /* SingleMatchError.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEDFF2741FE1B09000AEB3D6 /* SingleMatchError.swift */; }; + FEAE99B31FEC42EC00B52CA9 /* MultipleMatchError.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEDFF2761FE1B0A000AEB3D6 /* MultipleMatchError.swift */; }; + FEAE99B41FEC42ED00B52CA9 /* MultipleMatchError.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEDFF2761FE1B0A000AEB3D6 /* MultipleMatchError.swift */; }; + FEAE99B61FEC42EE00B52CA9 /* MultipleMatchError.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEDFF2761FE1B0A000AEB3D6 /* MultipleMatchError.swift */; }; + FEAE99B71FEC42F100B52CA9 /* Array+ContentComparison.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE7B53921FBF5BB1003C26BD /* Array+ContentComparison.swift */; }; + FEAE99B81FEC42F200B52CA9 /* Array+ContentComparison.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE7B53921FBF5BB1003C26BD /* Array+ContentComparison.swift */; }; + FEAE99BA1FEC42F300B52CA9 /* Array+ContentComparison.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE7B53921FBF5BB1003C26BD /* Array+ContentComparison.swift */; }; + FEAE99BB1FEC42F500B52CA9 /* Date+Seconds.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE8C2D591FBA391F005A4150 /* Date+Seconds.swift */; }; + FEAE99BC1FEC42F600B52CA9 /* Date+Seconds.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE8C2D591FBA391F005A4150 /* Date+Seconds.swift */; }; + FEAE99BD1FEC42F700B52CA9 /* Date+Seconds.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE8C2D591FBA391F005A4150 /* Date+Seconds.swift */; }; + FEAE99BE1FEC42FA00B52CA9 /* Equatable+OptionalArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2E774A1F85923F00EF5E54 /* Equatable+OptionalArray.swift */; }; + FEAE99BF1FEC42FA00B52CA9 /* Equatable+OptionalArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2E774A1F85923F00EF5E54 /* Equatable+OptionalArray.swift */; }; + FEAE99C01FEC42FB00B52CA9 /* Equatable+OptionalArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2E774A1F85923F00EF5E54 /* Equatable+OptionalArray.swift */; }; + FEAE99C11FEC42FC00B52CA9 /* Equatable+OptionalArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2E774A1F85923F00EF5E54 /* Equatable+OptionalArray.swift */; }; + FEAE99C21FEC42FE00B52CA9 /* Identifiable.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE20D03E1FD876820057A45C /* Identifiable.swift */; }; + FEAE99C31FEC42FF00B52CA9 /* Identifiable.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE20D03E1FD876820057A45C /* Identifiable.swift */; }; + FEAE99C41FEC430000B52CA9 /* Identifiable.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE20D03E1FD876820057A45C /* Identifiable.swift */; }; + FEAE99C51FEC430200B52CA9 /* JSONKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE5259111F82D1AD00E0DDB7 /* JSONKey.swift */; }; + FEAE99C61FEC430300B52CA9 /* JSONKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE5259111F82D1AD00E0DDB7 /* JSONKey.swift */; }; + FEAE99C71FEC430300B52CA9 /* JSONKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE5259111F82D1AD00E0DDB7 /* JSONKey.swift */; }; + FEAE99C81FEC430600B52CA9 /* JSONDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE813A171F73126F00265569 /* JSONDictionary.swift */; }; + FEAE99C91FEC430600B52CA9 /* JSONDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE813A171F73126F00265569 /* JSONDictionary.swift */; }; + FEAE99CA1FEC430800B52CA9 /* JSONDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE813A171F73126F00265569 /* JSONDictionary.swift */; }; + FEAE99CB1FEC430A00B52CA9 /* JSONDeserializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE4F8F3A1F8437DE00447F9E /* JSONDeserializable.swift */; }; + FEAE99CC1FEC430B00B52CA9 /* JSONDeserializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE4F8F3A1F8437DE00447F9E /* JSONDeserializable.swift */; }; + FEAE99CD1FEC430C00B52CA9 /* JSONDeserializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE4F8F3A1F8437DE00447F9E /* JSONDeserializable.swift */; }; + FEAE99CE1FEC430E00B52CA9 /* JSONSerializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2F1AD61F844FCD00FF9E0C /* JSONSerializable.swift */; }; + FEAE99CF1FEC430F00B52CA9 /* JSONSerializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2F1AD61F844FCD00FF9E0C /* JSONSerializable.swift */; }; + FEAE99D01FEC430F00B52CA9 /* JSONSerializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2F1AD61F844FCD00FF9E0C /* JSONSerializable.swift */; }; + FEAE99D11FEC431200B52CA9 /* AsyncOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEBF50361FCF3E91005B86B7 /* AsyncOperation.swift */; }; + FEAE99D21FEC431300B52CA9 /* AsyncOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEBF50361FCF3E91005B86B7 /* AsyncOperation.swift */; }; + FEAE99D31FEC431300B52CA9 /* AsyncOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEBF50361FCF3E91005B86B7 /* AsyncOperation.swift */; }; + FEAE99D41FEC431500B52CA9 /* UpdateRequestJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE1FB1031F9131B200383724 /* UpdateRequestJSON.swift */; }; + FEAE99D51FEC431600B52CA9 /* UpdateRequestJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE1FB1031F9131B200383724 /* UpdateRequestJSON.swift */; }; + FEAE99D61FEC431700B52CA9 /* UpdateRequestJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE1FB1031F9131B200383724 /* UpdateRequestJSON.swift */; }; + FEAE99D71FEC431900B52CA9 /* UpdateRequestJSONKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6838E11F8FE10500431C1C /* UpdateRequestJSONKeys.swift */; }; + FEAE99D81FEC431900B52CA9 /* UpdateRequestJSONKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6838E11F8FE10500431C1C /* UpdateRequestJSONKeys.swift */; }; + FEAE99D91FEC431A00B52CA9 /* UpdateRequestJSONKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6838E11F8FE10500431C1C /* UpdateRequestJSONKeys.swift */; }; + FEAE99DA1FEC431B00B52CA9 /* UpdateRequestJSONKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6838E11F8FE10500431C1C /* UpdateRequestJSONKeys.swift */; }; + FEAE99DB1FEC431E00B52CA9 /* Validatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE208DF91FBB95150065BE88 /* Validatable.swift */; }; + FEAE99DC1FEC431F00B52CA9 /* Validatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE208DF91FBB95150065BE88 /* Validatable.swift */; }; + FEAE99DD1FEC431F00B52CA9 /* Validatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE208DF91FBB95150065BE88 /* Validatable.swift */; }; + FEAE99DE1FEC432200B52CA9 /* Outcome.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEB546871F9170A900AA6DA5 /* Outcome.swift */; }; + FEAE99DF1FEC432300B52CA9 /* Outcome.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEB546871F9170A900AA6DA5 /* Outcome.swift */; }; + FEAE99E01FEC432300B52CA9 /* Outcome.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEB546871F9170A900AA6DA5 /* Outcome.swift */; }; + FEAE99E11FEC432700B52CA9 /* QueryItemProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEEC443F1FB4D3270042BD5A /* QueryItemProvider.swift */; }; + FEAE99E21FEC432700B52CA9 /* QueryItemProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEEC443F1FB4D3270042BD5A /* QueryItemProvider.swift */; }; + FEAE99E31FEC432800B52CA9 /* QueryItemProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEEC443F1FB4D3270042BD5A /* QueryItemProvider.swift */; }; + FEAE99E41FEC432A00B52CA9 /* UniqueSelection.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE5A24F11FE092D300198848 /* UniqueSelection.swift */; }; + FEAE99E51FEC432A00B52CA9 /* UniqueSelection.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE5A24F11FE092D300198848 /* UniqueSelection.swift */; }; + FEAE99E61FEC432B00B52CA9 /* UniqueSelection.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE5A24F11FE092D300198848 /* UniqueSelection.swift */; }; + FEAE99E71FEC432E00B52CA9 /* CustomFieldType.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE331C1B1F6B406300F9A653 /* CustomFieldType.swift */; }; + FEAE99E81FEC432E00B52CA9 /* CustomFieldType.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE331C1B1F6B406300F9A653 /* CustomFieldType.swift */; }; + FEAE99E91FEC432F00B52CA9 /* CustomFieldType.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE331C1B1F6B406300F9A653 /* CustomFieldType.swift */; }; + FEAE99EA1FEC433100B52CA9 /* Project.SuiteMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEAF0BFD1F7D9F15001D4F10 /* Project.SuiteMode.swift */; }; + FEAE99EB1FEC433200B52CA9 /* Project.SuiteMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEAF0BFD1F7D9F15001D4F10 /* Project.SuiteMode.swift */; }; + FEAE99EC1FEC433200B52CA9 /* Project.SuiteMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEAF0BFD1F7D9F15001D4F10 /* Project.SuiteMode.swift */; }; + FEAE99ED1FEC433500B52CA9 /* Config.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE11181B1F6C3A4B00D24A5F /* Config.swift */; }; + FEAE99EE1FEC433600B52CA9 /* Config.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE11181B1F6C3A4B00D24A5F /* Config.swift */; }; + FEAE99EF1FEC433600B52CA9 /* Config.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE11181B1F6C3A4B00D24A5F /* Config.swift */; }; + FEAE99F01FEC433800B52CA9 /* Config.Context.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEAE7F911F7C5ABE00906FE1 /* Config.Context.swift */; }; + FEAE99F21FEC433B00B52CA9 /* Config.Context.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEAE7F911F7C5ABE00906FE1 /* Config.Context.swift */; }; + FEAE99F31FEC433B00B52CA9 /* Config.Context.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEAE7F911F7C5ABE00906FE1 /* Config.Context.swift */; }; + FEAE99F41FEC4EC100B52CA9 /* Case.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A9541F59F415007E946D /* Case.swift */; }; + FEAE99F51FEC4EC200B52CA9 /* Case.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A9541F59F415007E946D /* Case.swift */; }; + FEAE99F61FEC4EC200B52CA9 /* Case.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A9541F59F415007E946D /* Case.swift */; }; + FEAE99F71FEC4EC500B52CA9 /* CaseField.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A9561F59F42B007E946D /* CaseField.swift */; }; + FEAE99F81FEC4EC500B52CA9 /* CaseField.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A9561F59F42B007E946D /* CaseField.swift */; }; + FEAE99F91FEC4EC600B52CA9 /* CaseField.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A9561F59F42B007E946D /* CaseField.swift */; }; + FEAE99FA1FEC4EC800B52CA9 /* CaseType.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A9581F59F437007E946D /* CaseType.swift */; }; + FEAE99FB1FEC4EC800B52CA9 /* CaseType.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A9581F59F437007E946D /* CaseType.swift */; }; + FEAE99FC1FEC4EC900B52CA9 /* CaseType.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A9581F59F437007E946D /* CaseType.swift */; }; + FEAE99FD1FEC4ECB00B52CA9 /* Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A95A1F59F443007E946D /* Configuration.swift */; }; + FEAE99FE1FEC4ECB00B52CA9 /* Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A95A1F59F443007E946D /* Configuration.swift */; }; + FEAE99FF1FEC4ECD00B52CA9 /* Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A95A1F59F443007E946D /* Configuration.swift */; }; + FEAE9A001FEC4ED000B52CA9 /* ConfigurationGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A9741F5A042D007E946D /* ConfigurationGroup.swift */; }; + FEAE9A011FEC4ED100B52CA9 /* ConfigurationGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A9741F5A042D007E946D /* ConfigurationGroup.swift */; }; + FEAE9A021FEC4ED100B52CA9 /* ConfigurationGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A9741F5A042D007E946D /* ConfigurationGroup.swift */; }; + FEAE9A031FEC4ED400B52CA9 /* Milestone.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A95C1F59F450007E946D /* Milestone.swift */; }; + FEAE9A041FEC4ED400B52CA9 /* Milestone.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A95C1F59F450007E946D /* Milestone.swift */; }; + FEAE9A051FEC4ED500B52CA9 /* Milestone.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A95C1F59F450007E946D /* Milestone.swift */; }; + FEAE9A061FEC4ED800B52CA9 /* Plan.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A95E1F59F45C007E946D /* Plan.swift */; }; + FEAE9A071FEC4ED900B52CA9 /* Plan.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A95E1F59F45C007E946D /* Plan.swift */; }; + FEAE9A081FEC4ED900B52CA9 /* Plan.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A95E1F59F45C007E946D /* Plan.swift */; }; + FEAE9A091FEC4EDC00B52CA9 /* Plan.Entry.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE4E11D61F7C4112004A315E /* Plan.Entry.swift */; }; + FEAE9A0A1FEC4EDD00B52CA9 /* Plan.Entry.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE4E11D61F7C4112004A315E /* Plan.Entry.swift */; }; + FEAE9A0B1FEC4EDD00B52CA9 /* Plan.Entry.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE4E11D61F7C4112004A315E /* Plan.Entry.swift */; }; + FEAE9A0C1FEC4EED00B52CA9 /* Priority.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A9601F59F46C007E946D /* Priority.swift */; }; + FEAE9A0D1FEC4EED00B52CA9 /* Priority.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A9601F59F46C007E946D /* Priority.swift */; }; + FEAE9A0E1FEC4EEE00B52CA9 /* Priority.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A9601F59F46C007E946D /* Priority.swift */; }; + FEAE9A0F1FEC4EF100B52CA9 /* Project.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A9621F59F478007E946D /* Project.swift */; }; + FEAE9A101FEC4EF200B52CA9 /* Project.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A9621F59F478007E946D /* Project.swift */; }; + FEAE9A111FEC4EF200B52CA9 /* Project.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A9621F59F478007E946D /* Project.swift */; }; + FEAE9A121FEC4EF600B52CA9 /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A9641F59F489007E946D /* Result.swift */; }; + FEAE9A131FEC4EF700B52CA9 /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A9641F59F489007E946D /* Result.swift */; }; + FEAE9A141FEC4EF700B52CA9 /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A9641F59F489007E946D /* Result.swift */; }; + FEAE9A151FEC4EFA00B52CA9 /* ResultField.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A9661F59F4B3007E946D /* ResultField.swift */; }; + FEAE9A161FEC4EFB00B52CA9 /* ResultField.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A9661F59F4B3007E946D /* ResultField.swift */; }; + FEAE9A171FEC4EFB00B52CA9 /* ResultField.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A9661F59F4B3007E946D /* ResultField.swift */; }; + FEAE9A181FEC4EFF00B52CA9 /* Run.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A9681F59F4BA007E946D /* Run.swift */; }; + FEAE9A191FEC4EFF00B52CA9 /* Section.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A96A1F59F4C3007E946D /* Section.swift */; }; + FEAE9A1A1FEC4EFF00B52CA9 /* Status.swift in Sources */ = {isa = PBXBuildFile; fileRef = FED295461FA10E9300746DAB /* Status.swift */; }; + FEAE9A1B1FEC4EFF00B52CA9 /* Suite.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A96C1F59F4CE007E946D /* Suite.swift */; }; + FEAE9A1C1FEC4EFF00B52CA9 /* Template.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A96E1F59F4D9007E946D /* Template.swift */; }; + FEAE9A1D1FEC4EFF00B52CA9 /* Test.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A9701F59F4E2007E946D /* Test.swift */; }; + FEAE9A1E1FEC4EFF00B52CA9 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A9721F59F4EB007E946D /* User.swift */; }; + FEAE9A1F1FEC4F0000B52CA9 /* Run.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A9681F59F4BA007E946D /* Run.swift */; }; + FEAE9A201FEC4F0000B52CA9 /* Section.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A96A1F59F4C3007E946D /* Section.swift */; }; + FEAE9A211FEC4F0000B52CA9 /* Status.swift in Sources */ = {isa = PBXBuildFile; fileRef = FED295461FA10E9300746DAB /* Status.swift */; }; + FEAE9A221FEC4F0000B52CA9 /* Suite.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A96C1F59F4CE007E946D /* Suite.swift */; }; + FEAE9A231FEC4F0000B52CA9 /* Template.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A96E1F59F4D9007E946D /* Template.swift */; }; + FEAE9A241FEC4F0000B52CA9 /* Test.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A9701F59F4E2007E946D /* Test.swift */; }; + FEAE9A251FEC4F0000B52CA9 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A9721F59F4EB007E946D /* User.swift */; }; + FEAE9A261FEC4F0000B52CA9 /* Run.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A9681F59F4BA007E946D /* Run.swift */; }; + FEAE9A271FEC4F0000B52CA9 /* Section.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A96A1F59F4C3007E946D /* Section.swift */; }; + FEAE9A281FEC4F0000B52CA9 /* Status.swift in Sources */ = {isa = PBXBuildFile; fileRef = FED295461FA10E9300746DAB /* Status.swift */; }; + FEAE9A291FEC4F0000B52CA9 /* Suite.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A96C1F59F4CE007E946D /* Suite.swift */; }; + FEAE9A2A1FEC4F0000B52CA9 /* Template.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A96E1F59F4D9007E946D /* Template.swift */; }; + FEAE9A2B1FEC4F0000B52CA9 /* Test.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A9701F59F4E2007E946D /* Test.swift */; }; + FEAE9A2C1FEC4F0000B52CA9 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A9721F59F4EB007E946D /* User.swift */; }; + FEAE9A2D1FEC4F0600B52CA9 /* API.RequestErrorDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE978CEA1F9528400005D181 /* API.RequestErrorDebug.swift */; }; + FEAE9A2E1FEC4F0600B52CA9 /* API.RequestResultDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE978CE81F9528320005D181 /* API.RequestResultDebug.swift */; }; + FEAE9A2F1FEC4F0600B52CA9 /* API.RequestErrorDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE978CEA1F9528400005D181 /* API.RequestErrorDebug.swift */; }; + FEAE9A301FEC4F0600B52CA9 /* API.RequestResultDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE978CE81F9528320005D181 /* API.RequestResultDebug.swift */; }; + FEAE9A311FEC4F0700B52CA9 /* API.RequestErrorDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE978CEA1F9528400005D181 /* API.RequestErrorDebug.swift */; }; + FEAE9A321FEC4F0700B52CA9 /* API.RequestResultDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE978CE81F9528320005D181 /* API.RequestResultDebug.swift */; }; + FEAE9A331FEC4F0A00B52CA9 /* ObjectAPI.ClientErrorDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE1474E11FAA69F70049DA84 /* ObjectAPI.ClientErrorDebug.swift */; }; + FEAE9A341FEC4F0A00B52CA9 /* ObjectAPI.DataProcessingErrorDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE1474E91FAA6A340049DA84 /* ObjectAPI.DataProcessingErrorDebug.swift */; }; + FEAE9A351FEC4F0A00B52CA9 /* ObjectAPI.DataRequestErrorDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE1474DB1FAA69AA0049DA84 /* ObjectAPI.DataRequestErrorDebug.swift */; }; + FEAE9A361FEC4F0A00B52CA9 /* ObjectAPI.MatchErrorDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEC213FF1FD7302900036B17 /* ObjectAPI.MatchErrorDebug.swift */; }; + FEAE9A371FEC4F0A00B52CA9 /* ObjectAPI.ObjectConversionErrorDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE1474EB1FAA6A5B0049DA84 /* ObjectAPI.ObjectConversionErrorDebug.swift */; }; + FEAE9A381FEC4F0A00B52CA9 /* ObjectAPI.RequestErrorDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE1474D91FAA698F0049DA84 /* ObjectAPI.RequestErrorDebug.swift */; }; + FEAE9A391FEC4F0A00B52CA9 /* ObjectAPI.ServerErrorDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE1474E41FAA6A0D0049DA84 /* ObjectAPI.ServerErrorDebug.swift */; }; + FEAE9A3A1FEC4F0A00B52CA9 /* ObjectAPI.StatusCodeErrorDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE1474E71FAA6A270049DA84 /* ObjectAPI.StatusCodeErrorDebug.swift */; }; + FEAE9A3B1FEC4F0A00B52CA9 /* ObjectAPI.UpdateRequestErrorDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE1474DD1FAA69B70049DA84 /* ObjectAPI.UpdateRequestErrorDebug.swift */; }; + FEAE9A3C1FEC4F0A00B52CA9 /* URLRequestDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE978CEC1F9532BA0005D181 /* URLRequestDebug.swift */; }; + FEAE9A3D1FEC4F0B00B52CA9 /* ObjectAPI.ClientErrorDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE1474E11FAA69F70049DA84 /* ObjectAPI.ClientErrorDebug.swift */; }; + FEAE9A3E1FEC4F0B00B52CA9 /* ObjectAPI.DataProcessingErrorDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE1474E91FAA6A340049DA84 /* ObjectAPI.DataProcessingErrorDebug.swift */; }; + FEAE9A3F1FEC4F0B00B52CA9 /* ObjectAPI.DataRequestErrorDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE1474DB1FAA69AA0049DA84 /* ObjectAPI.DataRequestErrorDebug.swift */; }; + FEAE9A401FEC4F0B00B52CA9 /* ObjectAPI.MatchErrorDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEC213FF1FD7302900036B17 /* ObjectAPI.MatchErrorDebug.swift */; }; + FEAE9A411FEC4F0B00B52CA9 /* ObjectAPI.ObjectConversionErrorDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE1474EB1FAA6A5B0049DA84 /* ObjectAPI.ObjectConversionErrorDebug.swift */; }; + FEAE9A421FEC4F0B00B52CA9 /* ObjectAPI.RequestErrorDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE1474D91FAA698F0049DA84 /* ObjectAPI.RequestErrorDebug.swift */; }; + FEAE9A431FEC4F0B00B52CA9 /* ObjectAPI.ServerErrorDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE1474E41FAA6A0D0049DA84 /* ObjectAPI.ServerErrorDebug.swift */; }; + FEAE9A441FEC4F0B00B52CA9 /* ObjectAPI.StatusCodeErrorDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE1474E71FAA6A270049DA84 /* ObjectAPI.StatusCodeErrorDebug.swift */; }; + FEAE9A451FEC4F0B00B52CA9 /* ObjectAPI.UpdateRequestErrorDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE1474DD1FAA69B70049DA84 /* ObjectAPI.UpdateRequestErrorDebug.swift */; }; + FEAE9A461FEC4F0B00B52CA9 /* URLRequestDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE978CEC1F9532BA0005D181 /* URLRequestDebug.swift */; }; + FEAE9A471FEC4F0B00B52CA9 /* ObjectAPI.ClientErrorDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE1474E11FAA69F70049DA84 /* ObjectAPI.ClientErrorDebug.swift */; }; + FEAE9A481FEC4F0B00B52CA9 /* ObjectAPI.DataProcessingErrorDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE1474E91FAA6A340049DA84 /* ObjectAPI.DataProcessingErrorDebug.swift */; }; + FEAE9A491FEC4F0B00B52CA9 /* ObjectAPI.DataRequestErrorDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE1474DB1FAA69AA0049DA84 /* ObjectAPI.DataRequestErrorDebug.swift */; }; + FEAE9A4A1FEC4F0B00B52CA9 /* ObjectAPI.MatchErrorDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEC213FF1FD7302900036B17 /* ObjectAPI.MatchErrorDebug.swift */; }; + FEAE9A4B1FEC4F0B00B52CA9 /* ObjectAPI.ObjectConversionErrorDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE1474EB1FAA6A5B0049DA84 /* ObjectAPI.ObjectConversionErrorDebug.swift */; }; + FEAE9A4C1FEC4F0B00B52CA9 /* ObjectAPI.RequestErrorDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE1474D91FAA698F0049DA84 /* ObjectAPI.RequestErrorDebug.swift */; }; + FEAE9A4D1FEC4F0B00B52CA9 /* ObjectAPI.ServerErrorDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE1474E41FAA6A0D0049DA84 /* ObjectAPI.ServerErrorDebug.swift */; }; + FEAE9A4E1FEC4F0B00B52CA9 /* ObjectAPI.StatusCodeErrorDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE1474E71FAA6A270049DA84 /* ObjectAPI.StatusCodeErrorDebug.swift */; }; + FEAE9A4F1FEC4F0B00B52CA9 /* ObjectAPI.UpdateRequestErrorDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE1474DD1FAA69B70049DA84 /* ObjectAPI.UpdateRequestErrorDebug.swift */; }; + FEAE9A501FEC4F0B00B52CA9 /* URLRequestDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE978CEC1F9532BA0005D181 /* URLRequestDebug.swift */; }; + FEAE9A511FEC4F0F00B52CA9 /* Filter.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEEC443A1FB4D1BC0042BD5A /* Filter.swift */; }; + FEAE9A521FEC4F0F00B52CA9 /* Filter.Value.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEEC443D1FB4D23F0042BD5A /* Filter.Value.swift */; }; + FEAE9A531FEC4F1000B52CA9 /* Filter.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEEC443A1FB4D1BC0042BD5A /* Filter.swift */; }; + FEAE9A541FEC4F1000B52CA9 /* Filter.Value.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEEC443D1FB4D23F0042BD5A /* Filter.Value.swift */; }; + FEAE9A551FEC4F1000B52CA9 /* Filter.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEEC443A1FB4D1BC0042BD5A /* Filter.swift */; }; + FEAE9A561FEC4F1000B52CA9 /* Filter.Value.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEEC443D1FB4D23F0042BD5A /* Filter.Value.swift */; }; + FEAE9A571FEC4F1400B52CA9 /* NewCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE4F8F381F84361F00447F9E /* NewCase.swift */; }; + FEAE9A581FEC4F1400B52CA9 /* NewConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEDCBEEE1F96BEE70083AD46 /* NewConfiguration.swift */; }; + FEAE9A591FEC4F1400B52CA9 /* NewConfigurationGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEDCBEF01F96BEF10083AD46 /* NewConfigurationGroup.swift */; }; + FEAE9A5A1FEC4F1400B52CA9 /* NewMilestone.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEDCBEF21F96BF020083AD46 /* NewMilestone.swift */; }; + FEAE9A5B1FEC4F1400B52CA9 /* NewPlan.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEDCBEF41F96BF130083AD46 /* NewPlan.swift */; }; + FEAE9A5C1FEC4F1400B52CA9 /* NewPlan.Entry.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE7CD3E31F9ABDC300C6108E /* NewPlan.Entry.swift */; }; + FEAE9A5D1FEC4F1400B52CA9 /* NewPlan.Entry.Run.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2D018A1FBCE70B00473B84 /* NewPlan.Entry.Run.swift */; }; + FEAE9A5E1FEC4F1400B52CA9 /* NewProject.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEDCBEF61F96BF290083AD46 /* NewProject.swift */; }; + FEAE9A5F1FEC4F1400B52CA9 /* NewResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEDCBEF81F96BF3A0083AD46 /* NewResult.swift */; }; + FEAE9A601FEC4F1400B52CA9 /* NewCaseResults.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE208DF01FBB6BA10065BE88 /* NewCaseResults.swift */; }; + FEAE9A611FEC4F1400B52CA9 /* NewCaseResults.Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE208DEC1FBB69C60065BE88 /* NewCaseResults.Result.swift */; }; + FEAE9A621FEC4F1400B52CA9 /* NewTestResults.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE208DEE1FBB6B920065BE88 /* NewTestResults.swift */; }; + FEAE9A631FEC4F1400B52CA9 /* NewTestResults.Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE208DEA1FBB69B80065BE88 /* NewTestResults.Result.swift */; }; + FEAE9A641FEC4F1400B52CA9 /* NewRun.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEDCBEFA1F96BF540083AD46 /* NewRun.swift */; }; + FEAE9A651FEC4F1400B52CA9 /* NewSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEDCBEFC1F96BF610083AD46 /* NewSection.swift */; }; + FEAE9A661FEC4F1400B52CA9 /* NewSuite.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEDCBEFE1F96BF730083AD46 /* NewSuite.swift */; }; + FEAE9A671FEC4F1500B52CA9 /* NewCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE4F8F381F84361F00447F9E /* NewCase.swift */; }; + FEAE9A681FEC4F1500B52CA9 /* NewConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEDCBEEE1F96BEE70083AD46 /* NewConfiguration.swift */; }; + FEAE9A691FEC4F1500B52CA9 /* NewConfigurationGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEDCBEF01F96BEF10083AD46 /* NewConfigurationGroup.swift */; }; + FEAE9A6A1FEC4F1500B52CA9 /* NewMilestone.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEDCBEF21F96BF020083AD46 /* NewMilestone.swift */; }; + FEAE9A6B1FEC4F1500B52CA9 /* NewPlan.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEDCBEF41F96BF130083AD46 /* NewPlan.swift */; }; + FEAE9A6C1FEC4F1500B52CA9 /* NewPlan.Entry.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE7CD3E31F9ABDC300C6108E /* NewPlan.Entry.swift */; }; + FEAE9A6D1FEC4F1500B52CA9 /* NewPlan.Entry.Run.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2D018A1FBCE70B00473B84 /* NewPlan.Entry.Run.swift */; }; + FEAE9A6E1FEC4F1500B52CA9 /* NewProject.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEDCBEF61F96BF290083AD46 /* NewProject.swift */; }; + FEAE9A6F1FEC4F1500B52CA9 /* NewResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEDCBEF81F96BF3A0083AD46 /* NewResult.swift */; }; + FEAE9A701FEC4F1500B52CA9 /* NewCaseResults.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE208DF01FBB6BA10065BE88 /* NewCaseResults.swift */; }; + FEAE9A711FEC4F1500B52CA9 /* NewCaseResults.Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE208DEC1FBB69C60065BE88 /* NewCaseResults.Result.swift */; }; + FEAE9A721FEC4F1500B52CA9 /* NewTestResults.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE208DEE1FBB6B920065BE88 /* NewTestResults.swift */; }; + FEAE9A731FEC4F1500B52CA9 /* NewTestResults.Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE208DEA1FBB69B80065BE88 /* NewTestResults.Result.swift */; }; + FEAE9A741FEC4F1500B52CA9 /* NewRun.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEDCBEFA1F96BF540083AD46 /* NewRun.swift */; }; + FEAE9A751FEC4F1500B52CA9 /* NewSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEDCBEFC1F96BF610083AD46 /* NewSection.swift */; }; + FEAE9A761FEC4F1500B52CA9 /* NewSuite.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEDCBEFE1F96BF730083AD46 /* NewSuite.swift */; }; + FEAE9A771FEC4F1500B52CA9 /* NewCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE4F8F381F84361F00447F9E /* NewCase.swift */; }; + FEAE9A781FEC4F1500B52CA9 /* NewConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEDCBEEE1F96BEE70083AD46 /* NewConfiguration.swift */; }; + FEAE9A791FEC4F1500B52CA9 /* NewConfigurationGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEDCBEF01F96BEF10083AD46 /* NewConfigurationGroup.swift */; }; + FEAE9A7A1FEC4F1500B52CA9 /* NewMilestone.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEDCBEF21F96BF020083AD46 /* NewMilestone.swift */; }; + FEAE9A7B1FEC4F1500B52CA9 /* NewPlan.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEDCBEF41F96BF130083AD46 /* NewPlan.swift */; }; + FEAE9A7C1FEC4F1500B52CA9 /* NewPlan.Entry.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE7CD3E31F9ABDC300C6108E /* NewPlan.Entry.swift */; }; + FEAE9A7D1FEC4F1500B52CA9 /* NewPlan.Entry.Run.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2D018A1FBCE70B00473B84 /* NewPlan.Entry.Run.swift */; }; + FEAE9A7E1FEC4F1500B52CA9 /* NewProject.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEDCBEF61F96BF290083AD46 /* NewProject.swift */; }; + FEAE9A7F1FEC4F1500B52CA9 /* NewResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEDCBEF81F96BF3A0083AD46 /* NewResult.swift */; }; + FEAE9A801FEC4F1500B52CA9 /* NewCaseResults.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE208DF01FBB6BA10065BE88 /* NewCaseResults.swift */; }; + FEAE9A811FEC4F1500B52CA9 /* NewCaseResults.Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE208DEC1FBB69C60065BE88 /* NewCaseResults.Result.swift */; }; + FEAE9A821FEC4F1500B52CA9 /* NewTestResults.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE208DEE1FBB6B920065BE88 /* NewTestResults.swift */; }; + FEAE9A831FEC4F1500B52CA9 /* NewTestResults.Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE208DEA1FBB69B80065BE88 /* NewTestResults.Result.swift */; }; + FEAE9A841FEC4F1500B52CA9 /* NewRun.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEDCBEFA1F96BF540083AD46 /* NewRun.swift */; }; + FEAE9A851FEC4F1500B52CA9 /* NewSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEDCBEFC1F96BF610083AD46 /* NewSection.swift */; }; + FEAE9A861FEC4F1500B52CA9 /* NewSuite.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEDCBEFE1F96BF730083AD46 /* NewSuite.swift */; }; + FEAE9A871FEC4F1800B52CA9 /* UpdatePlanEntryRuns.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6989201FA39B99006CC783 /* UpdatePlanEntryRuns.swift */; }; + FEAE9A881FEC4F1900B52CA9 /* UpdatePlanEntryRuns.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6989201FA39B99006CC783 /* UpdatePlanEntryRuns.swift */; }; + FEAE9A891FEC4F1A00B52CA9 /* UpdatePlanEntryRuns.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6989201FA39B99006CC783 /* UpdatePlanEntryRuns.swift */; }; + FEAE9A8A1FEC4F1D00B52CA9 /* GetConfigurationGroupsOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE63715E1FD61CA500192CED /* GetConfigurationGroupsOperation.swift */; }; + FEAE9A8B1FEC4F1D00B52CA9 /* GetProjectOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = FECF666F1FDF05DB00015CC4 /* GetProjectOperation.swift */; }; + FEAE9A8C1FEC4F1D00B52CA9 /* GetTemplatesOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3899161FCF2BDE0032E265 /* GetTemplatesOperation.swift */; }; + FEAE9A8D1FEC4F1D00B52CA9 /* API.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEA348771F5A141300C1E37A /* API.swift */; }; + FEAE9A8E1FEC4F1D00B52CA9 /* ObjectAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE894C251F5A25CA0057E021 /* ObjectAPI.swift */; }; + FEAE9A8F1FEC4F1E00B52CA9 /* GetConfigurationGroupsOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE63715E1FD61CA500192CED /* GetConfigurationGroupsOperation.swift */; }; + FEAE9A901FEC4F1E00B52CA9 /* GetProjectOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = FECF666F1FDF05DB00015CC4 /* GetProjectOperation.swift */; }; + FEAE9A911FEC4F1E00B52CA9 /* GetTemplatesOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3899161FCF2BDE0032E265 /* GetTemplatesOperation.swift */; }; + FEAE9A921FEC4F1E00B52CA9 /* API.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEA348771F5A141300C1E37A /* API.swift */; }; + FEAE9A931FEC4F1E00B52CA9 /* ObjectAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE894C251F5A25CA0057E021 /* ObjectAPI.swift */; }; + FEAE9A941FEC4F1E00B52CA9 /* GetConfigurationGroupsOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE63715E1FD61CA500192CED /* GetConfigurationGroupsOperation.swift */; }; + FEAE9A951FEC4F1E00B52CA9 /* GetProjectOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = FECF666F1FDF05DB00015CC4 /* GetProjectOperation.swift */; }; + FEAE9A961FEC4F1E00B52CA9 /* GetTemplatesOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3899161FCF2BDE0032E265 /* GetTemplatesOperation.swift */; }; + FEAE9A971FEC4F1E00B52CA9 /* API.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEA348771F5A141300C1E37A /* API.swift */; }; + FEAE9A981FEC4F1E00B52CA9 /* ObjectAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE894C251F5A25CA0057E021 /* ObjectAPI.swift */; }; + FEAE9A991FEC4F2200B52CA9 /* QuizTrain.h in Headers */ = {isa = PBXBuildFile; fileRef = FE23A91F1F59F3A0007E946D /* QuizTrain.h */; settings = {ATTRIBUTES = (Public, ); }; }; + FEAE9A9A1FEC4F2200B52CA9 /* QuizTrain.h in Headers */ = {isa = PBXBuildFile; fileRef = FE23A91F1F59F3A0007E946D /* QuizTrain.h */; settings = {ATTRIBUTES = (Public, ); }; }; + FEAE9A9B1FEC4FF100B52CA9 /* Array+Random.swift in Sources */ = {isa = PBXBuildFile; fileRef = FECFE7FC1FD9E13500968EA3 /* Array+Random.swift */; }; + FEAE9A9C1FEC4FF100B52CA9 /* TestCredentials.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEBFEEB81FE2EAAB00E7FE1B /* TestCredentials.swift */; }; + FEAE9A9D1FEC4FF100B52CA9 /* TestCredentials.json in Resources */ = {isa = PBXBuildFile; fileRef = FEBFEEBA1FE2EAB900E7FE1B /* TestCredentials.json */; }; + FEAE9A9E1FEC4FF200B52CA9 /* Array+Random.swift in Sources */ = {isa = PBXBuildFile; fileRef = FECFE7FC1FD9E13500968EA3 /* Array+Random.swift */; }; + FEAE9A9F1FEC4FF200B52CA9 /* TestCredentials.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEBFEEB81FE2EAAB00E7FE1B /* TestCredentials.swift */; }; + FEAE9AA01FEC4FF200B52CA9 /* TestCredentials.json in Resources */ = {isa = PBXBuildFile; fileRef = FEBFEEBA1FE2EAB900E7FE1B /* TestCredentials.json */; }; + FEAE9AA11FEC4FF500B52CA9 /* AssertAddRequestJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795E51FA7EA6C0030C395 /* AssertAddRequestJSON.swift */; }; + FEAE9AA21FEC4FF500B52CA9 /* AssertCustomFields.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE58F5491F7EBF12009A1B4E /* AssertCustomFields.swift */; }; + FEAE9AA31FEC4FF500B52CA9 /* AssertEquatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEE774361FA8EA980016AACE /* AssertEquatable.swift */; }; + FEAE9AA41FEC4FF500B52CA9 /* AssertJSONDeserializing.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE58F54B1F7EBF24009A1B4E /* AssertJSONDeserializing.swift */; }; + FEAE9AA51FEC4FF500B52CA9 /* AssertJSONSerializing.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEF172621F857F53004FFFFF /* AssertJSONSerializing.swift */; }; + FEAE9AA61FEC4FF500B52CA9 /* AssertJSONTwoWaySerialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEF172641F858230004FFFFF /* AssertJSONTwoWaySerialization.swift */; }; + FEAE9AA71FEC4FF500B52CA9 /* AssertProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE58F5471F7EBF04009A1B4E /* AssertProperties.swift */; }; + FEAE9AA81FEC4FF500B52CA9 /* AssertUpdateRequestJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795A91FA7915C0030C395 /* AssertUpdateRequestJSON.swift */; }; + FEAE9AA91FEC4FF500B52CA9 /* AssertValidatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2877ED1FBB9803004503FB /* AssertValidatable.swift */; }; + FEAE9AAA1FEC4FF500B52CA9 /* AssertAddRequestJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795E51FA7EA6C0030C395 /* AssertAddRequestJSON.swift */; }; + FEAE9AAB1FEC4FF500B52CA9 /* AssertCustomFields.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE58F5491F7EBF12009A1B4E /* AssertCustomFields.swift */; }; + FEAE9AAC1FEC4FF500B52CA9 /* AssertEquatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEE774361FA8EA980016AACE /* AssertEquatable.swift */; }; + FEAE9AAD1FEC4FF500B52CA9 /* AssertJSONDeserializing.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE58F54B1F7EBF24009A1B4E /* AssertJSONDeserializing.swift */; }; + FEAE9AAE1FEC4FF500B52CA9 /* AssertJSONSerializing.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEF172621F857F53004FFFFF /* AssertJSONSerializing.swift */; }; + FEAE9AAF1FEC4FF500B52CA9 /* AssertJSONTwoWaySerialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEF172641F858230004FFFFF /* AssertJSONTwoWaySerialization.swift */; }; + FEAE9AB01FEC4FF500B52CA9 /* AssertProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE58F5471F7EBF04009A1B4E /* AssertProperties.swift */; }; + FEAE9AB11FEC4FF500B52CA9 /* AssertUpdateRequestJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795A91FA7915C0030C395 /* AssertUpdateRequestJSON.swift */; }; + FEAE9AB21FEC4FF500B52CA9 /* AssertValidatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2877ED1FBB9803004503FB /* AssertValidatable.swift */; }; + FEAE9AB31FEC4FF800B52CA9 /* CustomFieldsDataProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE58F5651F7EEA29009A1B4E /* CustomFieldsDataProvider.swift */; }; + FEAE9AB41FEC4FF800B52CA9 /* JSONDataProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE58F5601F7EE91D009A1B4E /* JSONDataProvider.swift */; }; + FEAE9AB51FEC4FF800B52CA9 /* ObjectProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795CB1FA7A3AD0030C395 /* ObjectProvider.swift */; }; + FEAE9AB61FEC4FF800B52CA9 /* ValidatableObjectProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2877EF1FBB9A80004503FB /* ValidatableObjectProvider.swift */; }; + FEAE9AB71FEC4FF900B52CA9 /* CustomFieldsDataProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE58F5651F7EEA29009A1B4E /* CustomFieldsDataProvider.swift */; }; + FEAE9AB81FEC4FF900B52CA9 /* JSONDataProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE58F5601F7EE91D009A1B4E /* JSONDataProvider.swift */; }; + FEAE9AB91FEC4FF900B52CA9 /* ObjectProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795CB1FA7A3AD0030C395 /* ObjectProvider.swift */; }; + FEAE9ABA1FEC4FF900B52CA9 /* ValidatableObjectProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2877EF1FBB9A80004503FB /* ValidatableObjectProvider.swift */; }; + FEAE9ABB1FEC4FFE00B52CA9 /* AddRequestJSONTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795E71FA7EB380030C395 /* AddRequestJSONTests.swift */; }; + FEAE9ABC1FEC4FFE00B52CA9 /* EquatableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEE774341FA8E2480016AACE /* EquatableTests.swift */; }; + FEAE9ABD1FEC4FFE00B52CA9 /* InitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795D11FA7C7960030C395 /* InitTests.swift */; }; + FEAE9ABE1FEC4FFE00B52CA9 /* JSONDeserializingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795D31FA7C7A50030C395 /* JSONDeserializingTests.swift */; }; + FEAE9ABF1FEC4FFE00B52CA9 /* JSONSerializingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795D51FA7C7B60030C395 /* JSONSerializingTests.swift */; }; + FEAE9AC01FEC4FFE00B52CA9 /* JSONTwoWaySerializationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795D71FA7C7D00030C395 /* JSONTwoWaySerializationTests.swift */; }; + FEAE9AC11FEC4FFE00B52CA9 /* UpdateRequestJSONTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795DB1FA7C7FA0030C395 /* UpdateRequestJSONTests.swift */; }; + FEAE9AC21FEC4FFE00B52CA9 /* ValidatableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2877EB1FBB979C004503FB /* ValidatableTests.swift */; }; + FEAE9AC31FEC4FFE00B52CA9 /* VariablePropertyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795D91FA7C7E30030C395 /* VariablePropertyTests.swift */; }; + FEAE9AC41FEC4FFF00B52CA9 /* AddRequestJSONTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795E71FA7EB380030C395 /* AddRequestJSONTests.swift */; }; + FEAE9AC51FEC4FFF00B52CA9 /* EquatableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEE774341FA8E2480016AACE /* EquatableTests.swift */; }; + FEAE9AC61FEC4FFF00B52CA9 /* InitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795D11FA7C7960030C395 /* InitTests.swift */; }; + FEAE9AC71FEC4FFF00B52CA9 /* JSONDeserializingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795D31FA7C7A50030C395 /* JSONDeserializingTests.swift */; }; + FEAE9AC81FEC4FFF00B52CA9 /* JSONSerializingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795D51FA7C7B60030C395 /* JSONSerializingTests.swift */; }; + FEAE9AC91FEC4FFF00B52CA9 /* JSONTwoWaySerializationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795D71FA7C7D00030C395 /* JSONTwoWaySerializationTests.swift */; }; + FEAE9ACA1FEC4FFF00B52CA9 /* UpdateRequestJSONTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795DB1FA7C7FA0030C395 /* UpdateRequestJSONTests.swift */; }; + FEAE9ACB1FEC4FFF00B52CA9 /* ValidatableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2877EB1FBB979C004503FB /* ValidatableTests.swift */; }; + FEAE9ACC1FEC4FFF00B52CA9 /* VariablePropertyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795D91FA7C7E30030C395 /* VariablePropertyTests.swift */; }; + FEAE9ACD1FEC500200B52CA9 /* CustomFieldsContainerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE22458B1F75BECC009F2B2B /* CustomFieldsContainerTests.swift */; }; + FEAE9ACE1FEC500200B52CA9 /* ErrorContainerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEC2140A1FD7533000036B17 /* ErrorContainerTests.swift */; }; + FEAE9ACF1FEC500200B52CA9 /* JSONDictionaryContainerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE51EFA11F882454007012E0 /* JSONDictionaryContainerTests.swift */; }; + FEAE9AD01FEC500200B52CA9 /* CustomFieldsContainerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE22458B1F75BECC009F2B2B /* CustomFieldsContainerTests.swift */; }; + FEAE9AD11FEC500200B52CA9 /* ErrorContainerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEC2140A1FD7533000036B17 /* ErrorContainerTests.swift */; }; + FEAE9AD21FEC500200B52CA9 /* JSONDictionaryContainerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE51EFA11F882454007012E0 /* JSONDictionaryContainerTests.swift */; }; + FEAE9AD31FEC500600B52CA9 /* Array+ContentComparisonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE549C451FBE60F3008CDFCE /* Array+ContentComparisonTests.swift */; }; + FEAE9AD41FEC500600B52CA9 /* Array+RandomTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FECFE7FE1FD9E1AE00968EA3 /* Array+RandomTests.swift */; }; + FEAE9AD51FEC500600B52CA9 /* Equatable+OptionalArrayTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE1474D51FAA475A0049DA84 /* Equatable+OptionalArrayTests.swift */; }; + FEAE9AD61FEC500700B52CA9 /* Array+ContentComparisonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE549C451FBE60F3008CDFCE /* Array+ContentComparisonTests.swift */; }; + FEAE9AD71FEC500700B52CA9 /* Array+RandomTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FECFE7FE1FD9E1AE00968EA3 /* Array+RandomTests.swift */; }; + FEAE9AD81FEC500700B52CA9 /* Equatable+OptionalArrayTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE1474D51FAA475A0049DA84 /* Equatable+OptionalArrayTests.swift */; }; + FEAE9AD91FEC500900B52CA9 /* AsyncOperationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEC2140D1FD7537800036B17 /* AsyncOperationTests.swift */; }; + FEAE9ADA1FEC500A00B52CA9 /* AsyncOperationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEC2140D1FD7537800036B17 /* AsyncOperationTests.swift */; }; + FEAE9ADB1FEC500D00B52CA9 /* ModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE018DC61F85708C001A2FEF /* ModelTests.swift */; }; + FEAE9ADC1FEC500D00B52CA9 /* ModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE018DC61F85708C001A2FEF /* ModelTests.swift */; }; + FEAE9ADD1FEC501000B52CA9 /* CustomFieldTypeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D481F75914300DF1039 /* CustomFieldTypeTests.swift */; }; + FEAE9ADE1FEC501000B52CA9 /* Project.SuiteModeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE791A241F7D9FFA00D7E870 /* Project.SuiteModeTests.swift */; }; + FEAE9ADF1FEC501000B52CA9 /* UniqueSelectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FECF66751FDF593600015CC4 /* UniqueSelectionTests.swift */; }; + FEAE9AE01FEC501100B52CA9 /* CustomFieldTypeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D481F75914300DF1039 /* CustomFieldTypeTests.swift */; }; + FEAE9AE11FEC501100B52CA9 /* Project.SuiteModeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE791A241F7D9FFA00D7E870 /* Project.SuiteModeTests.swift */; }; + FEAE9AE21FEC501100B52CA9 /* UniqueSelectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FECF66751FDF593600015CC4 /* UniqueSelectionTests.swift */; }; + FEAE9AE31FEC501400B52CA9 /* ConfigTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D461F75913900DF1039 /* ConfigTests.swift */; }; + FEAE9AE41FEC501400B52CA9 /* Config.ContextTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEAE7F951F7C5D0C00906FE1 /* Config.ContextTests.swift */; }; + FEAE9AE51FEC501400B52CA9 /* CaseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D261F75908100DF1039 /* CaseTests.swift */; }; + FEAE9AE61FEC501400B52CA9 /* CaseFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D441F75913000DF1039 /* CaseFieldTests.swift */; }; + FEAE9AE71FEC501400B52CA9 /* CaseTypeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D281F75909600DF1039 /* CaseTypeTests.swift */; }; + FEAE9AE81FEC501400B52CA9 /* ConfigurationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D2A1F7590A100DF1039 /* ConfigurationTests.swift */; }; + FEAE9AE91FEC501400B52CA9 /* ConfigurationGroupTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D2C1F7590B000DF1039 /* ConfigurationGroupTests.swift */; }; + FEAE9AEA1FEC501400B52CA9 /* MilestoneTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D2E1F7590BA00DF1039 /* MilestoneTests.swift */; }; + FEAE9AEB1FEC501400B52CA9 /* PlanTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D301F7590C700DF1039 /* PlanTests.swift */; }; + FEAE9AEC1FEC501400B52CA9 /* Plan.EntryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE30A6521F7C261400CE7D6B /* Plan.EntryTests.swift */; }; + FEAE9AED1FEC501400B52CA9 /* PriorityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D321F7590D200DF1039 /* PriorityTests.swift */; }; + FEAE9AEE1FEC501400B52CA9 /* ProjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D341F7590DC00DF1039 /* ProjectTests.swift */; }; + FEAE9AEF1FEC501400B52CA9 /* ResultTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D361F7590E500DF1039 /* ResultTests.swift */; }; + FEAE9AF01FEC501400B52CA9 /* ResultFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D4A1F75914E00DF1039 /* ResultFieldTests.swift */; }; + FEAE9AF11FEC501400B52CA9 /* RunTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D381F7590F000DF1039 /* RunTests.swift */; }; + FEAE9AF21FEC501400B52CA9 /* SectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D3A1F7590F900DF1039 /* SectionTests.swift */; }; + FEAE9AF31FEC501400B52CA9 /* StatusTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FED295481FA1117F00746DAB /* StatusTests.swift */; }; + FEAE9AF41FEC501400B52CA9 /* SuiteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D3C1F75910500DF1039 /* SuiteTests.swift */; }; + FEAE9AF51FEC501400B52CA9 /* TemplateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D3E1F75910E00DF1039 /* TemplateTests.swift */; }; + FEAE9AF61FEC501400B52CA9 /* TestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D401F75911A00DF1039 /* TestTests.swift */; }; + FEAE9AF71FEC501400B52CA9 /* UserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D421F75912400DF1039 /* UserTests.swift */; }; + FEAE9AF81FEC501400B52CA9 /* ConfigTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D461F75913900DF1039 /* ConfigTests.swift */; }; + FEAE9AF91FEC501400B52CA9 /* Config.ContextTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEAE7F951F7C5D0C00906FE1 /* Config.ContextTests.swift */; }; + FEAE9AFA1FEC501400B52CA9 /* CaseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D261F75908100DF1039 /* CaseTests.swift */; }; + FEAE9AFB1FEC501400B52CA9 /* CaseFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D441F75913000DF1039 /* CaseFieldTests.swift */; }; + FEAE9AFC1FEC501400B52CA9 /* CaseTypeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D281F75909600DF1039 /* CaseTypeTests.swift */; }; + FEAE9AFD1FEC501400B52CA9 /* ConfigurationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D2A1F7590A100DF1039 /* ConfigurationTests.swift */; }; + FEAE9AFE1FEC501400B52CA9 /* ConfigurationGroupTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D2C1F7590B000DF1039 /* ConfigurationGroupTests.swift */; }; + FEAE9AFF1FEC501400B52CA9 /* MilestoneTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D2E1F7590BA00DF1039 /* MilestoneTests.swift */; }; + FEAE9B001FEC501400B52CA9 /* PlanTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D301F7590C700DF1039 /* PlanTests.swift */; }; + FEAE9B011FEC501400B52CA9 /* Plan.EntryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE30A6521F7C261400CE7D6B /* Plan.EntryTests.swift */; }; + FEAE9B021FEC501400B52CA9 /* PriorityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D321F7590D200DF1039 /* PriorityTests.swift */; }; + FEAE9B031FEC501400B52CA9 /* ProjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D341F7590DC00DF1039 /* ProjectTests.swift */; }; + FEAE9B041FEC501400B52CA9 /* ResultTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D361F7590E500DF1039 /* ResultTests.swift */; }; + FEAE9B051FEC501400B52CA9 /* ResultFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D4A1F75914E00DF1039 /* ResultFieldTests.swift */; }; + FEAE9B061FEC501400B52CA9 /* RunTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D381F7590F000DF1039 /* RunTests.swift */; }; + FEAE9B071FEC501400B52CA9 /* SectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D3A1F7590F900DF1039 /* SectionTests.swift */; }; + FEAE9B081FEC501400B52CA9 /* StatusTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FED295481FA1117F00746DAB /* StatusTests.swift */; }; + FEAE9B091FEC501400B52CA9 /* SuiteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D3C1F75910500DF1039 /* SuiteTests.swift */; }; + FEAE9B0A1FEC501400B52CA9 /* TemplateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D3E1F75910E00DF1039 /* TemplateTests.swift */; }; + FEAE9B0B1FEC501400B52CA9 /* TestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D401F75911A00DF1039 /* TestTests.swift */; }; + FEAE9B0C1FEC501400B52CA9 /* UserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D421F75912400DF1039 /* UserTests.swift */; }; + FEAE9B0D1FEC501700B52CA9 /* FilterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2877F91FBBC47B004503FB /* FilterTests.swift */; }; + FEAE9B0E1FEC501800B52CA9 /* FilterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2877F91FBBC47B004503FB /* FilterTests.swift */; }; + FEAE9B0F1FEC501B00B52CA9 /* AddModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795CD1FA7A4530030C395 /* AddModelTests.swift */; }; + FEAE9B101FEC501B00B52CA9 /* UpdateModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795E01FA7D8360030C395 /* UpdateModelTests.swift */; }; + FEAE9B111FEC501C00B52CA9 /* AddModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795CD1FA7A4530030C395 /* AddModelTests.swift */; }; + FEAE9B121FEC501C00B52CA9 /* UpdateModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795E01FA7D8360030C395 /* UpdateModelTests.swift */; }; + FEAE9B131FEC501F00B52CA9 /* NewCaseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795AF1FA799150030C395 /* NewCaseTests.swift */; }; + FEAE9B141FEC501F00B52CA9 /* NewConfigurationGroupTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795B31FA799330030C395 /* NewConfigurationGroupTests.swift */; }; + FEAE9B151FEC501F00B52CA9 /* NewConfigurationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795B11FA799270030C395 /* NewConfigurationTests.swift */; }; + FEAE9B161FEC501F00B52CA9 /* NewMilestoneTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795B51FA799400030C395 /* NewMilestoneTests.swift */; }; + FEAE9B171FEC501F00B52CA9 /* NewPlanTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795B71FA799520030C395 /* NewPlanTests.swift */; }; + FEAE9B181FEC501F00B52CA9 /* NewPlan.EntryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795B91FA7996F0030C395 /* NewPlan.EntryTests.swift */; }; + FEAE9B191FEC501F00B52CA9 /* NewPlan.Entry.RunTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE64D1571FBD0C3700ABA133 /* NewPlan.Entry.RunTests.swift */; }; + FEAE9B1A1FEC501F00B52CA9 /* NewProjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795BB1FA7998F0030C395 /* NewProjectTests.swift */; }; + FEAE9B1B1FEC501F00B52CA9 /* NewResultTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795BD1FA7999C0030C395 /* NewResultTests.swift */; }; + FEAE9B1C1FEC501F00B52CA9 /* NewCaseResultsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE208DF21FBB714B0065BE88 /* NewCaseResultsTests.swift */; }; + FEAE9B1D1FEC501F00B52CA9 /* NewCaseResults.ResultTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE208DF41FBB716B0065BE88 /* NewCaseResults.ResultTests.swift */; }; + FEAE9B1E1FEC501F00B52CA9 /* NewTestResultsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795BF1FA799A90030C395 /* NewTestResultsTests.swift */; }; + FEAE9B1F1FEC501F00B52CA9 /* NewTestResults.ResultTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795C11FA799B70030C395 /* NewTestResults.ResultTests.swift */; }; + FEAE9B201FEC501F00B52CA9 /* NewRunTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795C31FA799D10030C395 /* NewRunTests.swift */; }; + FEAE9B211FEC501F00B52CA9 /* NewSectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795C51FA799E30030C395 /* NewSectionTests.swift */; }; + FEAE9B221FEC501F00B52CA9 /* NewSuiteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795C71FA799F50030C395 /* NewSuiteTests.swift */; }; + FEAE9B231FEC502000B52CA9 /* NewCaseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795AF1FA799150030C395 /* NewCaseTests.swift */; }; + FEAE9B241FEC502000B52CA9 /* NewConfigurationGroupTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795B31FA799330030C395 /* NewConfigurationGroupTests.swift */; }; + FEAE9B251FEC502000B52CA9 /* NewConfigurationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795B11FA799270030C395 /* NewConfigurationTests.swift */; }; + FEAE9B261FEC502000B52CA9 /* NewMilestoneTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795B51FA799400030C395 /* NewMilestoneTests.swift */; }; + FEAE9B271FEC502000B52CA9 /* NewPlanTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795B71FA799520030C395 /* NewPlanTests.swift */; }; + FEAE9B281FEC502000B52CA9 /* NewPlan.EntryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795B91FA7996F0030C395 /* NewPlan.EntryTests.swift */; }; + FEAE9B291FEC502000B52CA9 /* NewPlan.Entry.RunTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE64D1571FBD0C3700ABA133 /* NewPlan.Entry.RunTests.swift */; }; + FEAE9B2A1FEC502000B52CA9 /* NewProjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795BB1FA7998F0030C395 /* NewProjectTests.swift */; }; + FEAE9B2B1FEC502000B52CA9 /* NewResultTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795BD1FA7999C0030C395 /* NewResultTests.swift */; }; + FEAE9B2C1FEC502000B52CA9 /* NewCaseResultsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE208DF21FBB714B0065BE88 /* NewCaseResultsTests.swift */; }; + FEAE9B2D1FEC502000B52CA9 /* NewCaseResults.ResultTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE208DF41FBB716B0065BE88 /* NewCaseResults.ResultTests.swift */; }; + FEAE9B2E1FEC502000B52CA9 /* NewTestResultsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795BF1FA799A90030C395 /* NewTestResultsTests.swift */; }; + FEAE9B2F1FEC502000B52CA9 /* NewTestResults.ResultTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795C11FA799B70030C395 /* NewTestResults.ResultTests.swift */; }; + FEAE9B301FEC502000B52CA9 /* NewRunTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795C31FA799D10030C395 /* NewRunTests.swift */; }; + FEAE9B311FEC502000B52CA9 /* NewSectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795C51FA799E30030C395 /* NewSectionTests.swift */; }; + FEAE9B321FEC502000B52CA9 /* NewSuiteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795C71FA799F50030C395 /* NewSuiteTests.swift */; }; + FEAE9B331FEC502300B52CA9 /* UpdatePlanEntryRunsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795C91FA79A070030C395 /* UpdatePlanEntryRunsTests.swift */; }; + FEAE9B351FEC502300B52CA9 /* UpdatePlanEntryRunsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795C91FA79A070030C395 /* UpdatePlanEntryRunsTests.swift */; }; + FEAF0BFE1F7D9F15001D4F10 /* Project.SuiteMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEAF0BFD1F7D9F15001D4F10 /* Project.SuiteMode.swift */; }; + FEB546841F9135CB00AA6DA5 /* AddRequestJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEB546831F9135CB00AA6DA5 /* AddRequestJSON.swift */; }; + FEB546861F9135D500AA6DA5 /* AddRequestJSONKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEB546851F9135D500AA6DA5 /* AddRequestJSONKeys.swift */; }; + FEB546881F9170A900AA6DA5 /* Outcome.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEB546871F9170A900AA6DA5 /* Outcome.swift */; }; + FEBDA3C11FD1C7F400124430 /* ErrorContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEBDA3C01FD1C7F400124430 /* ErrorContainer.swift */; }; + FEBDA3C21FD1C88300124430 /* ObjectAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE894C251F5A25CA0057E021 /* ObjectAPI.swift */; }; + FEBF50371FCF3E91005B86B7 /* AsyncOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEBF50361FCF3E91005B86B7 /* AsyncOperation.swift */; }; + FEBFEEB91FE2EAAB00E7FE1B /* TestCredentials.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEBFEEB81FE2EAAB00E7FE1B /* TestCredentials.swift */; }; + FEBFEEBB1FE2EAB900E7FE1B /* TestCredentials.json in Resources */ = {isa = PBXBuildFile; fileRef = FEBFEEBA1FE2EAB900E7FE1B /* TestCredentials.json */; }; + FEC214001FD7302900036B17 /* ObjectAPI.MatchErrorDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEC213FF1FD7302900036B17 /* ObjectAPI.MatchErrorDebug.swift */; }; + FEC214071FD741F700036B17 /* DebugDescription.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEC214061FD741F700036B17 /* DebugDescription.swift */; }; + FEC214091FD7420300036B17 /* DebugDetails.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEC214081FD7420300036B17 /* DebugDetails.swift */; }; + FEC2140B1FD7533000036B17 /* ErrorContainerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEC2140A1FD7533000036B17 /* ErrorContainerTests.swift */; }; + FEC2140E1FD7537800036B17 /* AsyncOperationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEC2140D1FD7537800036B17 /* AsyncOperationTests.swift */; }; + FECF66701FDF05DB00015CC4 /* GetProjectOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = FECF666F1FDF05DB00015CC4 /* GetProjectOperation.swift */; }; + FECF66761FDF593600015CC4 /* UniqueSelectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FECF66751FDF593600015CC4 /* UniqueSelectionTests.swift */; }; + FECFE7FF1FD9E1AE00968EA3 /* Array+RandomTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FECFE7FE1FD9E1AE00968EA3 /* Array+RandomTests.swift */; }; + FED295471FA10E9300746DAB /* Status.swift in Sources */ = {isa = PBXBuildFile; fileRef = FED295461FA10E9300746DAB /* Status.swift */; }; + FED295491FA1117F00746DAB /* StatusTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FED295481FA1117F00746DAB /* StatusTests.swift */; }; + FEDCBEEF1F96BEE70083AD46 /* NewConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEDCBEEE1F96BEE70083AD46 /* NewConfiguration.swift */; }; + FEDCBEF11F96BEF10083AD46 /* NewConfigurationGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEDCBEF01F96BEF10083AD46 /* NewConfigurationGroup.swift */; }; + FEDCBEF31F96BF020083AD46 /* NewMilestone.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEDCBEF21F96BF020083AD46 /* NewMilestone.swift */; }; + FEDCBEF51F96BF130083AD46 /* NewPlan.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEDCBEF41F96BF130083AD46 /* NewPlan.swift */; }; + FEDCBEF71F96BF290083AD46 /* NewProject.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEDCBEF61F96BF290083AD46 /* NewProject.swift */; }; + FEDCBEF91F96BF3A0083AD46 /* NewResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEDCBEF81F96BF3A0083AD46 /* NewResult.swift */; }; + FEDCBEFB1F96BF540083AD46 /* NewRun.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEDCBEFA1F96BF540083AD46 /* NewRun.swift */; }; + FEDCBEFD1F96BF610083AD46 /* NewSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEDCBEFC1F96BF610083AD46 /* NewSection.swift */; }; + FEDCBEFF1F96BF730083AD46 /* NewSuite.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEDCBEFE1F96BF730083AD46 /* NewSuite.swift */; }; + FEDD80781F69CA3C00D56EF9 /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A9641F59F489007E946D /* Result.swift */; }; + FEDD80791F69D7A700D56EF9 /* Project.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A9621F59F478007E946D /* Project.swift */; }; + FEDD807A1F69DE1A00D56EF9 /* Priority.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A9601F59F46C007E946D /* Priority.swift */; }; + FEDFF2751FE1B09000AEB3D6 /* SingleMatchError.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEDFF2741FE1B09000AEB3D6 /* SingleMatchError.swift */; }; + FEDFF2771FE1B0A000AEB3D6 /* MultipleMatchError.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEDFF2761FE1B0A000AEB3D6 /* MultipleMatchError.swift */; }; + FEE774351FA8E2480016AACE /* EquatableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEE774341FA8E2480016AACE /* EquatableTests.swift */; }; + FEE774371FA8EA980016AACE /* AssertEquatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEE774361FA8EA980016AACE /* AssertEquatable.swift */; }; + FEEC443B1FB4D1BC0042BD5A /* Filter.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEEC443A1FB4D1BC0042BD5A /* Filter.swift */; }; + FEEC443E1FB4D23F0042BD5A /* Filter.Value.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEEC443D1FB4D23F0042BD5A /* Filter.Value.swift */; }; + FEEC44401FB4D3270042BD5A /* QueryItemProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEEC443F1FB4D3270042BD5A /* QueryItemProvider.swift */; }; + FEF172631F857F53004FFFFF /* AssertJSONSerializing.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEF172621F857F53004FFFFF /* AssertJSONSerializing.swift */; }; + FEF172661F858880004FFFFF /* AssertJSONTwoWaySerialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEF172641F858230004FFFFF /* AssertJSONTwoWaySerialization.swift */; }; + FEF9CE7E1F7C538F0078CD4E /* Plan.EntryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE30A6521F7C261400CE7D6B /* Plan.EntryTests.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + FE23A9271F59F3A0007E946D /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FE23A9131F59F39F007E946D /* Project object */; + proxyType = 1; + remoteGlobalIDString = FE23A91B1F59F39F007E946D; + remoteInfo = QuizTrain; + }; + FEAE99691FEC3BCE00B52CA9 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FE23A9131F59F39F007E946D /* Project object */; + proxyType = 1; + remoteGlobalIDString = FEAE995E1FEC3BCD00B52CA9; + remoteInfo = "QuizTrain-tvOS"; + }; + FEAE99851FEC3BEB00B52CA9 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FE23A9131F59F39F007E946D /* Project object */; + proxyType = 1; + remoteGlobalIDString = FEAE997A1FEC3BEB00B52CA9; + remoteInfo = "QuizTrain-macOS"; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + FE018DC61F85708C001A2FEF /* ModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModelTests.swift; sourceTree = ""; }; + FE09FFB31FE320CA0009FB2F /* .swiftlint.yml */ = {isa = PBXFileReference; lastKnownFileType = text; path = .swiftlint.yml; sourceTree = ""; }; + FE09FFCF1FE325CE0009FB2F /* .gitignore */ = {isa = PBXFileReference; lastKnownFileType = text; path = .gitignore; sourceTree = ""; }; + FE11181B1F6C3A4B00D24A5F /* Config.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Config.swift; sourceTree = ""; }; + FE1474D51FAA475A0049DA84 /* Equatable+OptionalArrayTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Equatable+OptionalArrayTests.swift"; sourceTree = ""; }; + FE1474D91FAA698F0049DA84 /* ObjectAPI.RequestErrorDebug.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObjectAPI.RequestErrorDebug.swift; sourceTree = ""; }; + FE1474DB1FAA69AA0049DA84 /* ObjectAPI.DataRequestErrorDebug.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObjectAPI.DataRequestErrorDebug.swift; sourceTree = ""; }; + FE1474DD1FAA69B70049DA84 /* ObjectAPI.UpdateRequestErrorDebug.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObjectAPI.UpdateRequestErrorDebug.swift; sourceTree = ""; }; + FE1474E11FAA69F70049DA84 /* ObjectAPI.ClientErrorDebug.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObjectAPI.ClientErrorDebug.swift; sourceTree = ""; }; + FE1474E41FAA6A0D0049DA84 /* ObjectAPI.ServerErrorDebug.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObjectAPI.ServerErrorDebug.swift; sourceTree = ""; }; + FE1474E71FAA6A270049DA84 /* ObjectAPI.StatusCodeErrorDebug.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObjectAPI.StatusCodeErrorDebug.swift; sourceTree = ""; }; + FE1474E91FAA6A340049DA84 /* ObjectAPI.DataProcessingErrorDebug.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObjectAPI.DataProcessingErrorDebug.swift; sourceTree = ""; }; + FE1474EB1FAA6A5B0049DA84 /* ObjectAPI.ObjectConversionErrorDebug.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObjectAPI.ObjectConversionErrorDebug.swift; sourceTree = ""; }; + FE1F301B1FEC0C890046F99E /* Entities.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Entities.png; sourceTree = ""; }; + FE1FB1031F9131B200383724 /* UpdateRequestJSON.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateRequestJSON.swift; sourceTree = ""; }; + FE208DEA1FBB69B80065BE88 /* NewTestResults.Result.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewTestResults.Result.swift; sourceTree = ""; }; + FE208DEC1FBB69C60065BE88 /* NewCaseResults.Result.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewCaseResults.Result.swift; sourceTree = ""; }; + FE208DEE1FBB6B920065BE88 /* NewTestResults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewTestResults.swift; sourceTree = ""; }; + FE208DF01FBB6BA10065BE88 /* NewCaseResults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewCaseResults.swift; sourceTree = ""; }; + FE208DF21FBB714B0065BE88 /* NewCaseResultsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NewCaseResultsTests.swift; sourceTree = ""; }; + FE208DF41FBB716B0065BE88 /* NewCaseResults.ResultTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NewCaseResults.ResultTests.swift; sourceTree = ""; }; + FE208DF91FBB95150065BE88 /* Validatable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Validatable.swift; sourceTree = ""; }; + FE20D03E1FD876820057A45C /* Identifiable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Identifiable.swift; sourceTree = ""; }; + FE2245841F75AC82009F2B2B /* CustomFieldsContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomFieldsContainer.swift; sourceTree = ""; }; + FE22458B1F75BECC009F2B2B /* CustomFieldsContainerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomFieldsContainerTests.swift; sourceTree = ""; }; + FE23A91C1F59F3A0007E946D /* QuizTrain.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = QuizTrain.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + FE23A91F1F59F3A0007E946D /* QuizTrain.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = QuizTrain.h; sourceTree = ""; }; + FE23A9251F59F3A0007E946D /* QuizTrainTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = QuizTrainTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + FE23A92C1F59F3A0007E946D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + FE23A9541F59F415007E946D /* Case.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Case.swift; sourceTree = ""; }; + FE23A9561F59F42B007E946D /* CaseField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CaseField.swift; sourceTree = ""; }; + FE23A9581F59F437007E946D /* CaseType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CaseType.swift; sourceTree = ""; }; + FE23A95A1F59F443007E946D /* Configuration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Configuration.swift; sourceTree = ""; }; + FE23A95C1F59F450007E946D /* Milestone.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Milestone.swift; sourceTree = ""; }; + FE23A95E1F59F45C007E946D /* Plan.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Plan.swift; sourceTree = ""; }; + FE23A9601F59F46C007E946D /* Priority.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Priority.swift; sourceTree = ""; }; + FE23A9621F59F478007E946D /* Project.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Project.swift; sourceTree = ""; }; + FE23A9641F59F489007E946D /* Result.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Result.swift; sourceTree = ""; }; + FE23A9661F59F4B3007E946D /* ResultField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResultField.swift; sourceTree = ""; }; + FE23A9681F59F4BA007E946D /* Run.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Run.swift; sourceTree = ""; }; + FE23A96A1F59F4C3007E946D /* Section.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Section.swift; sourceTree = ""; }; + FE23A96C1F59F4CE007E946D /* Suite.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Suite.swift; sourceTree = ""; }; + FE23A96E1F59F4D9007E946D /* Template.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Template.swift; sourceTree = ""; }; + FE23A9701F59F4E2007E946D /* Test.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Test.swift; sourceTree = ""; }; + FE23A9721F59F4EB007E946D /* User.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = User.swift; sourceTree = ""; }; + FE23A9741F5A042D007E946D /* ConfigurationGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationGroup.swift; sourceTree = ""; }; + FE2877EB1FBB979C004503FB /* ValidatableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValidatableTests.swift; sourceTree = ""; }; + FE2877ED1FBB9803004503FB /* AssertValidatable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssertValidatable.swift; sourceTree = ""; }; + FE2877EF1FBB9A80004503FB /* ValidatableObjectProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValidatableObjectProvider.swift; sourceTree = ""; }; + FE2877F91FBBC47B004503FB /* FilterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterTests.swift; sourceTree = ""; }; + FE2D018A1FBCE70B00473B84 /* NewPlan.Entry.Run.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewPlan.Entry.Run.swift; sourceTree = ""; }; + FE2E774A1F85923F00EF5E54 /* Equatable+OptionalArray.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Equatable+OptionalArray.swift"; sourceTree = ""; }; + FE2F1AD61F844FCD00FF9E0C /* JSONSerializable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONSerializable.swift; sourceTree = ""; }; + FE30A6521F7C261400CE7D6B /* Plan.EntryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Plan.EntryTests.swift; sourceTree = ""; }; + FE331C1B1F6B406300F9A653 /* CustomFieldType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomFieldType.swift; sourceTree = ""; }; + FE3795A91FA7915C0030C395 /* AssertUpdateRequestJSON.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssertUpdateRequestJSON.swift; sourceTree = ""; }; + FE3795AF1FA799150030C395 /* NewCaseTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewCaseTests.swift; sourceTree = ""; }; + FE3795B11FA799270030C395 /* NewConfigurationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewConfigurationTests.swift; sourceTree = ""; }; + FE3795B31FA799330030C395 /* NewConfigurationGroupTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewConfigurationGroupTests.swift; sourceTree = ""; }; + FE3795B51FA799400030C395 /* NewMilestoneTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewMilestoneTests.swift; sourceTree = ""; }; + FE3795B71FA799520030C395 /* NewPlanTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewPlanTests.swift; sourceTree = ""; }; + FE3795B91FA7996F0030C395 /* NewPlan.EntryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewPlan.EntryTests.swift; sourceTree = ""; }; + FE3795BB1FA7998F0030C395 /* NewProjectTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewProjectTests.swift; sourceTree = ""; }; + FE3795BD1FA7999C0030C395 /* NewResultTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewResultTests.swift; sourceTree = ""; }; + FE3795BF1FA799A90030C395 /* NewTestResultsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewTestResultsTests.swift; sourceTree = ""; }; + FE3795C11FA799B70030C395 /* NewTestResults.ResultTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewTestResults.ResultTests.swift; sourceTree = ""; }; + FE3795C31FA799D10030C395 /* NewRunTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewRunTests.swift; sourceTree = ""; }; + FE3795C51FA799E30030C395 /* NewSectionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewSectionTests.swift; sourceTree = ""; }; + FE3795C71FA799F50030C395 /* NewSuiteTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewSuiteTests.swift; sourceTree = ""; }; + FE3795C91FA79A070030C395 /* UpdatePlanEntryRunsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdatePlanEntryRunsTests.swift; sourceTree = ""; }; + FE3795CB1FA7A3AD0030C395 /* ObjectProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObjectProvider.swift; sourceTree = ""; }; + FE3795CD1FA7A4530030C395 /* AddModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddModelTests.swift; sourceTree = ""; }; + FE3795D11FA7C7960030C395 /* InitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InitTests.swift; sourceTree = ""; }; + FE3795D31FA7C7A50030C395 /* JSONDeserializingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONDeserializingTests.swift; sourceTree = ""; }; + FE3795D51FA7C7B60030C395 /* JSONSerializingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONSerializingTests.swift; sourceTree = ""; }; + FE3795D71FA7C7D00030C395 /* JSONTwoWaySerializationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONTwoWaySerializationTests.swift; sourceTree = ""; }; + FE3795D91FA7C7E30030C395 /* VariablePropertyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VariablePropertyTests.swift; sourceTree = ""; }; + FE3795DB1FA7C7FA0030C395 /* UpdateRequestJSONTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateRequestJSONTests.swift; sourceTree = ""; }; + FE3795DD1FA7C90F0030C395 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; + FE3795E01FA7D8360030C395 /* UpdateModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateModelTests.swift; sourceTree = ""; }; + FE3795E51FA7EA6C0030C395 /* AssertAddRequestJSON.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssertAddRequestJSON.swift; sourceTree = ""; }; + FE3795E71FA7EB380030C395 /* AddRequestJSONTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddRequestJSONTests.swift; sourceTree = ""; }; + FE3899161FCF2BDE0032E265 /* GetTemplatesOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetTemplatesOperation.swift; sourceTree = ""; }; + FE4E11D61F7C4112004A315E /* Plan.Entry.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Plan.Entry.swift; sourceTree = ""; }; + FE4F8F381F84361F00447F9E /* NewCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewCase.swift; sourceTree = ""; }; + FE4F8F3A1F8437DE00447F9E /* JSONDeserializable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONDeserializable.swift; sourceTree = ""; }; + FE51EFA11F882454007012E0 /* JSONDictionaryContainerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONDictionaryContainerTests.swift; sourceTree = ""; }; + FE5259111F82D1AD00E0DDB7 /* JSONKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONKey.swift; sourceTree = ""; }; + FE549C451FBE60F3008CDFCE /* Array+ContentComparisonTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+ContentComparisonTests.swift"; sourceTree = ""; }; + FE5869D61F7478D600BE5C5C /* CustomFields.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomFields.swift; sourceTree = ""; }; + FE58F5471F7EBF04009A1B4E /* AssertProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssertProperties.swift; sourceTree = ""; }; + FE58F5491F7EBF12009A1B4E /* AssertCustomFields.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssertCustomFields.swift; sourceTree = ""; }; + FE58F54B1F7EBF24009A1B4E /* AssertJSONDeserializing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssertJSONDeserializing.swift; sourceTree = ""; }; + FE58F5601F7EE91D009A1B4E /* JSONDataProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONDataProvider.swift; sourceTree = ""; }; + FE58F5651F7EEA29009A1B4E /* CustomFieldsDataProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomFieldsDataProvider.swift; sourceTree = ""; }; + FE5A24F11FE092D300198848 /* UniqueSelection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UniqueSelection.swift; sourceTree = ""; }; + FE5E1A2F1F8804E3001E479B /* JSONDictionaryContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONDictionaryContainer.swift; sourceTree = ""; }; + FE63715E1FD61CA500192CED /* GetConfigurationGroupsOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetConfigurationGroupsOperation.swift; sourceTree = ""; }; + FE64D1571FBD0C3700ABA133 /* NewPlan.Entry.RunTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewPlan.Entry.RunTests.swift; sourceTree = ""; }; + FE6838E11F8FE10500431C1C /* UpdateRequestJSONKeys.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateRequestJSONKeys.swift; sourceTree = ""; }; + FE6989201FA39B99006CC783 /* UpdatePlanEntryRuns.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdatePlanEntryRuns.swift; sourceTree = ""; }; + FE6A6D261F75908100DF1039 /* CaseTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CaseTests.swift; sourceTree = ""; }; + FE6A6D281F75909600DF1039 /* CaseTypeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CaseTypeTests.swift; sourceTree = ""; }; + FE6A6D2A1F7590A100DF1039 /* ConfigurationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationTests.swift; sourceTree = ""; }; + FE6A6D2C1F7590B000DF1039 /* ConfigurationGroupTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationGroupTests.swift; sourceTree = ""; }; + FE6A6D2E1F7590BA00DF1039 /* MilestoneTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MilestoneTests.swift; sourceTree = ""; }; + FE6A6D301F7590C700DF1039 /* PlanTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlanTests.swift; sourceTree = ""; }; + FE6A6D321F7590D200DF1039 /* PriorityTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PriorityTests.swift; sourceTree = ""; }; + FE6A6D341F7590DC00DF1039 /* ProjectTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectTests.swift; sourceTree = ""; }; + FE6A6D361F7590E500DF1039 /* ResultTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResultTests.swift; sourceTree = ""; }; + FE6A6D381F7590F000DF1039 /* RunTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunTests.swift; sourceTree = ""; }; + FE6A6D3A1F7590F900DF1039 /* SectionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionTests.swift; sourceTree = ""; }; + FE6A6D3C1F75910500DF1039 /* SuiteTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuiteTests.swift; sourceTree = ""; }; + FE6A6D3E1F75910E00DF1039 /* TemplateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateTests.swift; sourceTree = ""; }; + FE6A6D401F75911A00DF1039 /* TestTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestTests.swift; sourceTree = ""; }; + FE6A6D421F75912400DF1039 /* UserTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserTests.swift; sourceTree = ""; }; + FE6A6D441F75913000DF1039 /* CaseFieldTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CaseFieldTests.swift; sourceTree = ""; }; + FE6A6D461F75913900DF1039 /* ConfigTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigTests.swift; sourceTree = ""; }; + FE6A6D481F75914300DF1039 /* CustomFieldTypeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomFieldTypeTests.swift; sourceTree = ""; }; + FE6A6D4A1F75914E00DF1039 /* ResultFieldTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResultFieldTests.swift; sourceTree = ""; }; + FE6A6D4F1F75956C00DF1039 /* ObjectAPITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObjectAPITests.swift; sourceTree = ""; }; + FE6C8AE11F8BEA2E00F45642 /* MutableCustomFields.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MutableCustomFields.swift; sourceTree = ""; }; + FE791A241F7D9FFA00D7E870 /* Project.SuiteModeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Project.SuiteModeTests.swift; sourceTree = ""; }; + FE7B53921FBF5BB1003C26BD /* Array+ContentComparison.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Array+ContentComparison.swift"; sourceTree = ""; }; + FE7CD3E31F9ABDC300C6108E /* NewPlan.Entry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewPlan.Entry.swift; sourceTree = ""; }; + FE813A171F73126F00265569 /* JSONDictionary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONDictionary.swift; sourceTree = ""; }; + FE8464621FE307B1006CB58F /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; + FE8464641FE307BB006CB58F /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; + FE8464671FE319EA006CB58F /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + FE8464841FE31D4A006CB58F /* .swiftlint.yml */ = {isa = PBXFileReference; lastKnownFileType = text; path = .swiftlint.yml; sourceTree = ""; }; + FE8464851FE31D58006CB58F /* .swiftlint.yml */ = {isa = PBXFileReference; lastKnownFileType = text; path = .swiftlint.yml; sourceTree = ""; }; + FE894C251F5A25CA0057E021 /* ObjectAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObjectAPI.swift; sourceTree = ""; }; + FE8C2D591FBA391F005A4150 /* Date+Seconds.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+Seconds.swift"; sourceTree = ""; }; + FE978CE81F9528320005D181 /* API.RequestResultDebug.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = API.RequestResultDebug.swift; sourceTree = ""; }; + FE978CEA1F9528400005D181 /* API.RequestErrorDebug.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = API.RequestErrorDebug.swift; sourceTree = ""; }; + FE978CEC1F9532BA0005D181 /* URLRequestDebug.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLRequestDebug.swift; sourceTree = ""; }; + FEA348771F5A141300C1E37A /* API.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = API.swift; sourceTree = ""; }; + FEAE7F911F7C5ABE00906FE1 /* Config.Context.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Config.Context.swift; sourceTree = ""; }; + FEAE7F951F7C5D0C00906FE1 /* Config.ContextTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Config.ContextTests.swift; sourceTree = ""; }; + FEAE99511FEC38BA00B52CA9 /* QuizTrain.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = QuizTrain.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + FEAE995F1FEC3BCD00B52CA9 /* QuizTrain.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = QuizTrain.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + FEAE99671FEC3BCE00B52CA9 /* QuizTrainTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = QuizTrainTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + FEAE997B1FEC3BEB00B52CA9 /* QuizTrain.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = QuizTrain.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + FEAE99831FEC3BEB00B52CA9 /* QuizTrainTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = QuizTrainTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + FEAF0BFD1F7D9F15001D4F10 /* Project.SuiteMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Project.SuiteMode.swift; sourceTree = ""; }; + FEB546831F9135CB00AA6DA5 /* AddRequestJSON.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddRequestJSON.swift; sourceTree = ""; }; + FEB546851F9135D500AA6DA5 /* AddRequestJSONKeys.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddRequestJSONKeys.swift; sourceTree = ""; }; + FEB546871F9170A900AA6DA5 /* Outcome.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Outcome.swift; sourceTree = ""; }; + FEB67180200FF7EA006283A6 /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; + FEBDA3C01FD1C7F400124430 /* ErrorContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorContainer.swift; sourceTree = ""; }; + FEBF50361FCF3E91005B86B7 /* AsyncOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncOperation.swift; sourceTree = ""; }; + FEBFEEB81FE2EAAB00E7FE1B /* TestCredentials.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestCredentials.swift; sourceTree = ""; }; + FEBFEEBA1FE2EAB900E7FE1B /* TestCredentials.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = TestCredentials.json; sourceTree = ""; }; + FEC213FF1FD7302900036B17 /* ObjectAPI.MatchErrorDebug.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObjectAPI.MatchErrorDebug.swift; sourceTree = ""; }; + FEC214061FD741F700036B17 /* DebugDescription.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugDescription.swift; sourceTree = ""; }; + FEC214081FD7420300036B17 /* DebugDetails.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugDetails.swift; sourceTree = ""; }; + FEC2140A1FD7533000036B17 /* ErrorContainerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorContainerTests.swift; sourceTree = ""; }; + FEC2140D1FD7537800036B17 /* AsyncOperationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncOperationTests.swift; sourceTree = ""; }; + FECF666F1FDF05DB00015CC4 /* GetProjectOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetProjectOperation.swift; sourceTree = ""; }; + FECF66751FDF593600015CC4 /* UniqueSelectionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UniqueSelectionTests.swift; sourceTree = ""; }; + FECFE7FC1FD9E13500968EA3 /* Array+Random.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+Random.swift"; sourceTree = ""; }; + FECFE7FE1FD9E1AE00968EA3 /* Array+RandomTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+RandomTests.swift"; sourceTree = ""; }; + FED295461FA10E9300746DAB /* Status.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Status.swift; sourceTree = ""; }; + FED295481FA1117F00746DAB /* StatusTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusTests.swift; sourceTree = ""; }; + FEDCBEEE1F96BEE70083AD46 /* NewConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewConfiguration.swift; sourceTree = ""; }; + FEDCBEF01F96BEF10083AD46 /* NewConfigurationGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewConfigurationGroup.swift; sourceTree = ""; }; + FEDCBEF21F96BF020083AD46 /* NewMilestone.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewMilestone.swift; sourceTree = ""; }; + FEDCBEF41F96BF130083AD46 /* NewPlan.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewPlan.swift; sourceTree = ""; }; + FEDCBEF61F96BF290083AD46 /* NewProject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewProject.swift; sourceTree = ""; }; + FEDCBEF81F96BF3A0083AD46 /* NewResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewResult.swift; sourceTree = ""; }; + FEDCBEFA1F96BF540083AD46 /* NewRun.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewRun.swift; sourceTree = ""; }; + FEDCBEFC1F96BF610083AD46 /* NewSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewSection.swift; sourceTree = ""; }; + FEDCBEFE1F96BF730083AD46 /* NewSuite.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewSuite.swift; sourceTree = ""; }; + FEDFF2741FE1B09000AEB3D6 /* SingleMatchError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SingleMatchError.swift; sourceTree = ""; }; + FEDFF2761FE1B0A000AEB3D6 /* MultipleMatchError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultipleMatchError.swift; sourceTree = ""; }; + FEE774341FA8E2480016AACE /* EquatableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EquatableTests.swift; sourceTree = ""; }; + FEE774361FA8EA980016AACE /* AssertEquatable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssertEquatable.swift; sourceTree = ""; }; + FEEC443A1FB4D1BC0042BD5A /* Filter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Filter.swift; path = QuizTrain/Network/Filters/Filter.swift; sourceTree = SOURCE_ROOT; }; + FEEC443D1FB4D23F0042BD5A /* Filter.Value.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Filter.Value.swift; sourceTree = ""; }; + FEEC443F1FB4D3270042BD5A /* QueryItemProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueryItemProvider.swift; sourceTree = ""; }; + FEF172621F857F53004FFFFF /* AssertJSONSerializing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssertJSONSerializing.swift; sourceTree = ""; }; + FEF172641F858230004FFFFF /* AssertJSONTwoWaySerialization.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssertJSONTwoWaySerialization.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + FE23A9181F59F39F007E946D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FE23A9221F59F3A0007E946D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + FE23A9261F59F3A0007E946D /* QuizTrain.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FEAE994D1FEC38BA00B52CA9 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FEAE995B1FEC3BCD00B52CA9 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FEAE99641FEC3BCE00B52CA9 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + FEAE99681FEC3BCE00B52CA9 /* QuizTrain.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FEAE99771FEC3BEB00B52CA9 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FEAE99801FEC3BEB00B52CA9 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + FEAE99841FEC3BEB00B52CA9 /* QuizTrain.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + FE018DC91F85719A001A2FEF /* Tests */ = { + isa = PBXGroup; + children = ( + FE3795DD1FA7C90F0030C395 /* README.md */, + FE3795E71FA7EB380030C395 /* AddRequestJSONTests.swift */, + FEE774341FA8E2480016AACE /* EquatableTests.swift */, + FE3795D11FA7C7960030C395 /* InitTests.swift */, + FE3795D31FA7C7A50030C395 /* JSONDeserializingTests.swift */, + FE3795D51FA7C7B60030C395 /* JSONSerializingTests.swift */, + FE3795D71FA7C7D00030C395 /* JSONTwoWaySerializationTests.swift */, + FE3795DB1FA7C7FA0030C395 /* UpdateRequestJSONTests.swift */, + FE2877EB1FBB979C004503FB /* ValidatableTests.swift */, + FE3795D91FA7C7E30030C395 /* VariablePropertyTests.swift */, + ); + path = Tests; + sourceTree = ""; + }; + FE028C251F7037C1002582B3 /* Config */ = { + isa = PBXGroup; + children = ( + FE11181B1F6C3A4B00D24A5F /* Config.swift */, + FEAE7F911F7C5ABE00906FE1 /* Config.Context.swift */, + ); + path = Config; + sourceTree = ""; + }; + FE1474D71FAA484D0049DA84 /* Extensions */ = { + isa = PBXGroup; + children = ( + FE7B53921FBF5BB1003C26BD /* Array+ContentComparison.swift */, + FE8C2D591FBA391F005A4150 /* Date+Seconds.swift */, + FE2E774A1F85923F00EF5E54 /* Equatable+OptionalArray.swift */, + ); + path = Extensions; + sourceTree = ""; + }; + FE1474D81FAA48900049DA84 /* Extensions */ = { + isa = PBXGroup; + children = ( + FE549C451FBE60F3008CDFCE /* Array+ContentComparisonTests.swift */, + FECFE7FE1FD9E1AE00968EA3 /* Array+RandomTests.swift */, + FE1474D51FAA475A0049DA84 /* Equatable+OptionalArrayTests.swift */, + ); + path = Extensions; + sourceTree = ""; + }; + FE1474DF1FAA69D60049DA84 /* API */ = { + isa = PBXGroup; + children = ( + FE978CEA1F9528400005D181 /* API.RequestErrorDebug.swift */, + FE978CE81F9528320005D181 /* API.RequestResultDebug.swift */, + ); + path = API; + sourceTree = ""; + }; + FE1474E01FAA69DD0049DA84 /* ObjectAPI */ = { + isa = PBXGroup; + children = ( + FE1474E11FAA69F70049DA84 /* ObjectAPI.ClientErrorDebug.swift */, + FE1474E91FAA6A340049DA84 /* ObjectAPI.DataProcessingErrorDebug.swift */, + FE1474DB1FAA69AA0049DA84 /* ObjectAPI.DataRequestErrorDebug.swift */, + FEC213FF1FD7302900036B17 /* ObjectAPI.MatchErrorDebug.swift */, + FE1474EB1FAA6A5B0049DA84 /* ObjectAPI.ObjectConversionErrorDebug.swift */, + FE1474D91FAA698F0049DA84 /* ObjectAPI.RequestErrorDebug.swift */, + FE1474E41FAA6A0D0049DA84 /* ObjectAPI.ServerErrorDebug.swift */, + FE1474E71FAA6A270049DA84 /* ObjectAPI.StatusCodeErrorDebug.swift */, + FE1474DD1FAA69B70049DA84 /* ObjectAPI.UpdateRequestErrorDebug.swift */, + ); + path = ObjectAPI; + sourceTree = ""; + }; + FE20D03D1FD876710057A45C /* Identity */ = { + isa = PBXGroup; + children = ( + FE20D03E1FD876820057A45C /* Identifiable.swift */, + ); + path = Identity; + sourceTree = ""; + }; + FE2245861F75B66F009F2B2B /* Misc */ = { + isa = PBXGroup; + children = ( + FEB546821F91359100AA6DA5 /* Add */, + FE5E1A2C1F88049B001E479B /* Containment */, + FEC214051FD741ED00036B17 /* Debug */, + FEDFF2731FE1B07700AEB3D6 /* Errors */, + FE1474D71FAA484D0049DA84 /* Extensions */, + FE20D03D1FD876710057A45C /* Identity */, + FE5869D81F7485D100BE5C5C /* JSON */, + FEBF50351FCF3E85005B86B7 /* Operations */, + FEB546811F91358800AA6DA5 /* Update */, + FE6371601FD7108600192CED /* Validation */, + FEB546871F9170A900AA6DA5 /* Outcome.swift */, + FEEC443F1FB4D3270042BD5A /* QueryItemProvider.swift */, + FE5A24F11FE092D300198848 /* UniqueSelection.swift */, + ); + path = Misc; + sourceTree = ""; + }; + FE2245891F75BEB2009F2B2B /* Misc */ = { + isa = PBXGroup; + children = ( + FE22458A1F75BEB8009F2B2B /* Containment */, + FE1474D81FAA48900049DA84 /* Extensions */, + FEC2140C1FD7536700036B17 /* Operations */, + ); + path = Misc; + sourceTree = ""; + }; + FE22458A1F75BEB8009F2B2B /* Containment */ = { + isa = PBXGroup; + children = ( + FE6989271FA3BBC2006CC783 /* Containers */, + ); + path = Containment; + sourceTree = ""; + }; + FE23A9121F59F39F007E946D = { + isa = PBXGroup; + children = ( + FE23A91E1F59F3A0007E946D /* QuizTrain */, + FE23A9291F59F3A0007E946D /* QuizTrainTests */, + FE23A91D1F59F3A0007E946D /* Products */, + FE1F301B1FEC0C890046F99E /* Entities.png */, + FEB67180200FF7EA006283A6 /* LICENSE */, + FE8464621FE307B1006CB58F /* README.md */, + FE09FFCF1FE325CE0009FB2F /* .gitignore */, + FE09FFB31FE320CA0009FB2F /* .swiftlint.yml */, + ); + sourceTree = ""; + }; + FE23A91D1F59F3A0007E946D /* Products */ = { + isa = PBXGroup; + children = ( + FE23A91C1F59F3A0007E946D /* QuizTrain.framework */, + FE23A9251F59F3A0007E946D /* QuizTrainTests.xctest */, + FEAE99511FEC38BA00B52CA9 /* QuizTrain.framework */, + FEAE995F1FEC3BCD00B52CA9 /* QuizTrain.framework */, + FEAE99671FEC3BCE00B52CA9 /* QuizTrainTests.xctest */, + FEAE997B1FEC3BEB00B52CA9 /* QuizTrain.framework */, + FEAE99831FEC3BEB00B52CA9 /* QuizTrainTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + FE23A91E1F59F3A0007E946D /* QuizTrain */ = { + isa = PBXGroup; + children = ( + FE2245861F75B66F009F2B2B /* Misc */, + FE23A9361F59F3AF007E946D /* Models */, + FEA348591F5A13F700C1E37A /* Network */, + FE23A91F1F59F3A0007E946D /* QuizTrain.h */, + FE8464671FE319EA006CB58F /* Info.plist */, + FE8464841FE31D4A006CB58F /* .swiftlint.yml */, + ); + path = QuizTrain; + sourceTree = ""; + }; + FE23A9291F59F3A0007E946D /* QuizTrainTests */ = { + isa = PBXGroup; + children = ( + FE5A24F51FE099EB00198848 /* Testing Misc */, + FE58F5461F7EBEF8009A1B4E /* Testing Protocols */, + FE2245891F75BEB2009F2B2B /* Misc */, + FE6A6D241F75904B00DF1039 /* Models */, + FE6A6D4C1F75954D00DF1039 /* Network */, + FE23A92C1F59F3A0007E946D /* Info.plist */, + FE8464641FE307BB006CB58F /* README.md */, + FE8464851FE31D58006CB58F /* .swiftlint.yml */, + ); + path = QuizTrainTests; + sourceTree = ""; + }; + FE23A9361F59F3AF007E946D /* Models */ = { + isa = PBXGroup; + children = ( + FE52590D1F82A95100E0DDB7 /* Types */, + FE028C251F7037C1002582B3 /* Config */, + FE23A9541F59F415007E946D /* Case.swift */, + FE23A9561F59F42B007E946D /* CaseField.swift */, + FE23A9581F59F437007E946D /* CaseType.swift */, + FE23A95A1F59F443007E946D /* Configuration.swift */, + FE23A9741F5A042D007E946D /* ConfigurationGroup.swift */, + FE23A95C1F59F450007E946D /* Milestone.swift */, + FE23A95E1F59F45C007E946D /* Plan.swift */, + FE4E11D61F7C4112004A315E /* Plan.Entry.swift */, + FE23A9601F59F46C007E946D /* Priority.swift */, + FE23A9621F59F478007E946D /* Project.swift */, + FE23A9641F59F489007E946D /* Result.swift */, + FE23A9661F59F4B3007E946D /* ResultField.swift */, + FE23A9681F59F4BA007E946D /* Run.swift */, + FE23A96A1F59F4C3007E946D /* Section.swift */, + FED295461FA10E9300746DAB /* Status.swift */, + FE23A96C1F59F4CE007E946D /* Suite.swift */, + FE23A96E1F59F4D9007E946D /* Template.swift */, + FE23A9701F59F4E2007E946D /* Test.swift */, + FE23A9721F59F4EB007E946D /* User.swift */, + ); + path = Models; + sourceTree = ""; + }; + FE2877F81FBBC469004503FB /* Filters */ = { + isa = PBXGroup; + children = ( + FE2877F91FBBC47B004503FB /* FilterTests.swift */, + ); + path = Filters; + sourceTree = ""; + }; + FE3795E21FA7DB900030C395 /* Testing Protocols */ = { + isa = PBXGroup; + children = ( + FE018DC61F85708C001A2FEF /* ModelTests.swift */, + ); + path = "Testing Protocols"; + sourceTree = ""; + }; + FE3795E31FA7DB9E0030C395 /* Testing Protocols */ = { + isa = PBXGroup; + children = ( + FE3795CD1FA7A4530030C395 /* AddModelTests.swift */, + FE3795E01FA7D8360030C395 /* UpdateModelTests.swift */, + ); + path = "Testing Protocols"; + sourceTree = ""; + }; + FE47DC771FCE27B50048BACE /* Operations */ = { + isa = PBXGroup; + children = ( + FE63715E1FD61CA500192CED /* GetConfigurationGroupsOperation.swift */, + FECF666F1FDF05DB00015CC4 /* GetProjectOperation.swift */, + FE3899161FCF2BDE0032E265 /* GetTemplatesOperation.swift */, + ); + path = Operations; + sourceTree = ""; + }; + FE52590D1F82A95100E0DDB7 /* Types */ = { + isa = PBXGroup; + children = ( + FE331C1B1F6B406300F9A653 /* CustomFieldType.swift */, + FEAF0BFD1F7D9F15001D4F10 /* Project.SuiteMode.swift */, + ); + path = Types; + sourceTree = ""; + }; + FE52590E1F82A98200E0DDB7 /* Types */ = { + isa = PBXGroup; + children = ( + FE6A6D481F75914300DF1039 /* CustomFieldTypeTests.swift */, + FE791A241F7D9FFA00D7E870 /* Project.SuiteModeTests.swift */, + FECF66751FDF593600015CC4 /* UniqueSelectionTests.swift */, + ); + path = Types; + sourceTree = ""; + }; + FE5869D81F7485D100BE5C5C /* JSON */ = { + isa = PBXGroup; + children = ( + FE5259111F82D1AD00E0DDB7 /* JSONKey.swift */, + FE813A171F73126F00265569 /* JSONDictionary.swift */, + FE4F8F3A1F8437DE00447F9E /* JSONDeserializable.swift */, + FE2F1AD61F844FCD00FF9E0C /* JSONSerializable.swift */, + ); + path = JSON; + sourceTree = ""; + }; + FE58F5461F7EBEF8009A1B4E /* Testing Protocols */ = { + isa = PBXGroup; + children = ( + FE58F5621F7EE923009A1B4E /* Asserts */, + FE58F5641F7EE92B009A1B4E /* Providers */, + FE018DC91F85719A001A2FEF /* Tests */, + ); + path = "Testing Protocols"; + sourceTree = ""; + }; + FE58F5621F7EE923009A1B4E /* Asserts */ = { + isa = PBXGroup; + children = ( + FE3795E51FA7EA6C0030C395 /* AssertAddRequestJSON.swift */, + FE58F5491F7EBF12009A1B4E /* AssertCustomFields.swift */, + FEE774361FA8EA980016AACE /* AssertEquatable.swift */, + FE58F54B1F7EBF24009A1B4E /* AssertJSONDeserializing.swift */, + FEF172621F857F53004FFFFF /* AssertJSONSerializing.swift */, + FEF172641F858230004FFFFF /* AssertJSONTwoWaySerialization.swift */, + FE58F5471F7EBF04009A1B4E /* AssertProperties.swift */, + FE3795A91FA7915C0030C395 /* AssertUpdateRequestJSON.swift */, + FE2877ED1FBB9803004503FB /* AssertValidatable.swift */, + ); + path = Asserts; + sourceTree = ""; + }; + FE58F5641F7EE92B009A1B4E /* Providers */ = { + isa = PBXGroup; + children = ( + FE58F5651F7EEA29009A1B4E /* CustomFieldsDataProvider.swift */, + FE58F5601F7EE91D009A1B4E /* JSONDataProvider.swift */, + FE3795CB1FA7A3AD0030C395 /* ObjectProvider.swift */, + FE2877EF1FBB9A80004503FB /* ValidatableObjectProvider.swift */, + ); + path = Providers; + sourceTree = ""; + }; + FE5A24F51FE099EB00198848 /* Testing Misc */ = { + isa = PBXGroup; + children = ( + FECFE7FC1FD9E13500968EA3 /* Array+Random.swift */, + FEBFEEB81FE2EAAB00E7FE1B /* TestCredentials.swift */, + FEBFEEBA1FE2EAB900E7FE1B /* TestCredentials.json */, + ); + path = "Testing Misc"; + sourceTree = ""; + }; + FE5E1A2C1F88049B001E479B /* Containment */ = { + isa = PBXGroup; + children = ( + FE5E1A371F882003001E479B /* Protocols */, + FEDCBEED1F96BDC90083AD46 /* Containers */, + ); + path = Containment; + sourceTree = ""; + }; + FE5E1A371F882003001E479B /* Protocols */ = { + isa = PBXGroup; + children = ( + FE5869D61F7478D600BE5C5C /* CustomFields.swift */, + FE6C8AE11F8BEA2E00F45642 /* MutableCustomFields.swift */, + ); + path = Protocols; + sourceTree = ""; + }; + FE6371601FD7108600192CED /* Validation */ = { + isa = PBXGroup; + children = ( + FE208DF91FBB95150065BE88 /* Validatable.swift */, + ); + path = Validation; + sourceTree = ""; + }; + FE6989221FA3B12B006CC783 /* Add */ = { + isa = PBXGroup; + children = ( + FE4F8F381F84361F00447F9E /* NewCase.swift */, + FEDCBEEE1F96BEE70083AD46 /* NewConfiguration.swift */, + FEDCBEF01F96BEF10083AD46 /* NewConfigurationGroup.swift */, + FEDCBEF21F96BF020083AD46 /* NewMilestone.swift */, + FEDCBEF41F96BF130083AD46 /* NewPlan.swift */, + FE7CD3E31F9ABDC300C6108E /* NewPlan.Entry.swift */, + FE2D018A1FBCE70B00473B84 /* NewPlan.Entry.Run.swift */, + FEDCBEF61F96BF290083AD46 /* NewProject.swift */, + FEDCBEF81F96BF3A0083AD46 /* NewResult.swift */, + FE208DF01FBB6BA10065BE88 /* NewCaseResults.swift */, + FE208DEC1FBB69C60065BE88 /* NewCaseResults.Result.swift */, + FE208DEE1FBB6B920065BE88 /* NewTestResults.swift */, + FE208DEA1FBB69B80065BE88 /* NewTestResults.Result.swift */, + FEDCBEFA1F96BF540083AD46 /* NewRun.swift */, + FEDCBEFC1F96BF610083AD46 /* NewSection.swift */, + FEDCBEFE1F96BF730083AD46 /* NewSuite.swift */, + ); + path = Add; + sourceTree = ""; + }; + FE6989231FA3B130006CC783 /* Update */ = { + isa = PBXGroup; + children = ( + FE6989201FA39B99006CC783 /* UpdatePlanEntryRuns.swift */, + ); + path = Update; + sourceTree = ""; + }; + FE6989241FA3BB83006CC783 /* Models */ = { + isa = PBXGroup; + children = ( + FE3795E31FA7DB9E0030C395 /* Testing Protocols */, + FE6989251FA3BB90006CC783 /* Add */, + FE6989261FA3BB94006CC783 /* Update */, + ); + path = Models; + sourceTree = ""; + }; + FE6989251FA3BB90006CC783 /* Add */ = { + isa = PBXGroup; + children = ( + FE3795AF1FA799150030C395 /* NewCaseTests.swift */, + FE3795B31FA799330030C395 /* NewConfigurationGroupTests.swift */, + FE3795B11FA799270030C395 /* NewConfigurationTests.swift */, + FE3795B51FA799400030C395 /* NewMilestoneTests.swift */, + FE3795B71FA799520030C395 /* NewPlanTests.swift */, + FE3795B91FA7996F0030C395 /* NewPlan.EntryTests.swift */, + FE64D1571FBD0C3700ABA133 /* NewPlan.Entry.RunTests.swift */, + FE3795BB1FA7998F0030C395 /* NewProjectTests.swift */, + FE3795BD1FA7999C0030C395 /* NewResultTests.swift */, + FE208DF21FBB714B0065BE88 /* NewCaseResultsTests.swift */, + FE208DF41FBB716B0065BE88 /* NewCaseResults.ResultTests.swift */, + FE3795BF1FA799A90030C395 /* NewTestResultsTests.swift */, + FE3795C11FA799B70030C395 /* NewTestResults.ResultTests.swift */, + FE3795C31FA799D10030C395 /* NewRunTests.swift */, + FE3795C51FA799E30030C395 /* NewSectionTests.swift */, + FE3795C71FA799F50030C395 /* NewSuiteTests.swift */, + ); + path = Add; + sourceTree = ""; + }; + FE6989261FA3BB94006CC783 /* Update */ = { + isa = PBXGroup; + children = ( + FE3795C91FA79A070030C395 /* UpdatePlanEntryRunsTests.swift */, + ); + path = Update; + sourceTree = ""; + }; + FE6989271FA3BBC2006CC783 /* Containers */ = { + isa = PBXGroup; + children = ( + FE22458B1F75BECC009F2B2B /* CustomFieldsContainerTests.swift */, + FEC2140A1FD7533000036B17 /* ErrorContainerTests.swift */, + FE51EFA11F882454007012E0 /* JSONDictionaryContainerTests.swift */, + ); + path = Containers; + sourceTree = ""; + }; + FE6A6D241F75904B00DF1039 /* Models */ = { + isa = PBXGroup; + children = ( + FE3795E21FA7DB900030C395 /* Testing Protocols */, + FE52590E1F82A98200E0DDB7 /* Types */, + FE6A6D251F75905800DF1039 /* Custom Fields */, + FE6A6D261F75908100DF1039 /* CaseTests.swift */, + FE6A6D441F75913000DF1039 /* CaseFieldTests.swift */, + FE6A6D281F75909600DF1039 /* CaseTypeTests.swift */, + FE6A6D2A1F7590A100DF1039 /* ConfigurationTests.swift */, + FE6A6D2C1F7590B000DF1039 /* ConfigurationGroupTests.swift */, + FE6A6D2E1F7590BA00DF1039 /* MilestoneTests.swift */, + FE6A6D301F7590C700DF1039 /* PlanTests.swift */, + FE30A6521F7C261400CE7D6B /* Plan.EntryTests.swift */, + FE6A6D321F7590D200DF1039 /* PriorityTests.swift */, + FE6A6D341F7590DC00DF1039 /* ProjectTests.swift */, + FE6A6D361F7590E500DF1039 /* ResultTests.swift */, + FE6A6D4A1F75914E00DF1039 /* ResultFieldTests.swift */, + FE6A6D381F7590F000DF1039 /* RunTests.swift */, + FE6A6D3A1F7590F900DF1039 /* SectionTests.swift */, + FED295481FA1117F00746DAB /* StatusTests.swift */, + FE6A6D3C1F75910500DF1039 /* SuiteTests.swift */, + FE6A6D3E1F75910E00DF1039 /* TemplateTests.swift */, + FE6A6D401F75911A00DF1039 /* TestTests.swift */, + FE6A6D421F75912400DF1039 /* UserTests.swift */, + ); + path = Models; + sourceTree = ""; + }; + FE6A6D251F75905800DF1039 /* Custom Fields */ = { + isa = PBXGroup; + children = ( + FE6A6D461F75913900DF1039 /* ConfigTests.swift */, + FEAE7F951F7C5D0C00906FE1 /* Config.ContextTests.swift */, + ); + path = "Custom Fields"; + sourceTree = ""; + }; + FE6A6D4C1F75954D00DF1039 /* Network */ = { + isa = PBXGroup; + children = ( + FE2877F81FBBC469004503FB /* Filters */, + FE6989241FA3BB83006CC783 /* Models */, + FE6A6D4F1F75956C00DF1039 /* ObjectAPITests.swift */, + ); + path = Network; + sourceTree = ""; + }; + FE978CE71F95281E0005D181 /* Extensions */ = { + isa = PBXGroup; + children = ( + FE1474DF1FAA69D60049DA84 /* API */, + FE1474E01FAA69DD0049DA84 /* ObjectAPI */, + FE978CEC1F9532BA0005D181 /* URLRequestDebug.swift */, + ); + path = Extensions; + sourceTree = ""; + }; + FE978CF11F9552120005D181 /* Models */ = { + isa = PBXGroup; + children = ( + FE6989221FA3B12B006CC783 /* Add */, + FE6989231FA3B130006CC783 /* Update */, + ); + path = Models; + sourceTree = ""; + }; + FEA348591F5A13F700C1E37A /* Network */ = { + isa = PBXGroup; + children = ( + FE978CE71F95281E0005D181 /* Extensions */, + FEEC443C1FB4D22B0042BD5A /* Filters */, + FE978CF11F9552120005D181 /* Models */, + FE47DC771FCE27B50048BACE /* Operations */, + FEA348771F5A141300C1E37A /* API.swift */, + FE894C251F5A25CA0057E021 /* ObjectAPI.swift */, + ); + path = Network; + sourceTree = ""; + }; + FEB546811F91358800AA6DA5 /* Update */ = { + isa = PBXGroup; + children = ( + FE1FB1031F9131B200383724 /* UpdateRequestJSON.swift */, + FE6838E11F8FE10500431C1C /* UpdateRequestJSONKeys.swift */, + ); + path = Update; + sourceTree = ""; + }; + FEB546821F91359100AA6DA5 /* Add */ = { + isa = PBXGroup; + children = ( + FEB546831F9135CB00AA6DA5 /* AddRequestJSON.swift */, + FEB546851F9135D500AA6DA5 /* AddRequestJSONKeys.swift */, + ); + path = Add; + sourceTree = ""; + }; + FEBF50351FCF3E85005B86B7 /* Operations */ = { + isa = PBXGroup; + children = ( + FEBF50361FCF3E91005B86B7 /* AsyncOperation.swift */, + ); + path = Operations; + sourceTree = ""; + }; + FEC214051FD741ED00036B17 /* Debug */ = { + isa = PBXGroup; + children = ( + FEC214061FD741F700036B17 /* DebugDescription.swift */, + FEC214081FD7420300036B17 /* DebugDetails.swift */, + ); + path = Debug; + sourceTree = ""; + }; + FEC2140C1FD7536700036B17 /* Operations */ = { + isa = PBXGroup; + children = ( + FEC2140D1FD7537800036B17 /* AsyncOperationTests.swift */, + ); + path = Operations; + sourceTree = ""; + }; + FEDCBEED1F96BDC90083AD46 /* Containers */ = { + isa = PBXGroup; + children = ( + FE2245841F75AC82009F2B2B /* CustomFieldsContainer.swift */, + FEBDA3C01FD1C7F400124430 /* ErrorContainer.swift */, + FE5E1A2F1F8804E3001E479B /* JSONDictionaryContainer.swift */, + ); + path = Containers; + sourceTree = ""; + }; + FEDFF2731FE1B07700AEB3D6 /* Errors */ = { + isa = PBXGroup; + children = ( + FEDFF2741FE1B09000AEB3D6 /* SingleMatchError.swift */, + FEDFF2761FE1B0A000AEB3D6 /* MultipleMatchError.swift */, + ); + path = Errors; + sourceTree = ""; + }; + FEEC443C1FB4D22B0042BD5A /* Filters */ = { + isa = PBXGroup; + children = ( + FEEC443A1FB4D1BC0042BD5A /* Filter.swift */, + FEEC443D1FB4D23F0042BD5A /* Filter.Value.swift */, + ); + path = Filters; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + FE23A9191F59F39F007E946D /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + FE23A92D1F59F3A0007E946D /* QuizTrain.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FEAE994E1FEC38BA00B52CA9 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + FEAE99591FEC38CE00B52CA9 /* QuizTrain.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FEAE995C1FEC3BCD00B52CA9 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + FEAE9A991FEC4F2200B52CA9 /* QuizTrain.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FEAE99781FEC3BEB00B52CA9 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + FEAE9A9A1FEC4F2200B52CA9 /* QuizTrain.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + FE23A91B1F59F39F007E946D /* QuizTrain-iOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = FE23A9301F59F3A0007E946D /* Build configuration list for PBXNativeTarget "QuizTrain-iOS" */; + buildPhases = ( + FE23A9171F59F39F007E946D /* Sources */, + FE23A9181F59F39F007E946D /* Frameworks */, + FE23A9191F59F39F007E946D /* Headers */, + FE23A91A1F59F39F007E946D /* Resources */, + FE8464661FE31868006CB58F /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "QuizTrain-iOS"; + productName = QuizTrain; + productReference = FE23A91C1F59F3A0007E946D /* QuizTrain.framework */; + productType = "com.apple.product-type.framework"; + }; + FE23A9241F59F3A0007E946D /* QuizTrainTests-iOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = FE23A9331F59F3A0007E946D /* Build configuration list for PBXNativeTarget "QuizTrainTests-iOS" */; + buildPhases = ( + FE23A9211F59F3A0007E946D /* Sources */, + FE23A9221F59F3A0007E946D /* Frameworks */, + FE23A9231F59F3A0007E946D /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + FE23A9281F59F3A0007E946D /* PBXTargetDependency */, + ); + name = "QuizTrainTests-iOS"; + productName = QuizTrainTests; + productReference = FE23A9251F59F3A0007E946D /* QuizTrainTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + FEAE99501FEC38BA00B52CA9 /* QuizTrain-watchOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = FEAE99561FEC38BA00B52CA9 /* Build configuration list for PBXNativeTarget "QuizTrain-watchOS" */; + buildPhases = ( + FEAE994C1FEC38BA00B52CA9 /* Sources */, + FEAE994D1FEC38BA00B52CA9 /* Frameworks */, + FEAE994E1FEC38BA00B52CA9 /* Headers */, + FEAE994F1FEC38BA00B52CA9 /* Resources */, + FEDAE2CE1FED580100828842 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "QuizTrain-watchOS"; + productName = "QuizTrain-watchOS"; + productReference = FEAE99511FEC38BA00B52CA9 /* QuizTrain.framework */; + productType = "com.apple.product-type.framework"; + }; + FEAE995E1FEC3BCD00B52CA9 /* QuizTrain-tvOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = FEAE99701FEC3BCE00B52CA9 /* Build configuration list for PBXNativeTarget "QuizTrain-tvOS" */; + buildPhases = ( + FEAE995A1FEC3BCD00B52CA9 /* Sources */, + FEAE995B1FEC3BCD00B52CA9 /* Frameworks */, + FEAE995C1FEC3BCD00B52CA9 /* Headers */, + FEAE995D1FEC3BCD00B52CA9 /* Resources */, + FEDAE2CD1FED57FA00828842 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "QuizTrain-tvOS"; + productName = "QuizTrain-tvOS"; + productReference = FEAE995F1FEC3BCD00B52CA9 /* QuizTrain.framework */; + productType = "com.apple.product-type.framework"; + }; + FEAE99661FEC3BCE00B52CA9 /* QuizTrainTests-tvOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = FEAE99731FEC3BCE00B52CA9 /* Build configuration list for PBXNativeTarget "QuizTrainTests-tvOS" */; + buildPhases = ( + FEAE99631FEC3BCE00B52CA9 /* Sources */, + FEAE99641FEC3BCE00B52CA9 /* Frameworks */, + FEAE99651FEC3BCE00B52CA9 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + FEAE996A1FEC3BCE00B52CA9 /* PBXTargetDependency */, + ); + name = "QuizTrainTests-tvOS"; + productName = "QuizTrain-tvOSTests"; + productReference = FEAE99671FEC3BCE00B52CA9 /* QuizTrainTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + FEAE997A1FEC3BEB00B52CA9 /* QuizTrain-macOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = FEAE998C1FEC3BEB00B52CA9 /* Build configuration list for PBXNativeTarget "QuizTrain-macOS" */; + buildPhases = ( + FEAE99761FEC3BEB00B52CA9 /* Sources */, + FEAE99771FEC3BEB00B52CA9 /* Frameworks */, + FEAE99781FEC3BEB00B52CA9 /* Headers */, + FEAE99791FEC3BEB00B52CA9 /* Resources */, + FEDAE2CC1FED57F000828842 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "QuizTrain-macOS"; + productName = "QuizTrain-macOS"; + productReference = FEAE997B1FEC3BEB00B52CA9 /* QuizTrain.framework */; + productType = "com.apple.product-type.framework"; + }; + FEAE99821FEC3BEB00B52CA9 /* QuizTrainTests-macOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = FEAE998F1FEC3BEB00B52CA9 /* Build configuration list for PBXNativeTarget "QuizTrainTests-macOS" */; + buildPhases = ( + FEAE997F1FEC3BEB00B52CA9 /* Sources */, + FEAE99801FEC3BEB00B52CA9 /* Frameworks */, + FEAE99811FEC3BEB00B52CA9 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + FEAE99861FEC3BEB00B52CA9 /* PBXTargetDependency */, + ); + name = "QuizTrainTests-macOS"; + productName = "QuizTrain-macOSTests"; + productReference = FEAE99831FEC3BEB00B52CA9 /* QuizTrainTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + FE23A9131F59F39F007E946D /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 0900; + ORGANIZATIONNAME = "Gallagher, David"; + TargetAttributes = { + FE23A91B1F59F39F007E946D = { + CreatedOnToolsVersion = 9.0; + LastSwiftMigration = 0900; + ProvisioningStyle = Automatic; + }; + FE23A9241F59F3A0007E946D = { + CreatedOnToolsVersion = 9.0; + ProvisioningStyle = Automatic; + }; + FEAE99501FEC38BA00B52CA9 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Automatic; + }; + FEAE995E1FEC3BCD00B52CA9 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Automatic; + }; + FEAE99661FEC3BCE00B52CA9 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Automatic; + }; + FEAE997A1FEC3BEB00B52CA9 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Automatic; + }; + FEAE99821FEC3BEB00B52CA9 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = FE23A9161F59F39F007E946D /* Build configuration list for PBXProject "QuizTrain" */; + compatibilityVersion = "Xcode 8.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = FE23A9121F59F39F007E946D; + productRefGroup = FE23A91D1F59F3A0007E946D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + FE23A91B1F59F39F007E946D /* QuizTrain-iOS */, + FEAE997A1FEC3BEB00B52CA9 /* QuizTrain-macOS */, + FEAE995E1FEC3BCD00B52CA9 /* QuizTrain-tvOS */, + FEAE99501FEC38BA00B52CA9 /* QuizTrain-watchOS */, + FE23A9241F59F3A0007E946D /* QuizTrainTests-iOS */, + FEAE99821FEC3BEB00B52CA9 /* QuizTrainTests-macOS */, + FEAE99661FEC3BCE00B52CA9 /* QuizTrainTests-tvOS */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + FE23A91A1F59F39F007E946D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FE23A9231F59F3A0007E946D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FEBFEEBB1FE2EAB900E7FE1B /* TestCredentials.json in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FEAE994F1FEC38BA00B52CA9 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FEAE995D1FEC3BCD00B52CA9 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FEAE99651FEC3BCE00B52CA9 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FEAE9AA01FEC4FF200B52CA9 /* TestCredentials.json in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FEAE99791FEC3BEB00B52CA9 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FEAE99811FEC3BEB00B52CA9 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FEAE9A9D1FEC4FF100B52CA9 /* TestCredentials.json in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + FE8464661FE31868006CB58F /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi"; + }; + FEDAE2CC1FED57F000828842 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi"; + }; + FEDAE2CD1FED57FA00828842 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi"; + }; + FEDAE2CE1FED580100828842 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + FE23A9171F59F39F007E946D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FE208DED1FBB69C60065BE88 /* NewCaseResults.Result.swift in Sources */, + FEEC44401FB4D3270042BD5A /* QueryItemProvider.swift in Sources */, + FEEC443E1FB4D23F0042BD5A /* Filter.Value.swift in Sources */, + FE2F1AD71F844FCD00FF9E0C /* JSONSerializable.swift in Sources */, + FEDCBEFB1F96BF540083AD46 /* NewRun.swift in Sources */, + FE813A181F73126F00265569 /* JSONDictionary.swift in Sources */, + FE9F38A61F69E5AD003BBA36 /* Plan.swift in Sources */, + FEBDA3C21FD1C88300124430 /* ObjectAPI.swift in Sources */, + FEC214001FD7302900036B17 /* ObjectAPI.MatchErrorDebug.swift in Sources */, + FE23A9671F59F4B3007E946D /* ResultField.swift in Sources */, + FE23A9711F59F4E2007E946D /* Test.swift in Sources */, + FEDFF2751FE1B09000AEB3D6 /* SingleMatchError.swift in Sources */, + FEDCBEF31F96BF020083AD46 /* NewMilestone.swift in Sources */, + FE30F11F1F6AEDF600AA7761 /* Configuration.swift in Sources */, + FE1474E51FAA6A0D0049DA84 /* ObjectAPI.ServerErrorDebug.swift in Sources */, + FE2245851F75AC82009F2B2B /* CustomFieldsContainer.swift in Sources */, + FE208DF11FBB6BA10065BE88 /* NewCaseResults.swift in Sources */, + FEC214071FD741F700036B17 /* DebugDescription.swift in Sources */, + FE63715F1FD61CA500192CED /* GetConfigurationGroupsOperation.swift in Sources */, + FE5869D71F7478D600BE5C5C /* CustomFields.swift in Sources */, + FE23A9731F59F4EB007E946D /* User.swift in Sources */, + FEDCBEF51F96BF130083AD46 /* NewPlan.swift in Sources */, + FEDCBEEF1F96BEE70083AD46 /* NewConfiguration.swift in Sources */, + FE4F8F3B1F8437DE00447F9E /* JSONDeserializable.swift in Sources */, + FEC214091FD7420300036B17 /* DebugDetails.swift in Sources */, + FEBF50371FCF3E91005B86B7 /* AsyncOperation.swift in Sources */, + FEB546841F9135CB00AA6DA5 /* AddRequestJSON.swift in Sources */, + FE1474EC1FAA6A5B0049DA84 /* ObjectAPI.ObjectConversionErrorDebug.swift in Sources */, + FEDCBEFD1F96BF610083AD46 /* NewSection.swift in Sources */, + FE1474DA1FAA698F0049DA84 /* ObjectAPI.RequestErrorDebug.swift in Sources */, + FE5E1A301F8804E3001E479B /* JSONDictionaryContainer.swift in Sources */, + FE30F11E1F6AED8100AA7761 /* ConfigurationGroup.swift in Sources */, + FE1FB1041F9131B200383724 /* UpdateRequestJSON.swift in Sources */, + FE23A96F1F59F4D9007E946D /* Template.swift in Sources */, + FE5A24F21FE092D300198848 /* UniqueSelection.swift in Sources */, + FE208DEF1FBB6B920065BE88 /* NewTestResults.swift in Sources */, + FE1474DE1FAA69B70049DA84 /* ObjectAPI.UpdateRequestErrorDebug.swift in Sources */, + FEDCBEF91F96BF3A0083AD46 /* NewResult.swift in Sources */, + FE20D03F1FD876820057A45C /* Identifiable.swift in Sources */, + FECF66701FDF05DB00015CC4 /* GetProjectOperation.swift in Sources */, + FEDCBEFF1F96BF730083AD46 /* NewSuite.swift in Sources */, + FE7B53931FBF5BB1003C26BD /* Array+ContentComparison.swift in Sources */, + FEAE7F921F7C5ABE00906FE1 /* Config.Context.swift in Sources */, + FED295471FA10E9300746DAB /* Status.swift in Sources */, + FE3899171FCF2BDE0032E265 /* GetTemplatesOperation.swift in Sources */, + FE4E11D71F7C4112004A315E /* Plan.Entry.swift in Sources */, + FEDD80781F69CA3C00D56EF9 /* Result.swift in Sources */, + FE6C8AE21F8BEA2E00F45642 /* MutableCustomFields.swift in Sources */, + FEAF0BFE1F7D9F15001D4F10 /* Project.SuiteMode.swift in Sources */, + FE30F11D1F6AE4A300AA7761 /* Milestone.swift in Sources */, + FEEC443B1FB4D1BC0042BD5A /* Filter.swift in Sources */, + FE5259121F82D1AD00E0DDB7 /* JSONKey.swift in Sources */, + FE978CEB1F9528400005D181 /* API.RequestErrorDebug.swift in Sources */, + FEA348781F5A141300C1E37A /* API.swift in Sources */, + FEB546861F9135D500AA6DA5 /* AddRequestJSONKeys.swift in Sources */, + FE331BDE1F6AF98F00F9A653 /* CaseType.swift in Sources */, + FEDCBEF71F96BF290083AD46 /* NewProject.swift in Sources */, + FE1474EA1FAA6A340049DA84 /* ObjectAPI.DataProcessingErrorDebug.swift in Sources */, + FE208DFA1FBB95150065BE88 /* Validatable.swift in Sources */, + FE7CD3E41F9ABDC300C6108E /* NewPlan.Entry.swift in Sources */, + FE2D018B1FBCE70B00473B84 /* NewPlan.Entry.Run.swift in Sources */, + FEDD80791F69D7A700D56EF9 /* Project.swift in Sources */, + FEBDA3C11FD1C7F400124430 /* ErrorContainer.swift in Sources */, + FE1474E21FAA69F70049DA84 /* ObjectAPI.ClientErrorDebug.swift in Sources */, + FEDD807A1F69DE1A00D56EF9 /* Priority.swift in Sources */, + FE1474DC1FAA69AA0049DA84 /* ObjectAPI.DataRequestErrorDebug.swift in Sources */, + FE331BDF1F6AFE5D00F9A653 /* CaseField.swift in Sources */, + FE11181C1F6C3A4B00D24A5F /* Config.swift in Sources */, + FEAE99D91FEC431A00B52CA9 /* UpdateRequestJSONKeys.swift in Sources */, + FE331C1C1F6B406300F9A653 /* CustomFieldType.swift in Sources */, + FE8C2D5A1FBA391F005A4150 /* Date+Seconds.swift in Sources */, + FE1474E81FAA6A270049DA84 /* ObjectAPI.StatusCodeErrorDebug.swift in Sources */, + FE6989211FA39B99006CC783 /* UpdatePlanEntryRuns.swift in Sources */, + FE23A96B1F59F4C3007E946D /* Section.swift in Sources */, + FE978CED1F9532BA0005D181 /* URLRequestDebug.swift in Sources */, + FEAE99C01FEC42FB00B52CA9 /* Equatable+OptionalArray.swift in Sources */, + FE331C1A1F6B3C0200F9A653 /* Case.swift in Sources */, + FEB546881F9170A900AA6DA5 /* Outcome.swift in Sources */, + FE208DEB1FBB69B80065BE88 /* NewTestResults.Result.swift in Sources */, + FE23A96D1F59F4CE007E946D /* Suite.swift in Sources */, + FEDFF2771FE1B0A000AEB3D6 /* MultipleMatchError.swift in Sources */, + FE978CE91F9528320005D181 /* API.RequestResultDebug.swift in Sources */, + FE23A9691F59F4BA007E946D /* Run.swift in Sources */, + FEDCBEF11F96BEF10083AD46 /* NewConfigurationGroup.swift in Sources */, + FE4F8F391F84361F00447F9E /* NewCase.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FE23A9211F59F3A0007E946D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FE6A6D311F7590C700DF1039 /* PlanTests.swift in Sources */, + FE58F5661F7EEA29009A1B4E /* CustomFieldsDataProvider.swift in Sources */, + FE3795B41FA799330030C395 /* NewConfigurationGroupTests.swift in Sources */, + FE58F5611F7EE91D009A1B4E /* JSONDataProvider.swift in Sources */, + FE6A6D2F1F7590BA00DF1039 /* MilestoneTests.swift in Sources */, + FE3795D41FA7C7A50030C395 /* JSONDeserializingTests.swift in Sources */, + FE3795CA1FA79A070030C395 /* UpdatePlanEntryRunsTests.swift in Sources */, + FE3795D21FA7C7960030C395 /* InitTests.swift in Sources */, + FE3795CC1FA7A3AD0030C395 /* ObjectProvider.swift in Sources */, + FE6A6D2D1F7590B000DF1039 /* ConfigurationGroupTests.swift in Sources */, + FE3795CE1FA7A4540030C395 /* AddModelTests.swift in Sources */, + FE6A6D331F7590D200DF1039 /* PriorityTests.swift in Sources */, + FE6A6D3B1F7590F900DF1039 /* SectionTests.swift in Sources */, + FE6A6D451F75913000DF1039 /* CaseFieldTests.swift in Sources */, + FE6A6D291F75909600DF1039 /* CaseTypeTests.swift in Sources */, + FE6A6D3D1F75910500DF1039 /* SuiteTests.swift in Sources */, + FE51EFA21F882454007012E0 /* JSONDictionaryContainerTests.swift in Sources */, + FECFE7FF1FD9E1AE00968EA3 /* Array+RandomTests.swift in Sources */, + FE3795B21FA799270030C395 /* NewConfigurationTests.swift in Sources */, + FE3795AA1FA7915C0030C395 /* AssertUpdateRequestJSON.swift in Sources */, + FE1474D61FAA475A0049DA84 /* Equatable+OptionalArrayTests.swift in Sources */, + FE3795C21FA799B70030C395 /* NewTestResults.ResultTests.swift in Sources */, + FE3795E41FA7E1770030C395 /* NewCaseTests.swift in Sources */, + FEF172631F857F53004FFFFF /* AssertJSONSerializing.swift in Sources */, + FE6A6D2B1F7590A100DF1039 /* ConfigurationTests.swift in Sources */, + FE6A6D491F75914300DF1039 /* CustomFieldTypeTests.swift in Sources */, + FE018DC71F85708C001A2FEF /* ModelTests.swift in Sources */, + FE6A6D391F7590F000DF1039 /* RunTests.swift in Sources */, + FE6A6D411F75911A00DF1039 /* TestTests.swift in Sources */, + FE3795B61FA799400030C395 /* NewMilestoneTests.swift in Sources */, + FEF9CE7E1F7C538F0078CD4E /* Plan.EntryTests.swift in Sources */, + FE3795DA1FA7C7E30030C395 /* VariablePropertyTests.swift in Sources */, + FE549C431FBE514A008CDFCE /* NewPlan.EntryTests.swift in Sources */, + FE3795D61FA7C7B60030C395 /* JSONSerializingTests.swift in Sources */, + FE6A6D271F75908100DF1039 /* CaseTests.swift in Sources */, + FE5A24F41FE099BD00198848 /* Array+Random.swift in Sources */, + FEC2140E1FD7537800036B17 /* AsyncOperationTests.swift in Sources */, + FE2877FA1FBBC47B004503FB /* FilterTests.swift in Sources */, + FE2F1AD51F843AEE00FF9E0C /* AssertJSONDeserializing.swift in Sources */, + FEC2140B1FD7533000036B17 /* ErrorContainerTests.swift in Sources */, + FE2877F01FBB9A80004503FB /* ValidatableObjectProvider.swift in Sources */, + FE58F54A1F7EBF12009A1B4E /* AssertCustomFields.swift in Sources */, + FE3795DC1FA7C7FA0030C395 /* UpdateRequestJSONTests.swift in Sources */, + FE2877EC1FBB979C004503FB /* ValidatableTests.swift in Sources */, + FE3795E81FA7EB380030C395 /* AddRequestJSONTests.swift in Sources */, + FEBFEEB91FE2EAAB00E7FE1B /* TestCredentials.swift in Sources */, + FE3795E11FA7D8360030C395 /* UpdateModelTests.swift in Sources */, + FEE774371FA8EA980016AACE /* AssertEquatable.swift in Sources */, + FE3795E61FA7EA6C0030C395 /* AssertAddRequestJSON.swift in Sources */, + FE6A6D471F75913900DF1039 /* ConfigTests.swift in Sources */, + FE3795C61FA799E30030C395 /* NewSectionTests.swift in Sources */, + FEE774351FA8E2480016AACE /* EquatableTests.swift in Sources */, + FE75B2E01F83078A00DF367A /* CustomFieldsContainerTests.swift in Sources */, + FE549C441FBE514C008CDFCE /* NewPlan.Entry.RunTests.swift in Sources */, + FEAE7F961F7C5D0C00906FE1 /* Config.ContextTests.swift in Sources */, + FE6A6D431F75912400DF1039 /* UserTests.swift in Sources */, + FE2877EE1FBB9803004503FB /* AssertValidatable.swift in Sources */, + FED295491FA1117F00746DAB /* StatusTests.swift in Sources */, + FE3795BE1FA7999C0030C395 /* NewResultTests.swift in Sources */, + FE549C421FBE5147008CDFCE /* NewPlanTests.swift in Sources */, + FE6A6D371F7590E500DF1039 /* ResultTests.swift in Sources */, + FE549C461FBE60F3008CDFCE /* Array+ContentComparisonTests.swift in Sources */, + FE77BB761FA9132800E23865 /* NewRunTests.swift in Sources */, + FE58F5481F7EBF04009A1B4E /* AssertProperties.swift in Sources */, + FE3795D81FA7C7D00030C395 /* JSONTwoWaySerializationTests.swift in Sources */, + FE6A6D351F7590DC00DF1039 /* ProjectTests.swift in Sources */, + FE2877F61FBBBD37004503FB /* NewTestResultsTests.swift in Sources */, + FE208DF51FBB716B0065BE88 /* NewCaseResults.ResultTests.swift in Sources */, + FE6A6D3F1F75910E00DF1039 /* TemplateTests.swift in Sources */, + FEF172661F858880004FFFFF /* AssertJSONTwoWaySerialization.swift in Sources */, + FE496B5F1F7D9BCF00AE9454 /* ResultFieldTests.swift in Sources */, + FE0B15471FED689F0009B570 /* ObjectAPITests.swift in Sources */, + FE3795C81FA799F50030C395 /* NewSuiteTests.swift in Sources */, + FE2877F41FBBBC50004503FB /* NewCaseResultsTests.swift in Sources */, + FECF66761FDF593600015CC4 /* UniqueSelectionTests.swift in Sources */, + FE3795BC1FA7998F0030C395 /* NewProjectTests.swift in Sources */, + FE791A251F7D9FFA00D7E870 /* Project.SuiteModeTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FEAE994C1FEC38BA00B52CA9 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FEAE99A41FEC42D900B52CA9 /* ErrorContainer.swift in Sources */, + FEAE9A551FEC4F1000B52CA9 /* Filter.swift in Sources */, + FEAE9A661FEC4F1400B52CA9 /* NewSuite.swift in Sources */, + FEAE99A11FEC42D500B52CA9 /* CustomFieldsContainer.swift in Sources */, + FEAE999E1FEC42D200B52CA9 /* MutableCustomFields.swift in Sources */, + FEAE99D41FEC431500B52CA9 /* UpdateRequestJSON.swift in Sources */, + FEAE999B1FEC42CE00B52CA9 /* CustomFields.swift in Sources */, + FEAE9A8E1FEC4F1D00B52CA9 /* ObjectAPI.swift in Sources */, + FEAE9A341FEC4F0A00B52CA9 /* ObjectAPI.DataProcessingErrorDebug.swift in Sources */, + FEAE9A1D1FEC4EFF00B52CA9 /* Test.swift in Sources */, + FEAE9A651FEC4F1400B52CA9 /* NewSection.swift in Sources */, + FEAE9A5A1FEC4F1400B52CA9 /* NewMilestone.swift in Sources */, + FEAE9A151FEC4EFA00B52CA9 /* ResultField.swift in Sources */, + FEAE9A871FEC4F1800B52CA9 /* UpdatePlanEntryRuns.swift in Sources */, + FEAE99BA1FEC42F300B52CA9 /* Array+ContentComparison.swift in Sources */, + FEAE99D71FEC431900B52CA9 /* UpdateRequestJSONKeys.swift in Sources */, + FEAE9A3B1FEC4F0A00B52CA9 /* ObjectAPI.UpdateRequestErrorDebug.swift in Sources */, + FEAE9A8B1FEC4F1D00B52CA9 /* GetProjectOperation.swift in Sources */, + FEAE99E71FEC432E00B52CA9 /* CustomFieldType.swift in Sources */, + FEAE99DB1FEC431E00B52CA9 /* Validatable.swift in Sources */, + FEAE9A571FEC4F1400B52CA9 /* NewCase.swift in Sources */, + FEAE99CB1FEC430A00B52CA9 /* JSONDeserializable.swift in Sources */, + FEAE9A581FEC4F1400B52CA9 /* NewConfiguration.swift in Sources */, + FEAE99FF1FEC4ECD00B52CA9 /* Configuration.swift in Sources */, + FEAE9A5F1FEC4F1400B52CA9 /* NewResult.swift in Sources */, + FEAE99F91FEC4EC600B52CA9 /* CaseField.swift in Sources */, + FEAE9A361FEC4F0A00B52CA9 /* ObjectAPI.MatchErrorDebug.swift in Sources */, + FEAE9A351FEC4F0A00B52CA9 /* ObjectAPI.DataRequestErrorDebug.swift in Sources */, + FEAE99AB1FEC42E000B52CA9 /* DebugDescription.swift in Sources */, + FEAE9A371FEC4F0A00B52CA9 /* ObjectAPI.ObjectConversionErrorDebug.swift in Sources */, + FEAE99A71FEC42DC00B52CA9 /* JSONDictionaryContainer.swift in Sources */, + FEAE99FC1FEC4EC900B52CA9 /* CaseType.swift in Sources */, + FEAE99F61FEC4EC200B52CA9 /* Case.swift in Sources */, + FEAE9A121FEC4EF600B52CA9 /* Result.swift in Sources */, + FEAE99F01FEC433800B52CA9 /* Config.Context.swift in Sources */, + FEAE9A8D1FEC4F1D00B52CA9 /* API.swift in Sources */, + FEAE9A5B1FEC4F1400B52CA9 /* NewPlan.swift in Sources */, + FEAE99B21FEC42E900B52CA9 /* SingleMatchError.swift in Sources */, + FEAE9A5C1FEC4F1400B52CA9 /* NewPlan.Entry.swift in Sources */, + FEAE9A3A1FEC4F0A00B52CA9 /* ObjectAPI.StatusCodeErrorDebug.swift in Sources */, + FEAE9A1C1FEC4EFF00B52CA9 /* Template.swift in Sources */, + FEAE9A051FEC4ED500B52CA9 /* Milestone.swift in Sources */, + FEAE9A3C1FEC4F0A00B52CA9 /* URLRequestDebug.swift in Sources */, + FEAE99AE1FEC42E500B52CA9 /* DebugDetails.swift in Sources */, + FEAE9A0B1FEC4EDD00B52CA9 /* Plan.Entry.swift in Sources */, + FEAE9A611FEC4F1400B52CA9 /* NewCaseResults.Result.swift in Sources */, + FEAE99C81FEC430600B52CA9 /* JSONDictionary.swift in Sources */, + FEAE99B61FEC42EE00B52CA9 /* MultipleMatchError.swift in Sources */, + FEAE9A0F1FEC4EF100B52CA9 /* Project.swift in Sources */, + FEAE9A381FEC4F0A00B52CA9 /* ObjectAPI.RequestErrorDebug.swift in Sources */, + FEAE9A8C1FEC4F1D00B52CA9 /* GetTemplatesOperation.swift in Sources */, + FEAE9A331FEC4F0A00B52CA9 /* ObjectAPI.ClientErrorDebug.swift in Sources */, + FEAE9A391FEC4F0A00B52CA9 /* ObjectAPI.ServerErrorDebug.swift in Sources */, + FEAE9A0C1FEC4EED00B52CA9 /* Priority.swift in Sources */, + FEAE9A5E1FEC4F1400B52CA9 /* NewProject.swift in Sources */, + FEAE9A601FEC4F1400B52CA9 /* NewCaseResults.swift in Sources */, + FEAE9A181FEC4EFF00B52CA9 /* Run.swift in Sources */, + FEAE99BE1FEC42FA00B52CA9 /* Equatable+OptionalArray.swift in Sources */, + FEAE99CE1FEC430E00B52CA9 /* JSONSerializable.swift in Sources */, + FEAE99EA1FEC433100B52CA9 /* Project.SuiteMode.swift in Sources */, + FEAE99C51FEC430200B52CA9 /* JSONKey.swift in Sources */, + FEAE99941FEC42C600B52CA9 /* AddRequestJSON.swift in Sources */, + FEAE9A2E1FEC4F0600B52CA9 /* API.RequestResultDebug.swift in Sources */, + FEAE9A8A1FEC4F1D00B52CA9 /* GetConfigurationGroupsOperation.swift in Sources */, + FEAE9A5D1FEC4F1400B52CA9 /* NewPlan.Entry.Run.swift in Sources */, + FEAE99971FEC42CA00B52CA9 /* AddRequestJSONKeys.swift in Sources */, + FEAE9A641FEC4F1400B52CA9 /* NewRun.swift in Sources */, + FEAE99ED1FEC433500B52CA9 /* Config.swift in Sources */, + FEAE99E11FEC432700B52CA9 /* QueryItemProvider.swift in Sources */, + FEAE99DE1FEC432200B52CA9 /* Outcome.swift in Sources */, + FEAE9A191FEC4EFF00B52CA9 /* Section.swift in Sources */, + FEAE99C21FEC42FE00B52CA9 /* Identifiable.swift in Sources */, + FEAE9A631FEC4F1400B52CA9 /* NewTestResults.Result.swift in Sources */, + FEAE9A1E1FEC4EFF00B52CA9 /* User.swift in Sources */, + FEAE9A2D1FEC4F0600B52CA9 /* API.RequestErrorDebug.swift in Sources */, + FEAE9A021FEC4ED100B52CA9 /* ConfigurationGroup.swift in Sources */, + FEAE9A1B1FEC4EFF00B52CA9 /* Suite.swift in Sources */, + FEAE99D11FEC431200B52CA9 /* AsyncOperation.swift in Sources */, + FEAE9A1A1FEC4EFF00B52CA9 /* Status.swift in Sources */, + FEAE9A081FEC4ED900B52CA9 /* Plan.swift in Sources */, + FEAE9A621FEC4F1400B52CA9 /* NewTestResults.swift in Sources */, + FEAE9A561FEC4F1000B52CA9 /* Filter.Value.swift in Sources */, + FEAE99BB1FEC42F500B52CA9 /* Date+Seconds.swift in Sources */, + FEAE99E41FEC432A00B52CA9 /* UniqueSelection.swift in Sources */, + FEAE9A591FEC4F1400B52CA9 /* NewConfigurationGroup.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FEAE995A1FEC3BCD00B52CA9 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FEAE99A31FEC42D800B52CA9 /* ErrorContainer.swift in Sources */, + FEAE9A531FEC4F1000B52CA9 /* Filter.swift in Sources */, + FEAE9A761FEC4F1500B52CA9 /* NewSuite.swift in Sources */, + FEAE99A01FEC42D500B52CA9 /* CustomFieldsContainer.swift in Sources */, + FEAE999D1FEC42D100B52CA9 /* MutableCustomFields.swift in Sources */, + FEAE99D51FEC431600B52CA9 /* UpdateRequestJSON.swift in Sources */, + FEAE99991FEC42CD00B52CA9 /* CustomFields.swift in Sources */, + FEAE9A931FEC4F1E00B52CA9 /* ObjectAPI.swift in Sources */, + FEAE9A3E1FEC4F0B00B52CA9 /* ObjectAPI.DataProcessingErrorDebug.swift in Sources */, + FEAE9A241FEC4F0000B52CA9 /* Test.swift in Sources */, + FEAE9A751FEC4F1500B52CA9 /* NewSection.swift in Sources */, + FEAE9A6A1FEC4F1500B52CA9 /* NewMilestone.swift in Sources */, + FEAE9A161FEC4EFB00B52CA9 /* ResultField.swift in Sources */, + FEAE9A881FEC4F1900B52CA9 /* UpdatePlanEntryRuns.swift in Sources */, + FEAE99B81FEC42F200B52CA9 /* Array+ContentComparison.swift in Sources */, + FEAE99D81FEC431900B52CA9 /* UpdateRequestJSONKeys.swift in Sources */, + FEAE9A451FEC4F0B00B52CA9 /* ObjectAPI.UpdateRequestErrorDebug.swift in Sources */, + FEAE9A901FEC4F1E00B52CA9 /* GetProjectOperation.swift in Sources */, + FEAE99E81FEC432E00B52CA9 /* CustomFieldType.swift in Sources */, + FEAE99DC1FEC431F00B52CA9 /* Validatable.swift in Sources */, + FEAE9A671FEC4F1500B52CA9 /* NewCase.swift in Sources */, + FEAE99CC1FEC430B00B52CA9 /* JSONDeserializable.swift in Sources */, + FEAE9A681FEC4F1500B52CA9 /* NewConfiguration.swift in Sources */, + FEAE99FE1FEC4ECB00B52CA9 /* Configuration.swift in Sources */, + FEAE9A6F1FEC4F1500B52CA9 /* NewResult.swift in Sources */, + FEAE99F81FEC4EC500B52CA9 /* CaseField.swift in Sources */, + FEAE9A401FEC4F0B00B52CA9 /* ObjectAPI.MatchErrorDebug.swift in Sources */, + FEAE9A3F1FEC4F0B00B52CA9 /* ObjectAPI.DataRequestErrorDebug.swift in Sources */, + FEAE99A91FEC42DF00B52CA9 /* DebugDescription.swift in Sources */, + FEAE9A411FEC4F0B00B52CA9 /* ObjectAPI.ObjectConversionErrorDebug.swift in Sources */, + FEAE99A61FEC42DB00B52CA9 /* JSONDictionaryContainer.swift in Sources */, + FEAE99FB1FEC4EC800B52CA9 /* CaseType.swift in Sources */, + FEAE99F51FEC4EC200B52CA9 /* Case.swift in Sources */, + FEAE9A131FEC4EF700B52CA9 /* Result.swift in Sources */, + FEAE99F31FEC433B00B52CA9 /* Config.Context.swift in Sources */, + FEAE9A921FEC4F1E00B52CA9 /* API.swift in Sources */, + FEAE9A6B1FEC4F1500B52CA9 /* NewPlan.swift in Sources */, + FEAE99B01FEC42E800B52CA9 /* SingleMatchError.swift in Sources */, + FEAE9A6C1FEC4F1500B52CA9 /* NewPlan.Entry.swift in Sources */, + FEAE9A441FEC4F0B00B52CA9 /* ObjectAPI.StatusCodeErrorDebug.swift in Sources */, + FEAE9A231FEC4F0000B52CA9 /* Template.swift in Sources */, + FEAE9A041FEC4ED400B52CA9 /* Milestone.swift in Sources */, + FEAE9A461FEC4F0B00B52CA9 /* URLRequestDebug.swift in Sources */, + FEAE99AD1FEC42E500B52CA9 /* DebugDetails.swift in Sources */, + FEAE9A0A1FEC4EDD00B52CA9 /* Plan.Entry.swift in Sources */, + FEAE9A711FEC4F1500B52CA9 /* NewCaseResults.Result.swift in Sources */, + FEAE99C91FEC430600B52CA9 /* JSONDictionary.swift in Sources */, + FEAE99B41FEC42ED00B52CA9 /* MultipleMatchError.swift in Sources */, + FEAE9A101FEC4EF200B52CA9 /* Project.swift in Sources */, + FEAE9A421FEC4F0B00B52CA9 /* ObjectAPI.RequestErrorDebug.swift in Sources */, + FEAE9A911FEC4F1E00B52CA9 /* GetTemplatesOperation.swift in Sources */, + FEAE9A3D1FEC4F0B00B52CA9 /* ObjectAPI.ClientErrorDebug.swift in Sources */, + FEAE9A431FEC4F0B00B52CA9 /* ObjectAPI.ServerErrorDebug.swift in Sources */, + FEAE9A0D1FEC4EED00B52CA9 /* Priority.swift in Sources */, + FEAE9A6E1FEC4F1500B52CA9 /* NewProject.swift in Sources */, + FEAE9A701FEC4F1500B52CA9 /* NewCaseResults.swift in Sources */, + FEAE9A1F1FEC4F0000B52CA9 /* Run.swift in Sources */, + FEAE99BF1FEC42FA00B52CA9 /* Equatable+OptionalArray.swift in Sources */, + FEAE99CF1FEC430F00B52CA9 /* JSONSerializable.swift in Sources */, + FEAE99EB1FEC433200B52CA9 /* Project.SuiteMode.swift in Sources */, + FEAE99C61FEC430300B52CA9 /* JSONKey.swift in Sources */, + FEAE99931FEC42C600B52CA9 /* AddRequestJSON.swift in Sources */, + FEAE9A301FEC4F0600B52CA9 /* API.RequestResultDebug.swift in Sources */, + FEAE9A8F1FEC4F1E00B52CA9 /* GetConfigurationGroupsOperation.swift in Sources */, + FEAE9A6D1FEC4F1500B52CA9 /* NewPlan.Entry.Run.swift in Sources */, + FEAE99961FEC42CA00B52CA9 /* AddRequestJSONKeys.swift in Sources */, + FEAE9A741FEC4F1500B52CA9 /* NewRun.swift in Sources */, + FEAE99EE1FEC433600B52CA9 /* Config.swift in Sources */, + FEAE99E21FEC432700B52CA9 /* QueryItemProvider.swift in Sources */, + FEAE99DF1FEC432300B52CA9 /* Outcome.swift in Sources */, + FEAE9A201FEC4F0000B52CA9 /* Section.swift in Sources */, + FEAE99C31FEC42FF00B52CA9 /* Identifiable.swift in Sources */, + FEAE9A731FEC4F1500B52CA9 /* NewTestResults.Result.swift in Sources */, + FEAE9A251FEC4F0000B52CA9 /* User.swift in Sources */, + FEAE9A2F1FEC4F0600B52CA9 /* API.RequestErrorDebug.swift in Sources */, + FEAE9A011FEC4ED100B52CA9 /* ConfigurationGroup.swift in Sources */, + FEAE9A221FEC4F0000B52CA9 /* Suite.swift in Sources */, + FEAE99D21FEC431300B52CA9 /* AsyncOperation.swift in Sources */, + FEAE9A211FEC4F0000B52CA9 /* Status.swift in Sources */, + FEAE9A071FEC4ED900B52CA9 /* Plan.swift in Sources */, + FEAE9A721FEC4F1500B52CA9 /* NewTestResults.swift in Sources */, + FEAE9A541FEC4F1000B52CA9 /* Filter.Value.swift in Sources */, + FEAE99BC1FEC42F600B52CA9 /* Date+Seconds.swift in Sources */, + FEAE99E51FEC432A00B52CA9 /* UniqueSelection.swift in Sources */, + FEAE9A691FEC4F1500B52CA9 /* NewConfigurationGroup.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FEAE99631FEC3BCE00B52CA9 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FEAE9B1A1FEC501F00B52CA9 /* NewProjectTests.swift in Sources */, + FEAE9ADD1FEC501000B52CA9 /* CustomFieldTypeTests.swift in Sources */, + FEAE9B0D1FEC501700B52CA9 /* FilterTests.swift in Sources */, + FEAE9B161FEC501F00B52CA9 /* NewMilestoneTests.swift in Sources */, + FEAE9ADA1FEC500A00B52CA9 /* AsyncOperationTests.swift in Sources */, + FEAE9B151FEC501F00B52CA9 /* NewConfigurationTests.swift in Sources */, + FEAE9AD31FEC500600B52CA9 /* Array+ContentComparisonTests.swift in Sources */, + FEAE9AB81FEC4FF900B52CA9 /* JSONDataProvider.swift in Sources */, + FEAE9B1D1FEC501F00B52CA9 /* NewCaseResults.ResultTests.swift in Sources */, + FEAE9AEB1FEC501400B52CA9 /* PlanTests.swift in Sources */, + FEAE9AF21FEC501400B52CA9 /* SectionTests.swift in Sources */, + FEAE9AEE1FEC501400B52CA9 /* ProjectTests.swift in Sources */, + FEAE9ADE1FEC501000B52CA9 /* Project.SuiteModeTests.swift in Sources */, + FEAE9B1E1FEC501F00B52CA9 /* NewTestResultsTests.swift in Sources */, + FEAE9B1F1FEC501F00B52CA9 /* NewTestResults.ResultTests.swift in Sources */, + FEAE9B0F1FEC501B00B52CA9 /* AddModelTests.swift in Sources */, + FEAE9AAD1FEC4FF500B52CA9 /* AssertJSONDeserializing.swift in Sources */, + FEAE9AAA1FEC4FF500B52CA9 /* AssertAddRequestJSON.swift in Sources */, + FEAE9B1B1FEC501F00B52CA9 /* NewResultTests.swift in Sources */, + FEAE9ACF1FEC500200B52CA9 /* JSONDictionaryContainerTests.swift in Sources */, + FEAE9ABB1FEC4FFE00B52CA9 /* AddRequestJSONTests.swift in Sources */, + FEAE9ABE1FEC4FFE00B52CA9 /* JSONDeserializingTests.swift in Sources */, + FEAE9ABA1FEC4FF900B52CA9 /* ValidatableObjectProvider.swift in Sources */, + FEAE9B191FEC501F00B52CA9 /* NewPlan.Entry.RunTests.swift in Sources */, + FEAE9AF11FEC501400B52CA9 /* RunTests.swift in Sources */, + FEAE9AF61FEC501400B52CA9 /* TestTests.swift in Sources */, + FEAE9AD41FEC500600B52CA9 /* Array+RandomTests.swift in Sources */, + FEAE9AE41FEC501400B52CA9 /* Config.ContextTests.swift in Sources */, + FEAE9A9F1FEC4FF200B52CA9 /* TestCredentials.swift in Sources */, + FEAE9AE31FEC501400B52CA9 /* ConfigTests.swift in Sources */, + FEAE9AE51FEC501400B52CA9 /* CaseTests.swift in Sources */, + FEAE9ABF1FEC4FFE00B52CA9 /* JSONSerializingTests.swift in Sources */, + FEAE9ACE1FEC500200B52CA9 /* ErrorContainerTests.swift in Sources */, + FEAE9B181FEC501F00B52CA9 /* NewPlan.EntryTests.swift in Sources */, + FEAE9AB01FEC4FF500B52CA9 /* AssertProperties.swift in Sources */, + FEAE9B201FEC501F00B52CA9 /* NewRunTests.swift in Sources */, + FEAE9B131FEC501F00B52CA9 /* NewCaseTests.swift in Sources */, + FEAE9ABD1FEC4FFE00B52CA9 /* InitTests.swift in Sources */, + FEAE9ADC1FEC500D00B52CA9 /* ModelTests.swift in Sources */, + FEAE9ADF1FEC501000B52CA9 /* UniqueSelectionTests.swift in Sources */, + FEAE9AED1FEC501400B52CA9 /* PriorityTests.swift in Sources */, + FEAE9AF01FEC501400B52CA9 /* ResultFieldTests.swift in Sources */, + FEAE9AC11FEC4FFE00B52CA9 /* UpdateRequestJSONTests.swift in Sources */, + FEAE9AEC1FEC501400B52CA9 /* Plan.EntryTests.swift in Sources */, + FEAE9B211FEC501F00B52CA9 /* NewSectionTests.swift in Sources */, + FEAE9AB71FEC4FF900B52CA9 /* CustomFieldsDataProvider.swift in Sources */, + FEAE9AAF1FEC4FF500B52CA9 /* AssertJSONTwoWaySerialization.swift in Sources */, + FEAE9B141FEC501F00B52CA9 /* NewConfigurationGroupTests.swift in Sources */, + FEAE9AC01FEC4FFE00B52CA9 /* JSONTwoWaySerializationTests.swift in Sources */, + FEAE9B101FEC501B00B52CA9 /* UpdateModelTests.swift in Sources */, + FEAE9AEF1FEC501400B52CA9 /* ResultTests.swift in Sources */, + FEAE9ABC1FEC4FFE00B52CA9 /* EquatableTests.swift in Sources */, + FEAE9B221FEC501F00B52CA9 /* NewSuiteTests.swift in Sources */, + FEAE9AE61FEC501400B52CA9 /* CaseFieldTests.swift in Sources */, + FEAE9AAE1FEC4FF500B52CA9 /* AssertJSONSerializing.swift in Sources */, + FEAE9AB11FEC4FF500B52CA9 /* AssertUpdateRequestJSON.swift in Sources */, + FEAE9AAC1FEC4FF500B52CA9 /* AssertEquatable.swift in Sources */, + FEAE9AEA1FEC501400B52CA9 /* MilestoneTests.swift in Sources */, + FEAE9AE71FEC501400B52CA9 /* CaseTypeTests.swift in Sources */, + FEAE9AB21FEC4FF500B52CA9 /* AssertValidatable.swift in Sources */, + FEAE9A9E1FEC4FF200B52CA9 /* Array+Random.swift in Sources */, + FEAE9AF31FEC501400B52CA9 /* StatusTests.swift in Sources */, + FEAE9AE91FEC501400B52CA9 /* ConfigurationGroupTests.swift in Sources */, + FEAE9B171FEC501F00B52CA9 /* NewPlanTests.swift in Sources */, + FEAE9AD51FEC500600B52CA9 /* Equatable+OptionalArrayTests.swift in Sources */, + FEAE9AE81FEC501400B52CA9 /* ConfigurationTests.swift in Sources */, + FEAE9ACD1FEC500200B52CA9 /* CustomFieldsContainerTests.swift in Sources */, + FEAE9AC31FEC4FFE00B52CA9 /* VariablePropertyTests.swift in Sources */, + FEAE9B351FEC502300B52CA9 /* UpdatePlanEntryRunsTests.swift in Sources */, + FEAE9AF41FEC501400B52CA9 /* SuiteTests.swift in Sources */, + FEAE9AAB1FEC4FF500B52CA9 /* AssertCustomFields.swift in Sources */, + FE0B15491FED68A00009B570 /* ObjectAPITests.swift in Sources */, + FEAE9AF71FEC501400B52CA9 /* UserTests.swift in Sources */, + FEAE9AF51FEC501400B52CA9 /* TemplateTests.swift in Sources */, + FEAE9AB91FEC4FF900B52CA9 /* ObjectProvider.swift in Sources */, + FEAE9B1C1FEC501F00B52CA9 /* NewCaseResultsTests.swift in Sources */, + FEAE9AC21FEC4FFE00B52CA9 /* ValidatableTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FEAE99761FEC3BEB00B52CA9 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FEAE99A21FEC42D800B52CA9 /* ErrorContainer.swift in Sources */, + FEAE9A511FEC4F0F00B52CA9 /* Filter.swift in Sources */, + FEAE9A861FEC4F1500B52CA9 /* NewSuite.swift in Sources */, + FEAE999F1FEC42D400B52CA9 /* CustomFieldsContainer.swift in Sources */, + FEAE999C1FEC42D000B52CA9 /* MutableCustomFields.swift in Sources */, + FEAE99D61FEC431700B52CA9 /* UpdateRequestJSON.swift in Sources */, + FEAE99981FEC42CD00B52CA9 /* CustomFields.swift in Sources */, + FEAE9A981FEC4F1E00B52CA9 /* ObjectAPI.swift in Sources */, + FEAE9A481FEC4F0B00B52CA9 /* ObjectAPI.DataProcessingErrorDebug.swift in Sources */, + FEAE9A2B1FEC4F0000B52CA9 /* Test.swift in Sources */, + FEAE9A851FEC4F1500B52CA9 /* NewSection.swift in Sources */, + FEAE9A7A1FEC4F1500B52CA9 /* NewMilestone.swift in Sources */, + FEAE9A171FEC4EFB00B52CA9 /* ResultField.swift in Sources */, + FEAE9A891FEC4F1A00B52CA9 /* UpdatePlanEntryRuns.swift in Sources */, + FEAE99B71FEC42F100B52CA9 /* Array+ContentComparison.swift in Sources */, + FEAE99DA1FEC431B00B52CA9 /* UpdateRequestJSONKeys.swift in Sources */, + FEAE9A4F1FEC4F0B00B52CA9 /* ObjectAPI.UpdateRequestErrorDebug.swift in Sources */, + FEAE9A951FEC4F1E00B52CA9 /* GetProjectOperation.swift in Sources */, + FEAE99E91FEC432F00B52CA9 /* CustomFieldType.swift in Sources */, + FEAE99DD1FEC431F00B52CA9 /* Validatable.swift in Sources */, + FEAE9A771FEC4F1500B52CA9 /* NewCase.swift in Sources */, + FEAE99CD1FEC430C00B52CA9 /* JSONDeserializable.swift in Sources */, + FEAE9A781FEC4F1500B52CA9 /* NewConfiguration.swift in Sources */, + FEAE99FD1FEC4ECB00B52CA9 /* Configuration.swift in Sources */, + FEAE9A7F1FEC4F1500B52CA9 /* NewResult.swift in Sources */, + FEAE99F71FEC4EC500B52CA9 /* CaseField.swift in Sources */, + FEAE9A4A1FEC4F0B00B52CA9 /* ObjectAPI.MatchErrorDebug.swift in Sources */, + FEAE9A491FEC4F0B00B52CA9 /* ObjectAPI.DataRequestErrorDebug.swift in Sources */, + FEAE99A81FEC42DE00B52CA9 /* DebugDescription.swift in Sources */, + FEAE9A4B1FEC4F0B00B52CA9 /* ObjectAPI.ObjectConversionErrorDebug.swift in Sources */, + FEAE99A51FEC42DB00B52CA9 /* JSONDictionaryContainer.swift in Sources */, + FEAE99FA1FEC4EC800B52CA9 /* CaseType.swift in Sources */, + FEAE99F41FEC4EC100B52CA9 /* Case.swift in Sources */, + FEAE9A141FEC4EF700B52CA9 /* Result.swift in Sources */, + FEAE99F21FEC433B00B52CA9 /* Config.Context.swift in Sources */, + FEAE9A971FEC4F1E00B52CA9 /* API.swift in Sources */, + FEAE9A7B1FEC4F1500B52CA9 /* NewPlan.swift in Sources */, + FEAE99AF1FEC42E800B52CA9 /* SingleMatchError.swift in Sources */, + FEAE9A7C1FEC4F1500B52CA9 /* NewPlan.Entry.swift in Sources */, + FEAE9A4E1FEC4F0B00B52CA9 /* ObjectAPI.StatusCodeErrorDebug.swift in Sources */, + FEAE9A2A1FEC4F0000B52CA9 /* Template.swift in Sources */, + FEAE9A031FEC4ED400B52CA9 /* Milestone.swift in Sources */, + FEAE9A501FEC4F0B00B52CA9 /* URLRequestDebug.swift in Sources */, + FEAE99AC1FEC42E400B52CA9 /* DebugDetails.swift in Sources */, + FEAE9A091FEC4EDC00B52CA9 /* Plan.Entry.swift in Sources */, + FEAE9A811FEC4F1500B52CA9 /* NewCaseResults.Result.swift in Sources */, + FEAE99CA1FEC430800B52CA9 /* JSONDictionary.swift in Sources */, + FEAE99B31FEC42EC00B52CA9 /* MultipleMatchError.swift in Sources */, + FEAE9A111FEC4EF200B52CA9 /* Project.swift in Sources */, + FEAE9A4C1FEC4F0B00B52CA9 /* ObjectAPI.RequestErrorDebug.swift in Sources */, + FEAE9A961FEC4F1E00B52CA9 /* GetTemplatesOperation.swift in Sources */, + FEAE9A471FEC4F0B00B52CA9 /* ObjectAPI.ClientErrorDebug.swift in Sources */, + FEAE9A4D1FEC4F0B00B52CA9 /* ObjectAPI.ServerErrorDebug.swift in Sources */, + FEAE9A0E1FEC4EEE00B52CA9 /* Priority.swift in Sources */, + FEAE9A7E1FEC4F1500B52CA9 /* NewProject.swift in Sources */, + FEAE9A801FEC4F1500B52CA9 /* NewCaseResults.swift in Sources */, + FEAE9A261FEC4F0000B52CA9 /* Run.swift in Sources */, + FEAE99C11FEC42FC00B52CA9 /* Equatable+OptionalArray.swift in Sources */, + FEAE99D01FEC430F00B52CA9 /* JSONSerializable.swift in Sources */, + FEAE99EC1FEC433200B52CA9 /* Project.SuiteMode.swift in Sources */, + FEAE99C71FEC430300B52CA9 /* JSONKey.swift in Sources */, + FEAE99921FEC42C600B52CA9 /* AddRequestJSON.swift in Sources */, + FEAE9A321FEC4F0700B52CA9 /* API.RequestResultDebug.swift in Sources */, + FEAE9A941FEC4F1E00B52CA9 /* GetConfigurationGroupsOperation.swift in Sources */, + FEAE9A7D1FEC4F1500B52CA9 /* NewPlan.Entry.Run.swift in Sources */, + FEAE99951FEC42C900B52CA9 /* AddRequestJSONKeys.swift in Sources */, + FEAE9A841FEC4F1500B52CA9 /* NewRun.swift in Sources */, + FEAE99EF1FEC433600B52CA9 /* Config.swift in Sources */, + FEAE99E31FEC432800B52CA9 /* QueryItemProvider.swift in Sources */, + FEAE99E01FEC432300B52CA9 /* Outcome.swift in Sources */, + FEAE9A271FEC4F0000B52CA9 /* Section.swift in Sources */, + FEAE99C41FEC430000B52CA9 /* Identifiable.swift in Sources */, + FEAE9A831FEC4F1500B52CA9 /* NewTestResults.Result.swift in Sources */, + FEAE9A2C1FEC4F0000B52CA9 /* User.swift in Sources */, + FEAE9A311FEC4F0700B52CA9 /* API.RequestErrorDebug.swift in Sources */, + FEAE9A001FEC4ED000B52CA9 /* ConfigurationGroup.swift in Sources */, + FEAE9A291FEC4F0000B52CA9 /* Suite.swift in Sources */, + FEAE99D31FEC431300B52CA9 /* AsyncOperation.swift in Sources */, + FEAE9A281FEC4F0000B52CA9 /* Status.swift in Sources */, + FEAE9A061FEC4ED800B52CA9 /* Plan.swift in Sources */, + FEAE9A821FEC4F1500B52CA9 /* NewTestResults.swift in Sources */, + FEAE9A521FEC4F0F00B52CA9 /* Filter.Value.swift in Sources */, + FEAE99BD1FEC42F700B52CA9 /* Date+Seconds.swift in Sources */, + FEAE99E61FEC432B00B52CA9 /* UniqueSelection.swift in Sources */, + FEAE9A791FEC4F1500B52CA9 /* NewConfigurationGroup.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FEAE997F1FEC3BEB00B52CA9 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FEAE9B2A1FEC502000B52CA9 /* NewProjectTests.swift in Sources */, + FEAE9AE01FEC501100B52CA9 /* CustomFieldTypeTests.swift in Sources */, + FEAE9B0E1FEC501800B52CA9 /* FilterTests.swift in Sources */, + FEAE9B261FEC502000B52CA9 /* NewMilestoneTests.swift in Sources */, + FEAE9AD91FEC500900B52CA9 /* AsyncOperationTests.swift in Sources */, + FEAE9B251FEC502000B52CA9 /* NewConfigurationTests.swift in Sources */, + FEAE9AD61FEC500700B52CA9 /* Array+ContentComparisonTests.swift in Sources */, + FEAE9AB41FEC4FF800B52CA9 /* JSONDataProvider.swift in Sources */, + FEAE9B2D1FEC502000B52CA9 /* NewCaseResults.ResultTests.swift in Sources */, + FEAE9B001FEC501400B52CA9 /* PlanTests.swift in Sources */, + FEAE9B071FEC501400B52CA9 /* SectionTests.swift in Sources */, + FEAE9B031FEC501400B52CA9 /* ProjectTests.swift in Sources */, + FEAE9AE11FEC501100B52CA9 /* Project.SuiteModeTests.swift in Sources */, + FEAE9B2E1FEC502000B52CA9 /* NewTestResultsTests.swift in Sources */, + FEAE9B2F1FEC502000B52CA9 /* NewTestResults.ResultTests.swift in Sources */, + FEAE9B111FEC501C00B52CA9 /* AddModelTests.swift in Sources */, + FEAE9AA41FEC4FF500B52CA9 /* AssertJSONDeserializing.swift in Sources */, + FEAE9AA11FEC4FF500B52CA9 /* AssertAddRequestJSON.swift in Sources */, + FEAE9B2B1FEC502000B52CA9 /* NewResultTests.swift in Sources */, + FEAE9AD21FEC500200B52CA9 /* JSONDictionaryContainerTests.swift in Sources */, + FEAE9AC41FEC4FFF00B52CA9 /* AddRequestJSONTests.swift in Sources */, + FEAE9AC71FEC4FFF00B52CA9 /* JSONDeserializingTests.swift in Sources */, + FEAE9AB61FEC4FF800B52CA9 /* ValidatableObjectProvider.swift in Sources */, + FEAE9B291FEC502000B52CA9 /* NewPlan.Entry.RunTests.swift in Sources */, + FEAE9B061FEC501400B52CA9 /* RunTests.swift in Sources */, + FEAE9B0B1FEC501400B52CA9 /* TestTests.swift in Sources */, + FEAE9AD71FEC500700B52CA9 /* Array+RandomTests.swift in Sources */, + FEAE9AF91FEC501400B52CA9 /* Config.ContextTests.swift in Sources */, + FEAE9A9C1FEC4FF100B52CA9 /* TestCredentials.swift in Sources */, + FEAE9AF81FEC501400B52CA9 /* ConfigTests.swift in Sources */, + FEAE9AFA1FEC501400B52CA9 /* CaseTests.swift in Sources */, + FEAE9AC81FEC4FFF00B52CA9 /* JSONSerializingTests.swift in Sources */, + FEAE9AD11FEC500200B52CA9 /* ErrorContainerTests.swift in Sources */, + FEAE9B281FEC502000B52CA9 /* NewPlan.EntryTests.swift in Sources */, + FEAE9AA71FEC4FF500B52CA9 /* AssertProperties.swift in Sources */, + FEAE9B301FEC502000B52CA9 /* NewRunTests.swift in Sources */, + FEAE9B231FEC502000B52CA9 /* NewCaseTests.swift in Sources */, + FEAE9AC61FEC4FFF00B52CA9 /* InitTests.swift in Sources */, + FEAE9ADB1FEC500D00B52CA9 /* ModelTests.swift in Sources */, + FEAE9AE21FEC501100B52CA9 /* UniqueSelectionTests.swift in Sources */, + FEAE9B021FEC501400B52CA9 /* PriorityTests.swift in Sources */, + FEAE9B051FEC501400B52CA9 /* ResultFieldTests.swift in Sources */, + FEAE9ACA1FEC4FFF00B52CA9 /* UpdateRequestJSONTests.swift in Sources */, + FEAE9B011FEC501400B52CA9 /* Plan.EntryTests.swift in Sources */, + FEAE9B311FEC502000B52CA9 /* NewSectionTests.swift in Sources */, + FEAE9AB31FEC4FF800B52CA9 /* CustomFieldsDataProvider.swift in Sources */, + FEAE9AA61FEC4FF500B52CA9 /* AssertJSONTwoWaySerialization.swift in Sources */, + FEAE9B241FEC502000B52CA9 /* NewConfigurationGroupTests.swift in Sources */, + FEAE9AC91FEC4FFF00B52CA9 /* JSONTwoWaySerializationTests.swift in Sources */, + FEAE9B121FEC501C00B52CA9 /* UpdateModelTests.swift in Sources */, + FEAE9B041FEC501400B52CA9 /* ResultTests.swift in Sources */, + FEAE9AC51FEC4FFF00B52CA9 /* EquatableTests.swift in Sources */, + FEAE9B321FEC502000B52CA9 /* NewSuiteTests.swift in Sources */, + FEAE9AFB1FEC501400B52CA9 /* CaseFieldTests.swift in Sources */, + FEAE9AA51FEC4FF500B52CA9 /* AssertJSONSerializing.swift in Sources */, + FEAE9AA81FEC4FF500B52CA9 /* AssertUpdateRequestJSON.swift in Sources */, + FEAE9AA31FEC4FF500B52CA9 /* AssertEquatable.swift in Sources */, + FEAE9AFF1FEC501400B52CA9 /* MilestoneTests.swift in Sources */, + FEAE9AFC1FEC501400B52CA9 /* CaseTypeTests.swift in Sources */, + FEAE9AA91FEC4FF500B52CA9 /* AssertValidatable.swift in Sources */, + FEAE9A9B1FEC4FF100B52CA9 /* Array+Random.swift in Sources */, + FEAE9B081FEC501400B52CA9 /* StatusTests.swift in Sources */, + FEAE9AFE1FEC501400B52CA9 /* ConfigurationGroupTests.swift in Sources */, + FEAE9B271FEC502000B52CA9 /* NewPlanTests.swift in Sources */, + FEAE9AD81FEC500700B52CA9 /* Equatable+OptionalArrayTests.swift in Sources */, + FEAE9AFD1FEC501400B52CA9 /* ConfigurationTests.swift in Sources */, + FEAE9AD01FEC500200B52CA9 /* CustomFieldsContainerTests.swift in Sources */, + FEAE9ACC1FEC4FFF00B52CA9 /* VariablePropertyTests.swift in Sources */, + FEAE9B331FEC502300B52CA9 /* UpdatePlanEntryRunsTests.swift in Sources */, + FEAE9B091FEC501400B52CA9 /* SuiteTests.swift in Sources */, + FEAE9AA21FEC4FF500B52CA9 /* AssertCustomFields.swift in Sources */, + FE0B15481FED68A00009B570 /* ObjectAPITests.swift in Sources */, + FEAE9B0C1FEC501400B52CA9 /* UserTests.swift in Sources */, + FEAE9B0A1FEC501400B52CA9 /* TemplateTests.swift in Sources */, + FEAE9AB51FEC4FF800B52CA9 /* ObjectProvider.swift in Sources */, + FEAE9B2C1FEC502000B52CA9 /* NewCaseResultsTests.swift in Sources */, + FEAE9ACB1FEC4FFF00B52CA9 /* ValidatableTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + FE23A9281F59F3A0007E946D /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FE23A91B1F59F39F007E946D /* QuizTrain-iOS */; + targetProxy = FE23A9271F59F3A0007E946D /* PBXContainerItemProxy */; + }; + FEAE996A1FEC3BCE00B52CA9 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FEAE995E1FEC3BCD00B52CA9 /* QuizTrain-tvOS */; + targetProxy = FEAE99691FEC3BCE00B52CA9 /* PBXContainerItemProxy */; + }; + FEAE99861FEC3BEB00B52CA9 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FEAE997A1FEC3BEB00B52CA9 /* QuizTrain-macOS */; + targetProxy = FEAE99851FEC3BEB00B52CA9 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + FE23A92E1F59F3A0007E946D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + MACOSX_DEPLOYMENT_TARGET = 10.12; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + FE23A92F1F59F3A0007E946D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + MACOSX_DEPLOYMENT_TARGET = 10.12; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + FE23A9311F59F3A0007E946D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "$(SRCROOT)/QuizTrain/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = venmo.QuizTrain; + PRODUCT_NAME = QuizTrain; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + FE23A9321F59F3A0007E946D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "$(SRCROOT)/QuizTrain/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = venmo.QuizTrain; + PRODUCT_NAME = QuizTrain; + SKIP_INSTALL = YES; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + FE23A9341F59F3A0007E946D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = QuizTrainTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = venmo.QuizTrainTests; + PRODUCT_NAME = QuizTrainTests; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + FE23A9351F59F3A0007E946D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = QuizTrainTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = venmo.QuizTrainTests; + PRODUCT_NAME = QuizTrainTests; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + FEAE99571FEC38BA00B52CA9 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "$(SRCROOT)/QuizTrain/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = venmo.QuizTrain; + PRODUCT_NAME = QuizTrain; + SDKROOT = watchos; + SKIP_INSTALL = YES; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = 4; + WATCHOS_DEPLOYMENT_TARGET = 3.0; + }; + name = Debug; + }; + FEAE99581FEC38BA00B52CA9 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "$(SRCROOT)/QuizTrain/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = venmo.QuizTrain; + PRODUCT_NAME = QuizTrain; + SDKROOT = watchos; + SKIP_INSTALL = YES; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = 4; + WATCHOS_DEPLOYMENT_TARGET = 3.0; + }; + name = Release; + }; + FEAE99711FEC3BCE00B52CA9 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "$(SRCROOT)/QuizTrain/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = venmo.QuizTrain; + PRODUCT_NAME = QuizTrain; + SDKROOT = appletvos; + SKIP_INSTALL = YES; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = 3; + TVOS_DEPLOYMENT_TARGET = 10.0; + }; + name = Debug; + }; + FEAE99721FEC3BCE00B52CA9 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "$(SRCROOT)/QuizTrain/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = venmo.QuizTrain; + PRODUCT_NAME = QuizTrain; + SDKROOT = appletvos; + SKIP_INSTALL = YES; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = 3; + TVOS_DEPLOYMENT_TARGET = 10.0; + }; + name = Release; + }; + FEAE99741FEC3BCE00B52CA9 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = QuizTrainTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = venmo.QuizTrainTests; + PRODUCT_NAME = QuizTrainTests; + SDKROOT = appletvos; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = 3; + TVOS_DEPLOYMENT_TARGET = 11.2; + }; + name = Debug; + }; + FEAE99751FEC3BCE00B52CA9 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = QuizTrainTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = venmo.QuizTrainTests; + PRODUCT_NAME = QuizTrainTests; + SDKROOT = appletvos; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = 3; + TVOS_DEPLOYMENT_TARGET = 11.2; + }; + name = Release; + }; + FEAE998D1FEC3BEB00B52CA9 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = "-"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_VERSION = A; + INFOPLIST_FILE = "$(SRCROOT)/QuizTrain/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.12; + PRODUCT_BUNDLE_IDENTIFIER = venmo.QuizTrain; + PRODUCT_NAME = QuizTrain; + SDKROOT = macosx; + SKIP_INSTALL = YES; + SWIFT_VERSION = 4.0; + }; + name = Debug; + }; + FEAE998E1FEC3BEB00B52CA9 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = "-"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_VERSION = A; + INFOPLIST_FILE = "$(SRCROOT)/QuizTrain/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.12; + PRODUCT_BUNDLE_IDENTIFIER = venmo.QuizTrain; + PRODUCT_NAME = QuizTrain; + SDKROOT = macosx; + SKIP_INSTALL = YES; + SWIFT_VERSION = 4.0; + }; + name = Release; + }; + FEAE99901FEC3BEB00B52CA9 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_IDENTITY = "-"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = QuizTrainTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.12; + PRODUCT_BUNDLE_IDENTIFIER = venmo.QuizTrainTests; + PRODUCT_NAME = QuizTrainTests; + SDKROOT = macosx; + SWIFT_VERSION = 4.0; + }; + name = Debug; + }; + FEAE99911FEC3BEB00B52CA9 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_IDENTITY = "-"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = QuizTrainTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.12; + PRODUCT_BUNDLE_IDENTIFIER = venmo.QuizTrainTests; + PRODUCT_NAME = QuizTrainTests; + SDKROOT = macosx; + SWIFT_VERSION = 4.0; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + FE23A9161F59F39F007E946D /* Build configuration list for PBXProject "QuizTrain" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FE23A92E1F59F3A0007E946D /* Debug */, + FE23A92F1F59F3A0007E946D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FE23A9301F59F3A0007E946D /* Build configuration list for PBXNativeTarget "QuizTrain-iOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FE23A9311F59F3A0007E946D /* Debug */, + FE23A9321F59F3A0007E946D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FE23A9331F59F3A0007E946D /* Build configuration list for PBXNativeTarget "QuizTrainTests-iOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FE23A9341F59F3A0007E946D /* Debug */, + FE23A9351F59F3A0007E946D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FEAE99561FEC38BA00B52CA9 /* Build configuration list for PBXNativeTarget "QuizTrain-watchOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FEAE99571FEC38BA00B52CA9 /* Debug */, + FEAE99581FEC38BA00B52CA9 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FEAE99701FEC3BCE00B52CA9 /* Build configuration list for PBXNativeTarget "QuizTrain-tvOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FEAE99711FEC3BCE00B52CA9 /* Debug */, + FEAE99721FEC3BCE00B52CA9 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FEAE99731FEC3BCE00B52CA9 /* Build configuration list for PBXNativeTarget "QuizTrainTests-tvOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FEAE99741FEC3BCE00B52CA9 /* Debug */, + FEAE99751FEC3BCE00B52CA9 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FEAE998C1FEC3BEB00B52CA9 /* Build configuration list for PBXNativeTarget "QuizTrain-macOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FEAE998D1FEC3BEB00B52CA9 /* Debug */, + FEAE998E1FEC3BEB00B52CA9 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FEAE998F1FEC3BEB00B52CA9 /* Build configuration list for PBXNativeTarget "QuizTrainTests-macOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FEAE99901FEC3BEB00B52CA9 /* Debug */, + FEAE99911FEC3BEB00B52CA9 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = FE23A9131F59F39F007E946D /* Project object */; +} diff --git a/QuizTrain.xcodeproj/xcshareddata/xcschemes/QuizTrain-iOS.xcscheme b/QuizTrain.xcodeproj/xcshareddata/xcschemes/QuizTrain-iOS.xcscheme new file mode 100644 index 0000000..90f6f2e --- /dev/null +++ b/QuizTrain.xcodeproj/xcshareddata/xcschemes/QuizTrain-iOS.xcscheme @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/QuizTrain.xcodeproj/xcshareddata/xcschemes/QuizTrain-macOS.xcscheme b/QuizTrain.xcodeproj/xcshareddata/xcschemes/QuizTrain-macOS.xcscheme new file mode 100644 index 0000000..b67ac23 --- /dev/null +++ b/QuizTrain.xcodeproj/xcshareddata/xcschemes/QuizTrain-macOS.xcscheme @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/QuizTrain.xcodeproj/xcshareddata/xcschemes/QuizTrain-tvOS.xcscheme b/QuizTrain.xcodeproj/xcshareddata/xcschemes/QuizTrain-tvOS.xcscheme new file mode 100644 index 0000000..af89266 --- /dev/null +++ b/QuizTrain.xcodeproj/xcshareddata/xcschemes/QuizTrain-tvOS.xcscheme @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/QuizTrain.xcodeproj/xcshareddata/xcschemes/QuizTrain-watchOS.xcscheme b/QuizTrain.xcodeproj/xcshareddata/xcschemes/QuizTrain-watchOS.xcscheme new file mode 100644 index 0000000..9f94c10 --- /dev/null +++ b/QuizTrain.xcodeproj/xcshareddata/xcschemes/QuizTrain-watchOS.xcscheme @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/QuizTrain.xcodeproj/xcshareddata/xcschemes/QuizTrainTests-iOS.xcscheme b/QuizTrain.xcodeproj/xcshareddata/xcschemes/QuizTrainTests-iOS.xcscheme new file mode 100644 index 0000000..e1ee477 --- /dev/null +++ b/QuizTrain.xcodeproj/xcshareddata/xcschemes/QuizTrainTests-iOS.xcscheme @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/QuizTrain.xcodeproj/xcshareddata/xcschemes/QuizTrainTests-macOS.xcscheme b/QuizTrain.xcodeproj/xcshareddata/xcschemes/QuizTrainTests-macOS.xcscheme new file mode 100644 index 0000000..5e4b8d9 --- /dev/null +++ b/QuizTrain.xcodeproj/xcshareddata/xcschemes/QuizTrainTests-macOS.xcscheme @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/QuizTrain.xcodeproj/xcshareddata/xcschemes/QuizTrainTests-tvOS.xcscheme b/QuizTrain.xcodeproj/xcshareddata/xcschemes/QuizTrainTests-tvOS.xcscheme new file mode 100644 index 0000000..f6e6896 --- /dev/null +++ b/QuizTrain.xcodeproj/xcshareddata/xcschemes/QuizTrainTests-tvOS.xcscheme @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/QuizTrain/.swiftlint.yml b/QuizTrain/.swiftlint.yml new file mode 100644 index 0000000..e69de29 diff --git a/QuizTrain/Info.plist b/QuizTrain/Info.plist new file mode 100644 index 0000000..4c0d218 --- /dev/null +++ b/QuizTrain/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSPrincipalClass + + + diff --git a/QuizTrain/Misc/Add/AddRequestJSON.swift b/QuizTrain/Misc/Add/AddRequestJSON.swift new file mode 100644 index 0000000..3365d6d --- /dev/null +++ b/QuizTrain/Misc/Add/AddRequestJSON.swift @@ -0,0 +1,16 @@ +/* + Returns JSON for an add request. + */ +protocol AddRequestJSON { + var addRequestJSON: JSONDictionary { get } +} + +extension AddRequestJSON where Self: JSONSerializable & AddRequestJSONKeys { + + var addRequestJSON: JSONDictionary { + var json = serialized() + json = json.filter { pair in addRequestJSONKeys.contains(pair.key) } + return json + } + +} diff --git a/QuizTrain/Misc/Add/AddRequestJSONKeys.swift b/QuizTrain/Misc/Add/AddRequestJSONKeys.swift new file mode 100644 index 0000000..865c340 --- /dev/null +++ b/QuizTrain/Misc/Add/AddRequestJSONKeys.swift @@ -0,0 +1,7 @@ +/* + Provides JSONKey's for all properties which can be submitted in an add request + to the API. + */ +protocol AddRequestJSONKeys { + var addRequestJSONKeys: [JSONKey] { get } +} diff --git a/QuizTrain/Misc/Containment/Containers/CustomFieldsContainer.swift b/QuizTrain/Misc/Containment/Containers/CustomFieldsContainer.swift new file mode 100644 index 0000000..1c9976c --- /dev/null +++ b/QuizTrain/Misc/Containment/Containers/CustomFieldsContainer.swift @@ -0,0 +1,67 @@ +/* + Provides a container for TestRail custom fields which can be added to some + objects. Enforces all custom fields to be prefixed with "custom_" (case + sensitive) in their key. Prevents adding any "custom_*" entries matching + omittedKeys. Any keys violating those rules will be silently omitted from being + added to customFields. + */ +struct CustomFieldsContainer: JSONDeserializable, JSONSerializable { + + // MARK: - Properties + + fileprivate var container: JSONDictionaryContainer + + public var customFields: JSONDictionary { + get { + return container.json + } + set { + container.json = CustomFieldsContainer.filter(newValue, requiringKeyPrefix: CustomFieldsContainer.requiredKeyPrefix, omittingKeys: self.omittedKeys) + } + } + + // MARK: - JSONDeserializable + + init(json: JSONDictionary) { + self.init(json: json, omittingKeys: []) + } + + init(json: JSONDictionary, omittingKeys omittedKeys: [JSONKey]) { + self.omittedKeys = omittedKeys + container = JSONDictionaryContainer(json: CustomFieldsContainer.filter(json, requiringKeyPrefix: CustomFieldsContainer.requiredKeyPrefix, omittingKeys: omittedKeys)) + } + + // MARK: - JSONSerializable + + func serialized() -> JSONDictionary { + return container.json + } + + // MARK: - Filtering + + public let omittedKeys: [JSONKey] // Keys which are omitted from customFields. This should contain any strongly-typed properties also prefixed with requiredKeyPrefix. + private static let requiredKeyPrefix: JSONKey = "custom_" // All TestRail custom fields are prefixed with: "custom_" + + private static func filter(_ json: JSONDictionary, requiringKeyPrefix requiredKeyPrefix: String, omittingKeys omittedKeys: [JSONKey]) -> JSONDictionary { + var jsonFiltered = json.filter { pair in pair.key.hasPrefix(requiredKeyPrefix) } + jsonFiltered = jsonFiltered.filter { pair in !omittedKeys.contains(pair.key) } + return jsonFiltered + } + +} + +extension CustomFieldsContainer: Equatable { + + static func==(lhs: CustomFieldsContainer, rhs: CustomFieldsContainer) -> Bool { + return lhs.container == rhs.container + } + +} + +extension CustomFieldsContainer { + + public static func empty() -> CustomFieldsContainer { + return CustomFieldsContainer(json: [:]) + } + +} diff --git a/QuizTrain/Misc/Containment/Containers/ErrorContainer.swift b/QuizTrain/Misc/Containment/Containers/ErrorContainer.swift new file mode 100644 index 0000000..d45fa96 --- /dev/null +++ b/QuizTrain/Misc/Containment/Containers/ErrorContainer.swift @@ -0,0 +1,44 @@ +/* + Provides a container conforming to Error which stores 1+ items also conforming + to Error. Useful in situations where multiple errors can occur. + */ +public struct ErrorContainer : Error { + + public let errors: [ErrorType] + + public init(_ error: ErrorType) { + self.errors = [error] + } + + public init?(_ errors: [ErrorType]) { + guard errors.count > 0 else { + return nil + } + self.errors = errors + } + +} + +extension ErrorContainer: DebugDescription { + + var debugDescription: String { + return "\(errors)" + } + +} + +extension ErrorContainer where ErrorType: DebugDescription { + + var debugDescription: String { + var description = "" + for error in errors { + if description.count == 0 { + description += error.debugDescription + } else { + description += "\n\n\(error.debugDescription)" + } + } + return description + } + +} diff --git a/QuizTrain/Misc/Containment/Containers/JSONDictionaryContainer.swift b/QuizTrain/Misc/Containment/Containers/JSONDictionaryContainer.swift new file mode 100644 index 0000000..9ee68f6 --- /dev/null +++ b/QuizTrain/Misc/Containment/Containers/JSONDictionaryContainer.swift @@ -0,0 +1,35 @@ +import Foundation + +/* + Provides a container to store arbitrary JSON dictionaries. + */ +struct JSONDictionaryContainer: JSONDeserializable, JSONSerializable { + + var json: JSONDictionary + + init(json: JSONDictionary) { + self.json = json + } + + func serialized() -> JSONDictionary { + return json + } + +} + +extension JSONDictionaryContainer: Equatable { + + static func==(lhs: JSONDictionaryContainer, rhs: JSONDictionaryContainer) -> Bool { + + let lhsSerialized = lhs.serialized() + let rhsSerialized = rhs.serialized() + + // Attempt to cast to NSDictionary to rely on its isEqual code. + if lhsSerialized as NSDictionary == rhsSerialized as NSDictionary { + return true + } + + return false + } + +} diff --git a/QuizTrain/Misc/Containment/Protocols/CustomFields.swift b/QuizTrain/Misc/Containment/Protocols/CustomFields.swift new file mode 100644 index 0000000..40a2c5d --- /dev/null +++ b/QuizTrain/Misc/Containment/Protocols/CustomFields.swift @@ -0,0 +1,13 @@ +/* + Provides read-only CustomField support. + */ +protocol CustomFields { + var customFields: JSONDictionary { get } + var customFieldsContainer: CustomFieldsContainer { get } +} + +extension CustomFields { + public var customFields: JSONDictionary { + return self.customFieldsContainer.customFields + } +} diff --git a/QuizTrain/Misc/Containment/Protocols/MutableCustomFields.swift b/QuizTrain/Misc/Containment/Protocols/MutableCustomFields.swift new file mode 100644 index 0000000..b338b1c --- /dev/null +++ b/QuizTrain/Misc/Containment/Protocols/MutableCustomFields.swift @@ -0,0 +1,18 @@ +/* + Provides read-write CustomField support. + */ +protocol MutableCustomFields: CustomFields { + var customFields: JSONDictionary { get set } + var customFieldsContainer: CustomFieldsContainer { get set } +} + +extension MutableCustomFields { + public var customFields: JSONDictionary { + get { + return self.customFieldsContainer.customFields + } + set { + customFieldsContainer.customFields = newValue + } + } +} diff --git a/QuizTrain/Misc/Debug/DebugDescription.swift b/QuizTrain/Misc/Debug/DebugDescription.swift new file mode 100644 index 0000000..40ad2ab --- /dev/null +++ b/QuizTrain/Misc/Debug/DebugDescription.swift @@ -0,0 +1,6 @@ +/* + Provides a property to return a description for use in debugging. + */ +protocol DebugDescription { + var debugDescription: String { get } +} diff --git a/QuizTrain/Misc/Debug/DebugDetails.swift b/QuizTrain/Misc/Debug/DebugDetails.swift new file mode 100644 index 0000000..724557e --- /dev/null +++ b/QuizTrain/Misc/Debug/DebugDetails.swift @@ -0,0 +1,6 @@ +/* + Provides a property to return a details string for use in debugging. + */ +protocol DebugDetails { + var debugDetails: String { get } +} diff --git a/QuizTrain/Misc/Errors/MultipleMatchError.swift b/QuizTrain/Misc/Errors/MultipleMatchError.swift new file mode 100644 index 0000000..3e120ce --- /dev/null +++ b/QuizTrain/Misc/Errors/MultipleMatchError.swift @@ -0,0 +1,34 @@ +public enum MultipleMatchError: Error { + case noMatchesFound(missing: Set) + case partialMatchesFound(matches: [MatchType], missing: Set) +} + +extension MultipleMatchError: DebugDescription { + + var debugDescription: String { + var description = "ObjectAPI.MultipleMatchError" + switch self { + case .noMatchesFound: + description += ".noMatchesFound:\n\n\(debugDetails)\n" + case .partialMatchesFound(_): + description += ".partialMatchesFound:\n\n\(debugDetails)\n" + } + return description + } + +} + +extension MultipleMatchError: DebugDetails { + + var debugDetails: String { + let details: String + switch self { + case .noMatchesFound: + details = "Zero matches were found." + case .partialMatchesFound(let matches): + details = "\(matches)" + } + return details + } + +} diff --git a/QuizTrain/Misc/Errors/SingleMatchError.swift b/QuizTrain/Misc/Errors/SingleMatchError.swift new file mode 100644 index 0000000..97728ad --- /dev/null +++ b/QuizTrain/Misc/Errors/SingleMatchError.swift @@ -0,0 +1,29 @@ +public enum SingleMatchError: Error { + case noMatchFound(missing: QueryType) +} + +extension SingleMatchError: DebugDescription { + + var debugDescription: String { + var description = "ObjectAPI.SingleMatchError" + switch self { + case .noMatchFound: + description += ".noMatchFound:\n\n\(debugDetails)\n" + } + return description + } + +} + +extension SingleMatchError: DebugDetails { + + var debugDetails: String { + let details: String + switch self { + case .noMatchFound: + details = "No match was found." + } + return details + } + +} diff --git a/QuizTrain/Misc/Extensions/Array+ContentComparison.swift b/QuizTrain/Misc/Extensions/Array+ContentComparison.swift new file mode 100644 index 0000000..8fd4ab8 --- /dev/null +++ b/QuizTrain/Misc/Extensions/Array+ContentComparison.swift @@ -0,0 +1,47 @@ +extension Array where Array.Element: Equatable { + + /* + For an array of Equatable elements, determines if both contain the same + contents regardless of their ordering. + + let a1 = [1, 2, 2] + let a2 = [2, 2, 1] + let a3 = [1, 1, 2] + + a1 == a2 // false + Array.contentsAreEqual(a1, a2) // true + Array.contentsAreEqual(a1, a3) // false + */ + static func contentsAreEqual(_ lhs: [Array.Element]?, _ rhs: [Array.Element]?) -> Bool { + switch (lhs, rhs) { + case (.some(let l), .some(let r)): + return Array.contentsAreEqual(l, r) + case (.none, .none): + return true + default: + return false + } + } + + private static func contentsAreEqual(_ lhs: [Array.Element], _ rhs: [Array.Element]) -> Bool { + + guard lhs.count == rhs.count else { + return false + } + + var rhsCopy = rhs + for item in lhs { + guard let index = rhsCopy.index(of: item) else { + return false + } + rhsCopy.remove(at: index) + } + + return true + } + + func contentsAreEqual(to array: [Array.Element]?) -> Bool { + return Array.contentsAreEqual(self, array) + } + +} diff --git a/QuizTrain/Misc/Extensions/Date+Seconds.swift b/QuizTrain/Misc/Extensions/Date+Seconds.swift new file mode 100644 index 0000000..02367e3 --- /dev/null +++ b/QuizTrain/Misc/Extensions/Date+Seconds.swift @@ -0,0 +1,17 @@ +import Foundation + +/* + Convinience methods for TestRail dates. TestRail dates are always a Unix + timestamp as a whole number. + */ +extension Date { + + init(secondsSince1970 seconds: Int) { + self.init(timeIntervalSince1970: TimeInterval(seconds)) + } + + var secondsSince1970: Int { + return Int(timeIntervalSince1970) + } + +} diff --git a/QuizTrain/Misc/Extensions/Equatable+OptionalArray.swift b/QuizTrain/Misc/Extensions/Equatable+OptionalArray.swift new file mode 100644 index 0000000..5d81b9a --- /dev/null +++ b/QuizTrain/Misc/Extensions/Equatable+OptionalArray.swift @@ -0,0 +1,13 @@ +/* + Adds == comparison to optional arrays containing an Equatable type. + */ +func ==(lhs: [Type]?, rhs: [Type]?) -> Bool { + switch (lhs, rhs) { + case (.some(let l), .some(let r)): + return l == r + case (.none, .none): + return true + default: + return false + } +} diff --git a/QuizTrain/Misc/Identity/Identifiable.swift b/QuizTrain/Misc/Identity/Identifiable.swift new file mode 100644 index 0000000..8bc4951 --- /dev/null +++ b/QuizTrain/Misc/Identity/Identifiable.swift @@ -0,0 +1,8 @@ +/* + Provides a way to identify an object. The Id type conforms to Hashable allowing + for comparison and use in sets. + */ +protocol Identifiable { + associatedtype Id: Hashable + var id: Id { get } +} diff --git a/QuizTrain/Misc/JSON/JSONDeserializable.swift b/QuizTrain/Misc/JSON/JSONDeserializable.swift new file mode 100644 index 0000000..8a7b5df --- /dev/null +++ b/QuizTrain/Misc/JSON/JSONDeserializable.swift @@ -0,0 +1,35 @@ +/* + Provides methods to deserialize JSON into an object or objects. + */ +protocol JSONDeserializable { + static func deserialized(_ json: [JSONDictionary]) -> [ObjectType]? + static func deserialized(_ json: JSONDictionary) -> ObjectType? + init?(json: JSONDictionary) +} + +extension JSONDeserializable { + + /* + Initializes and returns multiple objects of ObjectType which conform to + JSONDeserializable. + */ + static func deserialized(_ json: [JSONDictionary]) -> [ObjectType]? { + var objects = [ObjectType]() + for item in json { + guard let object: ObjectType = deserialized(item) else { + return nil + } + objects.append(object) + } + return objects + } + + /* + Initializes and returns a single object of ObjectType which conforms to + JSONDeserializable. + */ + static func deserialized(_ json: JSONDictionary) -> ObjectType? { + return ObjectType(json: json) + } + +} diff --git a/QuizTrain/Misc/JSON/JSONDictionary.swift b/QuizTrain/Misc/JSON/JSONDictionary.swift new file mode 100644 index 0000000..99e190d --- /dev/null +++ b/QuizTrain/Misc/JSON/JSONDictionary.swift @@ -0,0 +1 @@ +public typealias JSONDictionary = [JSONKey: Any] diff --git a/QuizTrain/Misc/JSON/JSONKey.swift b/QuizTrain/Misc/JSON/JSONKey.swift new file mode 100644 index 0000000..13a310f --- /dev/null +++ b/QuizTrain/Misc/JSON/JSONKey.swift @@ -0,0 +1 @@ +public typealias JSONKey = String diff --git a/QuizTrain/Misc/JSON/JSONSerializable.swift b/QuizTrain/Misc/JSON/JSONSerializable.swift new file mode 100644 index 0000000..a1404e9 --- /dev/null +++ b/QuizTrain/Misc/JSON/JSONSerializable.swift @@ -0,0 +1,30 @@ +/* + Provides methods to serialize an object or objects into JSON. + */ +protocol JSONSerializable { + static func serialized(_ objects: [ObjectType]) -> [JSONDictionary] + static func serialized(_ object: ObjectType) -> JSONDictionary + func serialized() -> JSONDictionary +} + +extension JSONSerializable { + + /* + Serializes an array of objects into JSONDictionary's. + */ + static func serialized(_ objects: [ObjectType]) -> [JSONDictionary] { + var jsonArray: [JSONDictionary] = [] + for object in objects { + jsonArray.append(ObjectType.serialized(object)) + } + return jsonArray + } + + /* + Serializes an object into a JSONDictionary. + */ + static func serialized(_ object: ObjectType) -> JSONDictionary { + return object.serialized() + } + +} diff --git a/QuizTrain/Misc/Operations/AsyncOperation.swift b/QuizTrain/Misc/Operations/AsyncOperation.swift new file mode 100644 index 0000000..b501e43 --- /dev/null +++ b/QuizTrain/Misc/Operations/AsyncOperation.swift @@ -0,0 +1,38 @@ +import Foundation + +/* + Asynchronous subclass of Operation providing state tracking with KVO. Subclass + this class to implement custom asynchronous operations. + */ +class AsyncOperation: Operation { + + enum State: String { + case ready = "isReady" + case executing = "isExecuting" + case finished = "isFinished" + } + + private var _state = State.ready + private let stateLock = NSLock() // atomic lock + var state: State { + get { + stateLock.lock() + let value = _state + stateLock.unlock() + return value + } + set { + willChangeValue(forKey: newValue.rawValue) + stateLock.lock() + _state = newValue + stateLock.unlock() + didChangeValue(forKey: newValue.rawValue) + } + } + + override var isAsynchronous: Bool { return true } + override var isReady: Bool { return state == .ready } + override var isExecuting: Bool { return state == .executing } + override var isFinished: Bool { return state == .finished } + +} diff --git a/QuizTrain/Misc/Outcome.swift b/QuizTrain/Misc/Outcome.swift new file mode 100644 index 0000000..36b0db8 --- /dev/null +++ b/QuizTrain/Misc/Outcome.swift @@ -0,0 +1,9 @@ +/* + Container for outcomes. For a .succeeded outcome which does not pass an object + ensure the Succeeded generic is an optional (e.g. Void?) and use + Outcome.succeeded(nil). + */ +public enum Outcome { + case succeeded(Succeeded) + case failed(Failed) +} diff --git a/QuizTrain/Misc/QueryItemProvider.swift b/QuizTrain/Misc/QueryItemProvider.swift new file mode 100644 index 0000000..2f2a907 --- /dev/null +++ b/QuizTrain/Misc/QueryItemProvider.swift @@ -0,0 +1,21 @@ +/* + Conforming objects can return a URLQueryItem representation of themselves. + */ +public protocol QueryItemProvider { + var queryItem: URLQueryItem { get } + static func queryItems(for providers: [QueryItemProvider]?) -> [URLQueryItem] +} + +extension QueryItemProvider { + + public static func queryItems(for providers: [QueryItemProvider]?) -> [URLQueryItem] { + var queryItems = [URLQueryItem]() + if let providers = providers { + for provider in providers { + queryItems.append(provider.queryItem) + } + } + return queryItems + } + +} diff --git a/QuizTrain/Misc/UniqueSelection.swift b/QuizTrain/Misc/UniqueSelection.swift new file mode 100644 index 0000000..1fd1aa4 --- /dev/null +++ b/QuizTrain/Misc/UniqueSelection.swift @@ -0,0 +1,36 @@ +/* + Provides unique selection state. + */ +enum UniqueSelection { + case all // Include everything. + case some(Set) // Include only what's specified. + case none // Include nothing. +} + +extension UniqueSelection: Equatable { + + public static func==(lhs: UniqueSelection, rhs: UniqueSelection) -> Bool { + switch lhs { + case .all: + switch rhs { + case .all: + return true + default: + return false + } + case .some(let lhsProjectIds): + guard case let .some(rhsProjectIds) = rhs else { + return false + } + return lhsProjectIds == rhsProjectIds + case .none: + switch rhs { + case .none: + return true + default: + return false + } + } + } + +} diff --git a/QuizTrain/Misc/Update/UpdateRequestJSON.swift b/QuizTrain/Misc/Update/UpdateRequestJSON.swift new file mode 100644 index 0000000..a1c056b --- /dev/null +++ b/QuizTrain/Misc/Update/UpdateRequestJSON.swift @@ -0,0 +1,16 @@ +/* + Returns JSON for an update request. + */ +protocol UpdateRequestJSON { + var updateRequestJSON: JSONDictionary { get } +} + +extension UpdateRequestJSON where Self: JSONSerializable & UpdateRequestJSONKeys { + + var updateRequestJSON: JSONDictionary { + var json = serialized() + json = json.filter { pair in updateRequestJSONKeys.contains(pair.key) } + return json + } + +} diff --git a/QuizTrain/Misc/Update/UpdateRequestJSONKeys.swift b/QuizTrain/Misc/Update/UpdateRequestJSONKeys.swift new file mode 100644 index 0000000..d8d19df --- /dev/null +++ b/QuizTrain/Misc/Update/UpdateRequestJSONKeys.swift @@ -0,0 +1,7 @@ +/* + Provides JSONKey's for all properties which can be submitted in an update + request to the API. + */ +protocol UpdateRequestJSONKeys { + var updateRequestJSONKeys: [JSONKey] { get } +} diff --git a/QuizTrain/Misc/Validation/Validatable.swift b/QuizTrain/Misc/Validation/Validatable.swift new file mode 100644 index 0000000..3bf9dc5 --- /dev/null +++ b/QuizTrain/Misc/Validation/Validatable.swift @@ -0,0 +1,7 @@ +/* + Provides a way to determine if something is in a valid state. It is up to the + conformer to determine what is valid or not. + */ +protocol Validatable { + var isValid: Bool { get } +} diff --git a/QuizTrain/Models/Case.swift b/QuizTrain/Models/Case.swift new file mode 100644 index 0000000..040f319 --- /dev/null +++ b/QuizTrain/Models/Case.swift @@ -0,0 +1,188 @@ +public struct Case: Identifiable, MutableCustomFields { + public typealias Id = Int + public let createdBy: User.Id + public let createdOn: Date + public var estimate: String? + public let estimateForecast: String? + public let id: Id + public var milestoneId: Milestone.Id? + public var priorityId: Priority.Id + public var refs: String? + public let sectionId: Section.Id? + public let suiteId: Suite.Id? + public var templateId: Template.Id + public var title: String + public var typeId: CaseType.Id + public let updatedBy: User.Id + public let updatedOn: Date + var customFieldsContainer: CustomFieldsContainer +} + +// MARK: - Foward Relationships (ObjectAPI) + +extension Case { + + public func createdBy(_ objectAPI: ObjectAPI, completionHandler: @escaping (Outcome) -> Void) { + objectAPI.createdBy(self, completionHandler: completionHandler) + } + + public func milestone(_ objectAPI: ObjectAPI, completionHandler: @escaping (Outcome) -> Void) { + objectAPI.milestone(self, completionHandler: completionHandler) + } + + public func priority(_ objectAPI: ObjectAPI, completionHandler: @escaping (Outcome, ObjectAPI.GetError>>) -> Void) { + objectAPI.priority(self, completionHandler: completionHandler) + } + + public func section(_ objectAPI: ObjectAPI, completionHandler: @escaping (Outcome) -> Void) { + objectAPI.section(self, completionHandler: completionHandler) + } + + public func suite(_ objectAPI: ObjectAPI, completionHandler: @escaping (Outcome) -> Void) { + objectAPI.suite(self, completionHandler: completionHandler) + } + + public func template(_ objectAPI: ObjectAPI, completionHandler: @escaping (Outcome, ErrorContainer>>) -> Void) { + objectAPI.template(self, completionHandler: completionHandler) + } + + public func type(_ objectAPI: ObjectAPI, completionHandler: @escaping (Outcome, ObjectAPI.GetError>>) -> Void) { + objectAPI.type(self, completionHandler: completionHandler) + } + + public func updatedBy(_ objectAPI: ObjectAPI, completionHandler: @escaping (Outcome) -> Void) { + objectAPI.updatedBy(self, completionHandler: completionHandler) + } + +} + +// MARK: - Equatable + +extension Case: Equatable { + + public static func==(lhs: Case, rhs: Case) -> Bool { + return (lhs.createdBy == rhs.createdBy && + lhs.createdOn.secondsSince1970 == rhs.createdOn.secondsSince1970 && + lhs.estimate == rhs.estimate && + lhs.estimateForecast == rhs.estimateForecast && + lhs.id == rhs.id && + lhs.milestoneId == rhs.milestoneId && + lhs.priorityId == rhs.priorityId && + lhs.refs == rhs.refs && + lhs.sectionId == rhs.sectionId && + lhs.suiteId == rhs.suiteId && + lhs.templateId == rhs.templateId && + lhs.title == rhs.title && + lhs.typeId == rhs.typeId && + lhs.updatedBy == rhs.updatedBy && + lhs.updatedOn.secondsSince1970 == rhs.updatedOn.secondsSince1970 && + lhs.customFieldsContainer == rhs.customFieldsContainer) + } + +} + +// MARK: - JSON Keys + +extension Case { + + enum JSONKeys: JSONKey { + case createdBy = "created_by" + case createdOn = "created_on" + case estimate + case estimateForecast = "estimate_forecast" + case id + case milestoneId = "milestone_id" + case priorityId = "priority_id" + case refs + case sectionId = "section_id" + case suiteId = "suite_id" + case templateId = "template_id" + case title + case typeId = "type_id" + case updatedBy = "updated_by" + case updatedOn = "updated_on" + } + +} + +extension Case: UpdateRequestJSONKeys { + + var updateRequestJSONKeys: [JSONKey] { + var keys = [ + JSONKeys.estimate.rawValue, + JSONKeys.milestoneId.rawValue, + JSONKeys.priorityId.rawValue, + JSONKeys.refs.rawValue, + JSONKeys.templateId.rawValue, + JSONKeys.title.rawValue, + JSONKeys.typeId.rawValue + ] + keys += self.customFieldsContainer.customFields.keys + return keys + } + +} + +// MARK: - Serialization + +extension Case: JSONDeserializable { + + init?(json: JSONDictionary) { + + guard let createdBy = json[JSONKeys.createdBy.rawValue] as? User.Id, + let createdOnSeconds = json[JSONKeys.createdOn.rawValue] as? Int, + let id = json[JSONKeys.id.rawValue] as? Id, + let priorityId = json[JSONKeys.priorityId.rawValue] as? Priority.Id, + let templateId = json[JSONKeys.templateId.rawValue] as? Template.Id, + let title = json[JSONKeys.title.rawValue] as? String, + let typeId = json[JSONKeys.typeId.rawValue] as? CaseType.Id, + let updatedBy = json[JSONKeys.updatedBy.rawValue] as? User.Id, + let updatedOnSeconds = json[JSONKeys.updatedOn.rawValue] as? Int else { + return nil + } + let createdOn = Date(secondsSince1970: createdOnSeconds) + let updatedOn = Date(secondsSince1970: updatedOnSeconds) + + let estimate = json[JSONKeys.estimate.rawValue] as? String ?? nil + let estimateForecast = json[JSONKeys.estimateForecast.rawValue] as? String ?? nil + let milestoneId = json[JSONKeys.milestoneId.rawValue] as? Milestone.Id ?? nil + let refs = json[JSONKeys.refs.rawValue] as? String ?? nil + let sectionId = json[JSONKeys.sectionId.rawValue] as? Section.Id ?? nil + let suiteId = json[JSONKeys.suiteId.rawValue] as? Suite.Id ?? nil + + let customFieldsContainer = CustomFieldsContainer(json: json) + + self.init(createdBy: createdBy, createdOn: createdOn, estimate: estimate, estimateForecast: estimateForecast, id: id, milestoneId: milestoneId, priorityId: priorityId, refs: refs, sectionId: sectionId, suiteId: suiteId, templateId: templateId, title: title, typeId: typeId, updatedBy: updatedBy, updatedOn: updatedOn, customFieldsContainer: customFieldsContainer) + } + +} + +extension Case: JSONSerializable { + + private var serializedProperties: JSONDictionary { + return [JSONKeys.createdBy.rawValue: createdBy, + JSONKeys.createdOn.rawValue: createdOn.secondsSince1970, + JSONKeys.estimate.rawValue: estimate as Any, + JSONKeys.estimateForecast.rawValue: estimateForecast as Any, + JSONKeys.id.rawValue: id, + JSONKeys.milestoneId.rawValue: milestoneId as Any, + JSONKeys.priorityId.rawValue: priorityId, + JSONKeys.refs.rawValue: refs as Any, + JSONKeys.templateId.rawValue: templateId, + JSONKeys.title.rawValue: title, + JSONKeys.typeId.rawValue: typeId, + JSONKeys.sectionId.rawValue: sectionId as Any, + JSONKeys.suiteId.rawValue: suiteId as Any, + JSONKeys.updatedBy.rawValue: updatedBy, + JSONKeys.updatedOn.rawValue: updatedOn.secondsSince1970] + } + + func serialized() -> JSONDictionary { + var json = serializedProperties + customFields.forEach { item in json[item.key] = item.value } + return json + } + +} + +extension Case: UpdateRequestJSON { } diff --git a/QuizTrain/Models/CaseField.swift b/QuizTrain/Models/CaseField.swift new file mode 100644 index 0000000..871e26c --- /dev/null +++ b/QuizTrain/Models/CaseField.swift @@ -0,0 +1,113 @@ +public struct CaseField: Identifiable { + public typealias Id = Int + public let configs: [Config] + public let description: String? + public let displayOrder: Int + public let id: Id + public let includeAll: Bool + public let isActive: Bool + public let label: String + public let name: String + public let systemName: String + public let templateIds: [Template.Id] + public let typeId: CustomFieldType +} + +// MARK: - Foward Relationships (ObjectAPI) + +extension CaseField { + + public func templates(_ objectAPI: ObjectAPI, completionHandler: @escaping (Outcome<[Template], ObjectAPI.MatchError, ErrorContainer>>) -> Void) { + objectAPI.templates(self, completionHandler: completionHandler) + } + +} + +// MARK: - Equatable + +extension CaseField: Equatable { + + public static func==(lhs: CaseField, rhs: CaseField) -> Bool { + return (lhs.configs.contentsAreEqual(to: rhs.configs) && + lhs.description == rhs.description && + lhs.displayOrder == rhs.displayOrder && + lhs.id == rhs.id && + lhs.includeAll == rhs.includeAll && + lhs.isActive == rhs.isActive && + lhs.label == rhs.label && + lhs.name == rhs.name && + lhs.systemName == rhs.systemName && + lhs.templateIds.sorted() == rhs.templateIds.sorted() && + lhs.typeId == rhs.typeId) + } + +} + +// MARK: - JSON Keys + +extension CaseField { + + enum JSONKeys: JSONKey { + case configs + case description + case displayOrder = "display_order" + case id + case includeAll = "include_all" + case isActive = "is_active" + case label + case name + case systemName = "system_name" + case templateIds = "template_ids" + case typeId = "type_id" + } + +} + +// MARK: - Serialization + +extension CaseField: JSONDeserializable { + + init?(json: JSONDictionary) { + + guard let configsJson = json[JSONKeys.configs.rawValue] as? [JSONDictionary], + let configs: [Config] = CaseField.deserialized(configsJson), + let displayOrder = json[JSONKeys.displayOrder.rawValue] as? Int, + let id = json[JSONKeys.id.rawValue] as? Id, + let includeAll = json[JSONKeys.includeAll.rawValue] as? Bool, + let isActive = json[JSONKeys.isActive.rawValue] as? Bool, + let label = json[JSONKeys.label.rawValue] as? String, + let name = json[JSONKeys.name.rawValue] as? String, + let systemName = json[JSONKeys.systemName.rawValue] as? String, + let templateIds = json[JSONKeys.templateIds.rawValue] as? [Template.Id], + let typeIdInt = json[JSONKeys.typeId.rawValue] as? Int, + let typeId = CustomFieldType(rawValue: typeIdInt) else { + return nil + } + + let description = json[JSONKeys.description.rawValue] as? String ?? nil + + self.init(configs: configs, description: description, displayOrder: displayOrder, id: id, includeAll: includeAll, isActive: isActive, label: label, name: name, systemName: systemName, templateIds: templateIds, typeId: typeId) + } + +} + +extension CaseField: JSONSerializable { + + func serialized() -> JSONDictionary { + + let configsSerialized: [JSONDictionary] = Config.serialized(configs) + + return [JSONKeys.configs.rawValue: configsSerialized, + JSONKeys.description.rawValue: description as Any, + JSONKeys.displayOrder.rawValue: displayOrder, + JSONKeys.id.rawValue: id, + JSONKeys.includeAll.rawValue: includeAll, + JSONKeys.isActive.rawValue: isActive, + JSONKeys.label.rawValue: label, + JSONKeys.name.rawValue: name, + JSONKeys.systemName.rawValue: systemName, + JSONKeys.templateIds.rawValue: templateIds, + JSONKeys.typeId.rawValue: typeId.rawValue] + } + +} diff --git a/QuizTrain/Models/CaseType.swift b/QuizTrain/Models/CaseType.swift new file mode 100644 index 0000000..572080b --- /dev/null +++ b/QuizTrain/Models/CaseType.swift @@ -0,0 +1,57 @@ +public struct CaseType: Identifiable { + public typealias Id = Int + public let id: Id + public let isDefault: Bool + public let name: String +} + +// MARK: - Equatable + +extension CaseType: Equatable { + + public static func==(lhs: CaseType, rhs: CaseType) -> Bool { + return (lhs.id == rhs.id && + lhs.isDefault == rhs.isDefault && + lhs.name == rhs.name) + } + +} + +// MARK: - JSON Keys + +extension CaseType { + + enum JSONKeys: JSONKey { + case id + case isDefault = "is_default" + case name + } + +} + +// MARK: - Serialization + +extension CaseType: JSONDeserializable { + + init?(json: JSONDictionary) { + + guard let id = json[JSONKeys.id.rawValue] as? Id, + let isDefault = json[JSONKeys.isDefault.rawValue] as? Bool, + let name = json[JSONKeys.name.rawValue] as? String else { + return nil + } + + self.init(id: id, isDefault: isDefault, name: name) + } + +} + +extension CaseType: JSONSerializable { + + func serialized() -> JSONDictionary { + return [JSONKeys.id.rawValue: id, + JSONKeys.isDefault.rawValue: isDefault, + JSONKeys.name.rawValue: name] + } + +} diff --git a/QuizTrain/Models/Config/Config.Context.swift b/QuizTrain/Models/Config/Config.Context.swift new file mode 100644 index 0000000..89a7e3e --- /dev/null +++ b/QuizTrain/Models/Config/Config.Context.swift @@ -0,0 +1,56 @@ +extension Config { + + public struct Context { + public let isGlobal: Bool // True indicates all projects. + public let projectIds: [Project.Id]? // Applies only if isGlobal is false. Can include projectIds for projects you do not have at least Read-only access to. + } + +} + +// MARK: - Equatable + +extension Config.Context: Equatable { + + public static func==(lhs: Config.Context, rhs: Config.Context) -> Bool { + return (lhs.isGlobal == rhs.isGlobal && + lhs.projectIds?.sorted() == rhs.projectIds?.sorted()) + } + +} + +// MARK: - JSON Keys + +extension Config.Context { + + enum JSONKeys: JSONKey { + case isGlobal = "is_global" + case projectIds = "project_ids" + } + +} + +// MARK: - Serialization + +extension Config.Context: JSONDeserializable { + + init?(json: JSONDictionary) { + + guard let isGlobal = json[JSONKeys.isGlobal.rawValue] as? Bool else { + return nil + } + + let projectIds = json[JSONKeys.projectIds.rawValue] as? [Project.Id] ?? nil + + self.init(isGlobal: isGlobal, projectIds: projectIds) + } + +} + +extension Config.Context: JSONSerializable { + + func serialized() -> JSONDictionary { + return [JSONKeys.isGlobal.rawValue: isGlobal, + JSONKeys.projectIds.rawValue: projectIds as Any] + } + +} diff --git a/QuizTrain/Models/Config/Config.swift b/QuizTrain/Models/Config/Config.swift new file mode 100644 index 0000000..06f46d3 --- /dev/null +++ b/QuizTrain/Models/Config/Config.swift @@ -0,0 +1,91 @@ +public struct Config: Identifiable { + public typealias Id = String + typealias OptionsContainer = JSONDictionaryContainer + public let context: Config.Context + public let id: Id + public var options: [String: Any] { return optionsContainer.json } + let optionsContainer: OptionsContainer +} + +// MARK: - Foward Relationships (ObjectAPI) + +extension Config { + + public func accessibleProjects(_ objectAPI: ObjectAPI, completionHandler: @escaping(Outcome<[Project]?, ErrorContainer>) -> Void) { + objectAPI.accessibleProjects(self, completionHandler: completionHandler) + } + + public func projects(_ objectAPI: ObjectAPI, completionHandler: @escaping(Outcome<[Project]?, ObjectAPI.MatchError, ErrorContainer>>) -> Void) { + objectAPI.projects(self, completionHandler: completionHandler) + } + +} + +// MARK: - Equatable + +extension Config: Equatable { + + public static func==(lhs: Config, rhs: Config) -> Bool { + return (lhs.context == rhs.context && + lhs.id == rhs.id && + lhs.optionsContainer == rhs.optionsContainer) + } + +} + +// MARK: - JSON Keys + +extension Config { + + enum JSONKeys: JSONKey { + case context + case id + case options + } + +} + +// MARK: - Serialization + +extension Config: JSONDeserializable { + + init?(json: JSONDictionary) { + + guard let contextJson = json[JSONKeys.context.rawValue] as? JSONDictionary, + let context: Config.Context = Config.Context.deserialized(contextJson), + let id = json[JSONKeys.id.rawValue] as? Id, + let options = json[JSONKeys.options.rawValue] as? [String: Any] else { + return nil + } + + let optionsContainer = OptionsContainer(json: options) + + self.init(context: context, id: id, optionsContainer: optionsContainer) + } + +} + +extension Config: JSONSerializable { + + func serialized() -> JSONDictionary { + return [JSONKeys.context.rawValue: context.serialized(), + JSONKeys.id.rawValue: id, + JSONKeys.options.rawValue: options] + } + +} + +// MARK: - ProjectSelection + +extension Config { + + var projects: UniqueSelection { + if context.isGlobal == true { + return .all + } else if let projectIds = context.projectIds { + return .some(Set(projectIds)) + } + return .none + } + +} diff --git a/QuizTrain/Models/Configuration.swift b/QuizTrain/Models/Configuration.swift new file mode 100644 index 0000000..3a10505 --- /dev/null +++ b/QuizTrain/Models/Configuration.swift @@ -0,0 +1,79 @@ +public struct Configuration: Identifiable { + public typealias Id = Int + public let id: Id + public let groupId: ConfigurationGroup.Id + public var name: String +} + +// MARK: - Foward Relationships (ObjectAPI) + +extension Configuration { + + public func configurationGroup(_ objectAPI: ObjectAPI, completionHandler: @escaping (Outcome, ErrorContainer>>) -> Void) { + objectAPI.configurationGroup(self, completionHandler: completionHandler) + } + +} + +// MARK: - Equatable + +extension Configuration: Equatable { + + public static func==(lhs: Configuration, rhs: Configuration) -> Bool { + return (lhs.id == rhs.id && + lhs.groupId == rhs.groupId && + lhs.name == rhs.name) + } + +} + +// MARK: - JSON Keys + +extension Configuration { + + enum JSONKeys: JSONKey { + case id + case groupId = "group_id" + case name + } + +} + +extension Configuration: UpdateRequestJSONKeys { + + var updateRequestJSONKeys: [JSONKey] { + return [ + JSONKeys.name.rawValue + ] + } + +} + +// MARK: - Serialization + +extension Configuration: JSONDeserializable { + + init?(json: JSONDictionary) { + + guard let id = json[JSONKeys.id.rawValue] as? Id, + let groupId = json[JSONKeys.groupId.rawValue] as? ConfigurationGroup.Id, + let name = json[JSONKeys.name.rawValue] as? String else { + return nil + } + + self.init(id: id, groupId: groupId, name: name) + } + +} + +extension Configuration: JSONSerializable { + + func serialized() -> JSONDictionary { + return [JSONKeys.id.rawValue: id, + JSONKeys.groupId.rawValue: groupId, + JSONKeys.name.rawValue: name] + } + +} + +extension Configuration: UpdateRequestJSON { } diff --git a/QuizTrain/Models/ConfigurationGroup.swift b/QuizTrain/Models/ConfigurationGroup.swift new file mode 100644 index 0000000..2e146fa --- /dev/null +++ b/QuizTrain/Models/ConfigurationGroup.swift @@ -0,0 +1,88 @@ +public struct ConfigurationGroup: Identifiable { + public typealias Id = Int + public let configs: [Configuration] + public let id: Id + public var name: String + public let projectId: Project.Id +} + +// MARK: - Foward Relationships (ObjectAPI) + +extension ConfigurationGroup { + + public func project(_ objectAPI: ObjectAPI, completionHandler: @escaping (Outcome) -> Void) { + objectAPI.project(self, completionHandler: completionHandler) + } + +} + +// MARK: - Equatable + +extension ConfigurationGroup: Equatable { + + public static func==(lhs: ConfigurationGroup, rhs: ConfigurationGroup) -> Bool { + return (lhs.configs.contentsAreEqual(to: rhs.configs) && + lhs.id == rhs.id && + lhs.name == rhs.name && + lhs.projectId == rhs.projectId) + } + +} + +// MARK: - JSON Keys + +extension ConfigurationGroup { + + enum JSONKeys: JSONKey { + case configs + case id + case name + case projectId = "project_id" + } + +} + +extension ConfigurationGroup: UpdateRequestJSONKeys { + + var updateRequestJSONKeys: [JSONKey] { + return [ + JSONKeys.name.rawValue + ] + } + +} + +// MARK: - Serialization + +extension ConfigurationGroup: JSONDeserializable { + + init?(json: JSONDictionary) { + + guard let configsJson = json[JSONKeys.configs.rawValue] as? [JSONDictionary], + let configs: [Configuration] = ConfigurationGroup.deserialized(configsJson), + let id = json[JSONKeys.id.rawValue] as? Id, + let name = json[JSONKeys.name.rawValue] as? String, + let projectId = json[JSONKeys.projectId.rawValue] as? Project.Id else { + return nil + } + + self.init(configs: configs, id: id, name: name, projectId: projectId) + } + +} + +extension ConfigurationGroup: JSONSerializable { + + func serialized() -> JSONDictionary { + + let configsSerialized: [JSONDictionary] = Configuration.serialized(configs) + + return [JSONKeys.configs.rawValue: configsSerialized, + JSONKeys.id.rawValue: id, + JSONKeys.name.rawValue: name, + JSONKeys.projectId.rawValue: projectId] + } + +} + +extension ConfigurationGroup: UpdateRequestJSON { } diff --git a/QuizTrain/Models/Milestone.swift b/QuizTrain/Models/Milestone.swift new file mode 100644 index 0000000..f28b025 --- /dev/null +++ b/QuizTrain/Models/Milestone.swift @@ -0,0 +1,178 @@ +public struct Milestone: Identifiable { + public typealias Id = Int + public let completedOn: Date? + public var description: String? + public var dueOn: Date? + public let id: Id + public var isCompleted: Bool + public var isStarted: Bool + public let milestones: [Milestone]? // Certain API calls will always return nil for this value. See TestRail API documentation for details. + public var name: String + public var parentId: Id? + public let projectId: Project.Id + public var startOn: Date? + public let startedOn: Date? + public let url: URL +} + +// MARK: - Foward Relationships (ObjectAPI) + +extension Milestone { + + public func parent(_ objectAPI: ObjectAPI, completionHandler: @escaping (Outcome) -> Void) { + objectAPI.parent(self, completionHandler: completionHandler) + } + + public func project(_ objectAPI: ObjectAPI, completionHandler: @escaping (Outcome) -> Void) { + objectAPI.project(self, completionHandler: completionHandler) + } + +} + +// MARK: - Equatable + +extension Milestone: Equatable { + + public static func==(lhs: Milestone, rhs: Milestone) -> Bool { + return (lhs.completedOn?.secondsSince1970 == rhs.completedOn?.secondsSince1970 && + lhs.description == rhs.description && + lhs.dueOn?.secondsSince1970 == rhs.dueOn?.secondsSince1970 && + lhs.id == rhs.id && + lhs.isCompleted == rhs.isCompleted && + lhs.isStarted == rhs.isStarted && + Array.contentsAreEqual(lhs.milestones, rhs.milestones) && + lhs.name == rhs.name && + lhs.parentId == rhs.parentId && + lhs.projectId == rhs.projectId && + lhs.startOn?.secondsSince1970 == rhs.startOn?.secondsSince1970 && + lhs.startedOn?.secondsSince1970 == rhs.startedOn?.secondsSince1970 && + lhs.url == rhs.url) + } + +} + +// MARK: - JSON Keys + +extension Milestone { + + enum JSONKeys: JSONKey { + case completedOn = "completed_on" + case description + case dueOn = "due_on" + case id + case isCompleted = "is_completed" + case isStarted = "is_started" + case milestones + case name + case parentId = "parent_id" + case projectId = "project_id" + case startOn = "start_on" + case startedOn = "started_on" + case url + } + +} + +extension Milestone: UpdateRequestJSONKeys { + + var updateRequestJSONKeys: [JSONKey] { + return [ + JSONKeys.description.rawValue, + JSONKeys.dueOn.rawValue, + JSONKeys.isCompleted.rawValue, + JSONKeys.isStarted.rawValue, + JSONKeys.name.rawValue, + JSONKeys.parentId.rawValue, + JSONKeys.startOn.rawValue + ] + } + +} + +// MARK: - Serialization + +extension Milestone: JSONDeserializable { + + init?(json: JSONDictionary) { + + guard let id = json[JSONKeys.id.rawValue] as? Id, + let isCompleted = json[JSONKeys.isCompleted.rawValue] as? Bool, + let isStarted = json[JSONKeys.isStarted.rawValue] as? Bool, + let name = json[JSONKeys.name.rawValue] as? String, + let projectId = json[JSONKeys.projectId.rawValue] as? Project.Id, + let urlString = json[JSONKeys.url.rawValue] as? String, + let url = URL(string: urlString) else { + return nil + } + + let completedOn: Date? + if let seconds = json[JSONKeys.completedOn.rawValue] as? Int { + completedOn = Date(secondsSince1970: seconds) + } else { + completedOn = nil + } + + let dueOn: Date? + if let seconds = json[JSONKeys.dueOn.rawValue] as? Int { + dueOn = Date(secondsSince1970: seconds) + } else { + dueOn = nil + } + + let startOn: Date? + if let seconds = json[JSONKeys.startOn.rawValue] as? Int { + startOn = Date(secondsSince1970: seconds) + } else { + startOn = nil + } + + let startedOn: Date? + if let seconds = json[JSONKeys.startedOn.rawValue] as? Int { + startedOn = Date(secondsSince1970: seconds) + } else { + startedOn = nil + } + + let description = json[JSONKeys.description.rawValue] as? String ?? nil + let milestones: [Milestone]? + if let milestonesJson = json[JSONKeys.milestones.rawValue] as? [JSONDictionary] { + milestones = Milestone.deserialized(milestonesJson) + } else { + milestones = nil + } + let parentId = json[JSONKeys.parentId.rawValue] as? Id ?? nil + + self.init(completedOn: completedOn, description: description, dueOn: dueOn, id: id, isCompleted: isCompleted, isStarted: isStarted, milestones: milestones, name: name, parentId: parentId, projectId: projectId, startOn: startOn, startedOn: startedOn, url: url) + } + +} + +extension Milestone: JSONSerializable { + + func serialized() -> JSONDictionary { + + let milestonesSerialized: [JSONDictionary]? + if let milestones = self.milestones { + milestonesSerialized = Milestone.serialized(milestones) + } else { + milestonesSerialized = nil + } + + return [JSONKeys.completedOn.rawValue: completedOn?.secondsSince1970 as Any, + JSONKeys.description.rawValue: description as Any, + JSONKeys.dueOn.rawValue: dueOn?.secondsSince1970 as Any, + JSONKeys.id.rawValue: id, + JSONKeys.isCompleted.rawValue: isCompleted, + JSONKeys.isStarted.rawValue: isStarted, + JSONKeys.milestones.rawValue: milestonesSerialized as Any, + JSONKeys.name.rawValue: name, + JSONKeys.parentId.rawValue: parentId as Any, + JSONKeys.projectId.rawValue: projectId, + JSONKeys.startOn.rawValue: startOn?.secondsSince1970 as Any, + JSONKeys.startedOn.rawValue: startedOn?.secondsSince1970 as Any, + JSONKeys.url.rawValue: url.absoluteString] + } + +} + +extension Milestone: UpdateRequestJSON { } diff --git a/QuizTrain/Models/Plan.Entry.swift b/QuizTrain/Models/Plan.Entry.swift new file mode 100644 index 0000000..8c8a86c --- /dev/null +++ b/QuizTrain/Models/Plan.Entry.swift @@ -0,0 +1,90 @@ +extension Plan { + + public struct Entry: Identifiable { + public typealias Id = String + public let id: Id + public var name: String + public let runs: [Run] // NOTE: Runs can be updated using a PlanEntryRunsData. See ObjectAPI for details. + public let suiteId: Suite.Id + } + +} + +// MARK: - Foward Relationships (ObjectAPI) + +extension Plan.Entry { + + public func suite(_ objectAPI: ObjectAPI, completionHandler: @escaping (Outcome) -> Void) { + objectAPI.suite(self, completionHandler: completionHandler) + } + +} + +// MARK: - Equatable + +extension Plan.Entry: Equatable { + + public static func==(lhs: Plan.Entry, rhs: Plan.Entry) -> Bool { + return (lhs.id == rhs.id && + lhs.name == rhs.name && + lhs.runs.contentsAreEqual(to: rhs.runs) && + lhs.suiteId == rhs.suiteId) + } + +} + +// MARK: - JSON Keys + +extension Plan.Entry { + + enum JSONKeys: JSONKey { + case id + case name + case runs + case suiteId = "suite_id" + } + +} + +extension Plan.Entry: UpdateRequestJSONKeys { + + var updateRequestJSONKeys: [JSONKey] { + return [JSONKeys.name.rawValue] + } + +} + +// MARK: - Serialization + +extension Plan.Entry: JSONDeserializable { + + init?(json: JSONDictionary) { + + guard let id = json[JSONKeys.id.rawValue] as? Id, + let name = json[JSONKeys.name.rawValue] as? String, + let runsJson = json[JSONKeys.runs.rawValue] as? [JSONDictionary], + let runs: [Run] = Run.deserialized(runsJson), + let suiteId = json[JSONKeys.suiteId.rawValue] as? Suite.Id else { + return nil + } + + self.init(id: id, name: name, runs: runs, suiteId: suiteId) + } + +} + +extension Plan.Entry: JSONSerializable { + + func serialized() -> JSONDictionary { + + let runsSerialized: [JSONDictionary] = Run.serialized(runs) + + return [JSONKeys.id.rawValue: id, + JSONKeys.name.rawValue: name, + JSONKeys.runs.rawValue: runsSerialized, + JSONKeys.suiteId.rawValue: suiteId] + } + +} + +extension Plan.Entry: UpdateRequestJSON { } diff --git a/QuizTrain/Models/Plan.swift b/QuizTrain/Models/Plan.swift new file mode 100644 index 0000000..7299a02 --- /dev/null +++ b/QuizTrain/Models/Plan.swift @@ -0,0 +1,216 @@ +public struct Plan: Identifiable { + public typealias Id = Int + public let assignedtoId: User.Id? + public let blockedCount: Int + public let completedOn: Date? + public let createdBy: User.Id + public let createdOn: Date + public let customStatus1Count: Int + public let customStatus2Count: Int + public let customStatus3Count: Int + public let customStatus4Count: Int + public let customStatus5Count: Int + public let customStatus6Count: Int + public let customStatus7Count: Int + public var description: String? + public let entries: [Plan.Entry]? // Certain API calls will always return nil for this value. Others will only return a single entry. See TestRail API documentation for details. + public let failedCount: Int + public let id: Id + public let isCompleted: Bool + public var milestoneId: Milestone.Id? + public var name: String + public let passedCount: Int + public let projectId: Project.Id + public let retestCount: Int + public let untestedCount: Int + public let url: URL +} + +// MARK: - Foward Relationships (ObjectAPI) + +extension Plan { + + public func assignedto(_ objectAPI: ObjectAPI, completionHandler: @escaping (Outcome) -> Void) { + objectAPI.assignedto(self, completionHandler: completionHandler) + } + + public func createdBy(_ objectAPI: ObjectAPI, completionHandler: @escaping (Outcome) -> Void) { + objectAPI.createdBy(self, completionHandler: completionHandler) + } + + public func milestone(_ objectAPI: ObjectAPI, completionHandler: @escaping (Outcome) -> Void) { + objectAPI.milestone(self, completionHandler: completionHandler) + } + + public func project(_ objectAPI: ObjectAPI, completionHandler: @escaping (Outcome) -> Void) { + objectAPI.project(self, completionHandler: completionHandler) + } + +} + +// MARK: - Equatable + +extension Plan: Equatable { + + public static func==(lhs: Plan, rhs: Plan) -> Bool { + return (lhs.assignedtoId == rhs.assignedtoId && + lhs.blockedCount == rhs.blockedCount && + lhs.completedOn?.secondsSince1970 == rhs.completedOn?.secondsSince1970 && + lhs.createdBy == rhs.createdBy && + lhs.createdOn.secondsSince1970 == rhs.createdOn.secondsSince1970 && + lhs.customStatus1Count == rhs.customStatus1Count && + lhs.customStatus2Count == rhs.customStatus2Count && + lhs.customStatus3Count == rhs.customStatus3Count && + lhs.customStatus4Count == rhs.customStatus4Count && + lhs.customStatus5Count == rhs.customStatus5Count && + lhs.customStatus6Count == rhs.customStatus6Count && + lhs.customStatus7Count == rhs.customStatus7Count && + lhs.description == rhs.description && + Array.contentsAreEqual(lhs.entries, rhs.entries) && + lhs.failedCount == rhs.failedCount && + lhs.id == rhs.id && + lhs.isCompleted == rhs.isCompleted && + lhs.milestoneId == rhs.milestoneId && + lhs.name == rhs.name && + lhs.passedCount == rhs.passedCount && + lhs.projectId == rhs.projectId && + lhs.retestCount == rhs.retestCount && + lhs.untestedCount == rhs.untestedCount && + lhs.url == rhs.url) + } + +} + +// MARK: - JSON Keys + +extension Plan { + + enum JSONKeys: JSONKey { + case assignedtoId = "assignedto_id" + case blockedCount = "blocked_count" + case completedOn = "completed_on" + case createdBy = "created_by" + case createdOn = "created_on" + case customStatus1Count = "custom_status1_count" + case customStatus2Count = "custom_status2_count" + case customStatus3Count = "custom_status3_count" + case customStatus4Count = "custom_status4_count" + case customStatus5Count = "custom_status5_count" + case customStatus6Count = "custom_status6_count" + case customStatus7Count = "custom_status7_count" + case description + case entries + case failedCount = "failed_count" + case id + case isCompleted = "is_completed" + case milestoneId = "milestone_id" + case name + case passedCount = "passed_count" + case projectId = "project_id" + case retestCount = "retest_count" + case untestedCount = "untested_count" + case url + } + +} + +extension Plan: UpdateRequestJSONKeys { + + var updateRequestJSONKeys: [JSONKey] { + return [ + JSONKeys.description.rawValue, + JSONKeys.milestoneId.rawValue, + JSONKeys.name.rawValue + ] + } + +} + +// MARK: - Serialization + +extension Plan: JSONDeserializable { + + init?(json: JSONDictionary) { + + guard let blockedCount = json[JSONKeys.blockedCount.rawValue] as? Int, + let createdBy = json[JSONKeys.createdBy.rawValue] as? User.Id, + let createdOnSeconds = json[JSONKeys.createdOn.rawValue] as? Int, + let customStatus1Count = json[JSONKeys.customStatus1Count.rawValue] as? Int, + let customStatus2Count = json[JSONKeys.customStatus2Count.rawValue] as? Int, + let customStatus3Count = json[JSONKeys.customStatus3Count.rawValue] as? Int, + let customStatus4Count = json[JSONKeys.customStatus4Count.rawValue] as? Int, + let customStatus5Count = json[JSONKeys.customStatus5Count.rawValue] as? Int, + let customStatus6Count = json[JSONKeys.customStatus6Count.rawValue] as? Int, + let customStatus7Count = json[JSONKeys.customStatus7Count.rawValue] as? Int, + let failedCount = json[JSONKeys.failedCount.rawValue] as? Int, + let id = json[JSONKeys.id.rawValue] as? Id, + let isCompleted = json[JSONKeys.isCompleted.rawValue] as? Bool, + let name = json[JSONKeys.name.rawValue] as? String, + let passedCount = json[JSONKeys.passedCount.rawValue] as? Int, + let projectId = json[JSONKeys.projectId.rawValue] as? Project.Id, + let retestCount = json[JSONKeys.retestCount.rawValue] as? Int, + let untestedCount = json[JSONKeys.untestedCount.rawValue] as? Int, + let urlString = json[JSONKeys.url.rawValue] as? String, + let url = URL(string: urlString) else { + return nil + } + let createdOn = Date(secondsSince1970: createdOnSeconds) + + let completedOn: Date? + if let seconds = json[JSONKeys.completedOn.rawValue] as? Int { + completedOn = Date(secondsSince1970: seconds) + } else { + completedOn = nil + } + + let assignedtoId = json[JSONKeys.assignedtoId.rawValue] as? User.Id ?? nil + let description = json[JSONKeys.description.rawValue] as? String ?? nil + let milestoneId = json[JSONKeys.milestoneId.rawValue] as? Milestone.Id ?? nil + let entriesJson = json[JSONKeys.entries.rawValue] as? [JSONDictionary] ?? nil + let entries: [Plan.Entry]? = entriesJson != nil ? Plan.Entry.deserialized(entriesJson!) : nil + + self.init(assignedtoId: assignedtoId, blockedCount: blockedCount, completedOn: completedOn, createdBy: createdBy, createdOn: createdOn, customStatus1Count: customStatus1Count, customStatus2Count: customStatus2Count, customStatus3Count: customStatus3Count, customStatus4Count: customStatus4Count, customStatus5Count: customStatus5Count, customStatus6Count: customStatus6Count, customStatus7Count: customStatus7Count, description: description, entries: entries, failedCount: failedCount, id: id, isCompleted: isCompleted, milestoneId: milestoneId, name: name, passedCount: passedCount, projectId: projectId, retestCount: retestCount, untestedCount: untestedCount, url: url) + } + +} + +extension Plan: JSONSerializable { + + func serialized() -> JSONDictionary { + + let entriesSerialized: [JSONDictionary]? + if let entries = self.entries { + entriesSerialized = Plan.Entry.serialized(entries) + } else { + entriesSerialized = nil + } + + return [JSONKeys.assignedtoId.rawValue: assignedtoId as Any, + JSONKeys.blockedCount.rawValue: blockedCount, + JSONKeys.completedOn.rawValue: completedOn?.secondsSince1970 as Any, + JSONKeys.createdBy.rawValue: createdBy, + JSONKeys.createdOn.rawValue: createdOn.secondsSince1970, + JSONKeys.customStatus1Count.rawValue: customStatus1Count, + JSONKeys.customStatus2Count.rawValue: customStatus2Count, + JSONKeys.customStatus3Count.rawValue: customStatus3Count, + JSONKeys.customStatus4Count.rawValue: customStatus4Count, + JSONKeys.customStatus5Count.rawValue: customStatus5Count, + JSONKeys.customStatus6Count.rawValue: customStatus6Count, + JSONKeys.customStatus7Count.rawValue: customStatus7Count, + JSONKeys.description.rawValue: description as Any, + JSONKeys.entries.rawValue: entriesSerialized as Any, + JSONKeys.failedCount.rawValue: failedCount, + JSONKeys.id.rawValue: id, + JSONKeys.isCompleted.rawValue: isCompleted, + JSONKeys.milestoneId.rawValue: milestoneId as Any, + JSONKeys.name.rawValue: name, + JSONKeys.passedCount.rawValue: passedCount, + JSONKeys.projectId.rawValue: projectId, + JSONKeys.retestCount.rawValue: retestCount, + JSONKeys.untestedCount.rawValue: untestedCount, + JSONKeys.url.rawValue: url.absoluteString] + } + +} + +extension Plan: UpdateRequestJSON { } diff --git a/QuizTrain/Models/Priority.swift b/QuizTrain/Models/Priority.swift new file mode 100644 index 0000000..a720740 --- /dev/null +++ b/QuizTrain/Models/Priority.swift @@ -0,0 +1,67 @@ +public struct Priority: Identifiable { + public typealias Id = Int + public let id: Id + public let isDefault: Bool + public let name: String + public let priority: Int + public let shortName: String +} + +// MARK: - Equatable + +extension Priority: Equatable { + + public static func==(lhs: Priority, rhs: Priority) -> Bool { + return (lhs.id == rhs.id && + lhs.isDefault == rhs.isDefault && + lhs.name == rhs.name && + lhs.priority == rhs.priority && + lhs.shortName == rhs.shortName) + } + +} + +// MARK: - JSON Keys + +extension Priority { + + enum JSONKeys: JSONKey { + case id + case isDefault = "is_default" + case name + case priority + case shortName = "short_name" + } + +} + +// MARK: - Serialization + +extension Priority: JSONDeserializable { + + init?(json: JSONDictionary) { + + guard let id = json[JSONKeys.id.rawValue] as? Id, + let isDefault = json[JSONKeys.isDefault.rawValue] as? Bool, + let name = json[JSONKeys.name.rawValue] as? String, + let priority = json[JSONKeys.priority.rawValue] as? Int, + let shortName = json[JSONKeys.shortName.rawValue] as? String else { + return nil + } + + self.init(id: id, isDefault: isDefault, name: name, priority: priority, shortName: shortName) + } + +} + +extension Priority: JSONSerializable { + + func serialized() -> JSONDictionary { + return [JSONKeys.id.rawValue: id, + JSONKeys.isDefault.rawValue: isDefault, + JSONKeys.name.rawValue: name, + JSONKeys.priority.rawValue: priority, + JSONKeys.shortName.rawValue: shortName] + } + +} diff --git a/QuizTrain/Models/Project.swift b/QuizTrain/Models/Project.swift new file mode 100644 index 0000000..039678d --- /dev/null +++ b/QuizTrain/Models/Project.swift @@ -0,0 +1,107 @@ +public struct Project: Identifiable { + public typealias Id = Int + public var announcement: String? + public let completedOn: Date? + public let id: Id + public var isCompleted: Bool + public var name: String + public var showAnnouncement: Bool + public var suiteMode: Project.SuiteMode + public let url: URL +} + +// MARK: - Equatable + +extension Project: Equatable { + + public static func==(lhs: Project, rhs: Project) -> Bool { + return (lhs.announcement == rhs.announcement && + lhs.completedOn?.secondsSince1970 == rhs.completedOn?.secondsSince1970 && + lhs.id == rhs.id && + lhs.isCompleted == rhs.isCompleted && + lhs.name == rhs.name && + lhs.showAnnouncement == rhs.showAnnouncement && + lhs.suiteMode == rhs.suiteMode && + lhs.url == rhs.url) + } + +} + +// MARK: - JSON Keys + +extension Project { + + enum JSONKeys: JSONKey { + case announcement + case completedOn = "completed_on" + case id + case isCompleted = "is_completed" + case name + case showAnnouncement = "show_announcement" + case suiteMode = "suite_mode" + case url + } + +} + +extension Project: UpdateRequestJSONKeys { + + var updateRequestJSONKeys: [JSONKey] { + return [ + JSONKeys.announcement.rawValue, + JSONKeys.isCompleted.rawValue, + JSONKeys.name.rawValue, + JSONKeys.showAnnouncement.rawValue, + JSONKeys.suiteMode.rawValue + ] + } + +} + +// MARK: - Serialization + +extension Project: JSONDeserializable { + + init?(json: JSONDictionary) { + + guard let id = json[JSONKeys.id.rawValue] as? Id, + let isCompleted = json[JSONKeys.isCompleted.rawValue] as? Bool, + let name = json[JSONKeys.name.rawValue] as? String, + let showAnnouncement = json[JSONKeys.showAnnouncement.rawValue] as? Bool, + let suiteModeInt = json[JSONKeys.suiteMode.rawValue] as? Int, + let suiteMode = Project.SuiteMode(rawValue: suiteModeInt), + let urlString = json[JSONKeys.url.rawValue] as? String, + let url = URL(string: urlString) else { + return nil + } + + let announcement = json[JSONKeys.announcement.rawValue] as? String ?? nil + + let completedOn: Date? + if let seconds = json[JSONKeys.completedOn.rawValue] as? Int { + completedOn = Date(secondsSince1970: seconds) + } else { + completedOn = nil + } + + self.init(announcement: announcement, completedOn: completedOn, id: id, isCompleted: isCompleted, name: name, showAnnouncement: showAnnouncement, suiteMode: suiteMode, url: url) + } + +} + +extension Project: JSONSerializable { + + func serialized() -> JSONDictionary { + return [JSONKeys.announcement.rawValue: announcement as Any, + JSONKeys.completedOn.rawValue: completedOn?.secondsSince1970 as Any, + JSONKeys.id.rawValue: id, + JSONKeys.isCompleted.rawValue: isCompleted, + JSONKeys.name.rawValue: name, + JSONKeys.showAnnouncement.rawValue: showAnnouncement, + JSONKeys.suiteMode.rawValue: suiteMode.rawValue, + JSONKeys.url.rawValue: url.absoluteString] + } + +} + +extension Project: UpdateRequestJSON { } diff --git a/QuizTrain/Models/Result.swift b/QuizTrain/Models/Result.swift new file mode 100644 index 0000000..c634835 --- /dev/null +++ b/QuizTrain/Models/Result.swift @@ -0,0 +1,126 @@ +public struct Result: CustomFields, Identifiable { + public typealias Id = Int + public let assignedtoId: User.Id? + public let comment: String? + public let createdBy: User.Id + public let createdOn: Date + public let defects: String? + public let elapsed: String? + public let id: Id + public let statusId: Status.Id? + public let testId: Test.Id + public let version: String? + let customFieldsContainer: CustomFieldsContainer +} + +// MARK: - Foward Relationships (ObjectAPI) + +extension Result { + + public func assignedto(_ objectAPI: ObjectAPI, completionHandler: @escaping (Outcome) -> Void) { + objectAPI.assignedto(self, completionHandler: completionHandler) + } + + public func createdBy(_ objectAPI: ObjectAPI, completionHandler: @escaping (Outcome) -> Void) { + objectAPI.createdBy(self, completionHandler: completionHandler) + } + + public func status(_ objectAPI: ObjectAPI, completionHandler: @escaping (Outcome, ObjectAPI.GetError>>) -> Void) { + objectAPI.status(self, completionHandler: completionHandler) + } + + public func test(_ objectAPI: ObjectAPI, completionHandler: @escaping (Outcome) -> Void) { + objectAPI.test(self, completionHandler: completionHandler) + } + +} + +// MARK: - Equatable + +extension Result: Equatable { + + public static func==(lhs: Result, rhs: Result) -> Bool { + return (lhs.assignedtoId == rhs.assignedtoId && + lhs.comment == rhs.comment && + lhs.createdBy == rhs.createdBy && + lhs.createdOn.secondsSince1970 == rhs.createdOn.secondsSince1970 && + lhs.defects == rhs.defects && + lhs.elapsed == rhs.elapsed && + lhs.id == rhs.id && + lhs.statusId == rhs.statusId && + lhs.testId == rhs.testId && + lhs.version == rhs.version && + lhs.customFieldsContainer == rhs.customFieldsContainer) + } + +} + +// MARK: - JSON Keys + +extension Result { + + enum JSONKeys: JSONKey { + case assignedtoId = "assignedto_id" + case comment + case createdBy = "created_by" + case createdOn = "created_on" + case defects + case elapsed + case id + case statusId = "status_id" + case testId = "test_id" + case version + } + +} + +// MARK: - Serialization + +extension Result: JSONDeserializable { + + init?(json: JSONDictionary) { + + guard let createdBy = json[JSONKeys.createdBy.rawValue] as? User.Id, + let createdOnSeconds = json[JSONKeys.createdOn.rawValue] as? Int, + let id = json[JSONKeys.id.rawValue] as? Id, + let testId = json[JSONKeys.testId.rawValue] as? Test.Id else { + return nil + } + let createdOn = Date(secondsSince1970: createdOnSeconds) + + let assignedtoId = json[JSONKeys.assignedtoId.rawValue] as? User.Id ?? nil + let comment = json[JSONKeys.comment.rawValue] as? String ?? nil + let defects = json[JSONKeys.defects.rawValue] as? String ?? nil + let elapsed = json[JSONKeys.elapsed.rawValue] as? String ?? nil + let statusId = json[JSONKeys.statusId.rawValue] as? Status.Id ?? nil + let version = json[JSONKeys.version.rawValue] as? String ?? nil + + let customFieldsContainer = CustomFieldsContainer(json: json) + + self.init(assignedtoId: assignedtoId, comment: comment, createdBy: createdBy, createdOn: createdOn, defects: defects, elapsed: elapsed, id: id, statusId: statusId, testId: testId, version: version, customFieldsContainer: customFieldsContainer) + } + +} + +extension Result: JSONSerializable { + + private var serializedProperties: JSONDictionary { + return [JSONKeys.assignedtoId.rawValue: assignedtoId as Any, + JSONKeys.comment.rawValue: comment as Any, + JSONKeys.createdBy.rawValue: createdBy, + JSONKeys.createdOn.rawValue: createdOn.secondsSince1970, + JSONKeys.defects.rawValue: defects as Any, + JSONKeys.elapsed.rawValue: elapsed as Any, + JSONKeys.id.rawValue: id, + JSONKeys.statusId.rawValue: statusId as Any, + JSONKeys.testId.rawValue: testId, + JSONKeys.version.rawValue: version as Any] + } + + func serialized() -> JSONDictionary { + var json = serializedProperties + customFields.forEach { item in json[item.key] = item.value } + return json + } + +} diff --git a/QuizTrain/Models/ResultField.swift b/QuizTrain/Models/ResultField.swift new file mode 100644 index 0000000..22b5e48 --- /dev/null +++ b/QuizTrain/Models/ResultField.swift @@ -0,0 +1,113 @@ +public struct ResultField: Identifiable { + public typealias Id = Int + public let configs: [Config] + public let description: String? + public let displayOrder: Int + public let id: Id + public let includeAll: Bool + public let isActive: Bool + public let label: String + public let name: String + public let systemName: String + public let templateIds: [Template.Id] + public let typeId: CustomFieldType +} + +// MARK: - Foward Relationships (ObjectAPI) + +extension ResultField { + + public func templates(_ objectAPI: ObjectAPI, completionHandler: @escaping (Outcome<[Template], ObjectAPI.MatchError, ErrorContainer>>) -> Void) { + objectAPI.templates(self, completionHandler: completionHandler) + } + +} + +// MARK: - Equatable + +extension ResultField: Equatable { + + public static func==(lhs: ResultField, rhs: ResultField) -> Bool { + return (lhs.configs.contentsAreEqual(to: rhs.configs) && + lhs.description == rhs.description && + lhs.displayOrder == rhs.displayOrder && + lhs.id == rhs.id && + lhs.includeAll == rhs.includeAll && + lhs.isActive == rhs.isActive && + lhs.label == rhs.label && + lhs.name == rhs.name && + lhs.systemName == rhs.systemName && + lhs.templateIds.sorted() == rhs.templateIds.sorted() && + lhs.typeId == rhs.typeId) + } + +} + +// MARK: - JSON Keys + +extension ResultField { + + enum JSONKeys: JSONKey { + case configs + case description + case displayOrder = "display_order" + case id + case includeAll = "include_all" + case isActive = "is_active" + case label + case name + case systemName = "system_name" + case templateIds = "template_ids" + case typeId = "type_id" + } + +} + +// MARK: - Serialization + +extension ResultField: JSONDeserializable { + + init?(json: JSONDictionary) { + + guard let configsJson = json[JSONKeys.configs.rawValue] as? [JSONDictionary], + let configs: [Config] = ResultField.deserialized(configsJson), + let displayOrder = json[JSONKeys.displayOrder.rawValue] as? Int, + let id = json[JSONKeys.id.rawValue] as? Id, + let includeAll = json[JSONKeys.includeAll.rawValue] as? Bool, + let isActive = json[JSONKeys.isActive.rawValue] as? Bool, + let label = json[JSONKeys.label.rawValue] as? String, + let name = json[JSONKeys.name.rawValue] as? String, + let systemName = json[JSONKeys.systemName.rawValue] as? String, + let templateIds = json[JSONKeys.templateIds.rawValue] as? [Template.Id], + let typeIdInt = json[JSONKeys.typeId.rawValue] as? Int, + let typeId = CustomFieldType(rawValue: typeIdInt) else { + return nil + } + + let description = json[JSONKeys.description.rawValue] as? String ?? nil + + self.init(configs: configs, description: description, displayOrder: displayOrder, id: id, includeAll: includeAll, isActive: isActive, label: label, name: name, systemName: systemName, templateIds: templateIds, typeId: typeId) + } + +} + +extension ResultField: JSONSerializable { + + func serialized() -> JSONDictionary { + + let configsSerialized: [JSONDictionary] = Config.serialized(configs) + + return [JSONKeys.configs.rawValue: configsSerialized, + JSONKeys.description.rawValue: description as Any, + JSONKeys.displayOrder.rawValue: displayOrder, + JSONKeys.id.rawValue: id, + JSONKeys.includeAll.rawValue: includeAll, + JSONKeys.isActive.rawValue: isActive, + JSONKeys.label.rawValue: label, + JSONKeys.name.rawValue: name, + JSONKeys.systemName.rawValue: systemName, + JSONKeys.templateIds.rawValue: templateIds, + JSONKeys.typeId.rawValue: typeId.rawValue] + } + +} diff --git a/QuizTrain/Models/Run.swift b/QuizTrain/Models/Run.swift new file mode 100644 index 0000000..8c7e99b --- /dev/null +++ b/QuizTrain/Models/Run.swift @@ -0,0 +1,240 @@ +public struct Run: Identifiable { + public typealias Id = Int + public let assignedtoId: User.Id? + public let blockedCount: Int + public let completedOn: Date? + public let config: String? + public let configIds: [Configuration.Id]? + public let createdBy: User.Id + public let createdOn: Date + public let customStatus1Count: Int + public let customStatus2Count: Int + public let customStatus3Count: Int + public let customStatus4Count: Int + public let customStatus5Count: Int + public let customStatus6Count: Int + public let customStatus7Count: Int + public var description: String? + public let failedCount: Int + public let id: Id + public var includeAll: Bool + public let isCompleted: Bool + public var milestoneId: Milestone.Id? + public var name: String + public let planId: Plan.Id? + public let passedCount: Int + public let projectId: Project.Id + public let retestCount: Int + public let suiteId: Suite.Id? + public let untestedCount: Int + public let url: URL +} + +// MARK: - Foward Relationships (ObjectAPI) + +extension Run { + + public func assignedto(_ objectAPI: ObjectAPI, completionHandler: @escaping (Outcome) -> Void) { + objectAPI.assignedto(self, completionHandler: completionHandler) + } + + public func configurations(_ objectAPI: ObjectAPI, completionHandler: @escaping (Outcome<[Configuration]?, ObjectAPI.MatchError, ObjectAPI.GetError>>) -> Void) { + objectAPI.configurations(self, completionHandler: completionHandler) + } + + public func createdBy(_ objectAPI: ObjectAPI, completionHandler: @escaping (Outcome) -> Void) { + objectAPI.createdBy(self, completionHandler: completionHandler) + } + + public func milestone(_ objectAPI: ObjectAPI, completionHandler: @escaping (Outcome) -> Void) { + objectAPI.milestone(self, completionHandler: completionHandler) + } + + public func plan(_ objectAPI: ObjectAPI, completionHandler: @escaping (Outcome) -> Void) { + objectAPI.plan(self, completionHandler: completionHandler) + } + + public func project(_ objectAPI: ObjectAPI, completionHandler: @escaping (Outcome) -> Void) { + objectAPI.project(self, completionHandler: completionHandler) + } + + public func suite(_ objectAPI: ObjectAPI, completionHandler: @escaping (Outcome) -> Void) { + objectAPI.suite(self, completionHandler: completionHandler) + } + +} + +// MARK: - Equatable + +extension Run: Equatable { + + public static func==(lhs: Run, rhs: Run) -> Bool { + return (lhs.assignedtoId == rhs.assignedtoId && + lhs.blockedCount == rhs.blockedCount && + lhs.completedOn?.secondsSince1970 == rhs.completedOn?.secondsSince1970 && + lhs.config == rhs.config && + lhs.configIds?.sorted() == rhs.configIds?.sorted() && + lhs.createdBy == rhs.createdBy && + lhs.createdOn.secondsSince1970 == rhs.createdOn.secondsSince1970 && + lhs.customStatus1Count == rhs.customStatus1Count && + lhs.customStatus2Count == rhs.customStatus2Count && + lhs.customStatus3Count == rhs.customStatus3Count && + lhs.customStatus4Count == rhs.customStatus4Count && + lhs.customStatus5Count == rhs.customStatus5Count && + lhs.customStatus6Count == rhs.customStatus6Count && + lhs.customStatus7Count == rhs.customStatus7Count && + lhs.description == rhs.description && + lhs.failedCount == rhs.failedCount && + lhs.id == rhs.id && + lhs.includeAll == rhs.includeAll && + lhs.isCompleted == rhs.isCompleted && + lhs.milestoneId == rhs.milestoneId && + lhs.name == rhs.name && + lhs.planId == rhs.planId && + lhs.passedCount == rhs.passedCount && + lhs.projectId == rhs.projectId && + lhs.retestCount == rhs.retestCount && + lhs.suiteId == rhs.suiteId && + lhs.untestedCount == rhs.untestedCount && + lhs.url == rhs.url) + } + +} + +// MARK: - JSON Keys + +extension Run { + + enum JSONKeys: JSONKey { + case assignedtoId = "assignedto_id" + case blockedCount = "blocked_count" + case completedOn = "completed_on" + case config + case configIds = "config_ids" + case createdBy = "created_by" + case createdOn = "created_on" + case customStatus1Count = "custom_status1_count" + case customStatus2Count = "custom_status2_count" + case customStatus3Count = "custom_status3_count" + case customStatus4Count = "custom_status4_count" + case customStatus5Count = "custom_status5_count" + case customStatus6Count = "custom_status6_count" + case customStatus7Count = "custom_status7_count" + case description + case failedCount = "failed_count" + case id + case includeAll = "include_all" + case isCompleted = "is_completed" + case milestoneId = "milestone_id" + case name + case planId = "plan_id" + case passedCount = "passed_count" + case projectId = "project_id" + case retestCount = "retest_count" + case suiteId = "suite_id" + case untestedCount = "untested_count" + case url + } + +} + +extension Run: UpdateRequestJSONKeys { + + var updateRequestJSONKeys: [JSONKey] { + return [ + JSONKeys.description.rawValue, + JSONKeys.includeAll.rawValue, + JSONKeys.milestoneId.rawValue, + JSONKeys.name.rawValue + ] + } + +} + +// MARK: - Serialization + +extension Run: JSONDeserializable { + + init?(json: JSONDictionary) { + + guard let blockedCount = json[JSONKeys.blockedCount.rawValue] as? Int, + let createdBy = json[JSONKeys.createdBy.rawValue] as? User.Id, + let createdOnSeconds = json[JSONKeys.createdOn.rawValue] as? Int, + let customStatus1Count = json[JSONKeys.customStatus1Count.rawValue] as? Int, + let customStatus2Count = json[JSONKeys.customStatus2Count.rawValue] as? Int, + let customStatus3Count = json[JSONKeys.customStatus3Count.rawValue] as? Int, + let customStatus4Count = json[JSONKeys.customStatus4Count.rawValue] as? Int, + let customStatus5Count = json[JSONKeys.customStatus5Count.rawValue] as? Int, + let customStatus6Count = json[JSONKeys.customStatus6Count.rawValue] as? Int, + let customStatus7Count = json[JSONKeys.customStatus7Count.rawValue] as? Int, + let failedCount = json[JSONKeys.failedCount.rawValue] as? Int, + let id = json[JSONKeys.id.rawValue] as? Id, + let includeAll = json[JSONKeys.includeAll.rawValue] as? Bool, + let isCompleted = json[JSONKeys.isCompleted.rawValue] as? Bool, + let name = json[JSONKeys.name.rawValue] as? String, + let passedCount = json[JSONKeys.passedCount.rawValue] as? Int, + let projectId = json[JSONKeys.projectId.rawValue] as? Project.Id, + let retestCount = json[JSONKeys.retestCount.rawValue] as? Int, + let untestedCount = json[JSONKeys.untestedCount.rawValue] as? Int, + let urlString = json[JSONKeys.url.rawValue] as? String, + let url = URL(string: urlString) else { + return nil + } + let createdOn = Date(secondsSince1970: createdOnSeconds) + + let completedOn: Date? + if let seconds = json[JSONKeys.completedOn.rawValue] as? Int { + completedOn = Date(secondsSince1970: seconds) + } else { + completedOn = nil + } + + let assignedtoId = json[JSONKeys.assignedtoId.rawValue] as? User.Id ?? nil + let config = json[JSONKeys.config.rawValue] as? String ?? nil + let configIds = json[JSONKeys.configIds.rawValue] as? [Configuration.Id] ?? nil + let description = json[JSONKeys.description.rawValue] as? String ?? nil + let milestoneId = json[JSONKeys.milestoneId.rawValue] as? Milestone.Id ?? nil + let planId = json[JSONKeys.planId.rawValue] as? Plan.Id ?? nil + let suiteId = json[JSONKeys.suiteId.rawValue] as? Suite.Id ?? nil + + self.init(assignedtoId: assignedtoId, blockedCount: blockedCount, completedOn: completedOn, config: config, configIds: configIds, createdBy: createdBy, createdOn: createdOn, customStatus1Count: customStatus1Count, customStatus2Count: customStatus2Count, customStatus3Count: customStatus3Count, customStatus4Count: customStatus4Count, customStatus5Count: customStatus5Count, customStatus6Count: customStatus6Count, customStatus7Count: customStatus7Count, description: description, failedCount: failedCount, id: id, includeAll: includeAll, isCompleted: isCompleted, milestoneId: milestoneId, name: name, planId: planId, passedCount: passedCount, projectId: projectId, retestCount: retestCount, suiteId: suiteId, untestedCount: untestedCount, url: url) + } + +} + +extension Run: JSONSerializable { + + func serialized() -> JSONDictionary { + return [JSONKeys.assignedtoId.rawValue: assignedtoId as Any, + JSONKeys.blockedCount.rawValue: blockedCount, + JSONKeys.completedOn.rawValue: completedOn?.secondsSince1970 as Any, + JSONKeys.config.rawValue: config as Any, + JSONKeys.configIds.rawValue: configIds as Any, + JSONKeys.createdBy.rawValue: createdBy, + JSONKeys.createdOn.rawValue: createdOn.secondsSince1970, + JSONKeys.customStatus1Count.rawValue: customStatus1Count, + JSONKeys.customStatus2Count.rawValue: customStatus2Count, + JSONKeys.customStatus3Count.rawValue: customStatus3Count, + JSONKeys.customStatus4Count.rawValue: customStatus4Count, + JSONKeys.customStatus5Count.rawValue: customStatus5Count, + JSONKeys.customStatus6Count.rawValue: customStatus6Count, + JSONKeys.customStatus7Count.rawValue: customStatus7Count, + JSONKeys.description.rawValue: description as Any, + JSONKeys.failedCount.rawValue: failedCount, + JSONKeys.id.rawValue: id, + JSONKeys.includeAll.rawValue: includeAll, + JSONKeys.isCompleted.rawValue: isCompleted, + JSONKeys.milestoneId.rawValue: milestoneId as Any, + JSONKeys.name.rawValue: name, + JSONKeys.planId.rawValue: planId as Any, + JSONKeys.passedCount.rawValue: passedCount, + JSONKeys.projectId.rawValue: projectId, + JSONKeys.retestCount.rawValue: retestCount, + JSONKeys.suiteId.rawValue: suiteId as Any, + JSONKeys.untestedCount.rawValue: untestedCount, + JSONKeys.url.rawValue: url.absoluteString] + } + +} + +extension Run: UpdateRequestJSON { } diff --git a/QuizTrain/Models/Section.swift b/QuizTrain/Models/Section.swift new file mode 100644 index 0000000..6f78abd --- /dev/null +++ b/QuizTrain/Models/Section.swift @@ -0,0 +1,105 @@ +public struct Section: Identifiable { + public typealias Id = Int + public let depth: Int + public var description: String? + public let displayOrder: Int + public let id: Id + public var name: String + public let parentId: Id? + public let suiteId: Suite.Id? +} + +// MARK: - Foward Relationships (ObjectAPI) + +extension Section { + + public func parent(_ objectAPI: ObjectAPI, completionHandler: @escaping (Outcome) -> Void) { + objectAPI.parent(self, completionHandler: completionHandler) + } + + public func suite(_ objectAPI: ObjectAPI, completionHandler: @escaping (Outcome) -> Void) { + objectAPI.suite(self, completionHandler: completionHandler) + } + +} + +// MARK: - Equatable + +extension Section: Equatable { + + public static func==(lhs: Section, rhs: Section) -> Bool { + return (lhs.depth == rhs.depth && + lhs.description == rhs.description && + lhs.displayOrder == rhs.displayOrder && + lhs.id == rhs.id && + lhs.name == rhs.name && + lhs.parentId == rhs.parentId && + lhs.suiteId == rhs.suiteId) + } + +} + +// MARK: - JSON Keys + +extension Section { + + enum JSONKeys: JSONKey { + case depth + case description + case displayOrder = "display_order" + case id + case name + case parentId = "parent_id" + case suiteId = "suite_id" + } + +} + +extension Section: UpdateRequestJSONKeys { + + var updateRequestJSONKeys: [JSONKey] { + return [ + JSONKeys.description.rawValue, + JSONKeys.name.rawValue + ] + } + +} + +// MARK: - Serialization + +extension Section: JSONDeserializable { + + init?(json: JSONDictionary) { + + guard let depth = json[JSONKeys.depth.rawValue] as? Int, + let displayOrder = json[JSONKeys.displayOrder.rawValue] as? Int, + let id = json[JSONKeys.id.rawValue] as? Id, + let name = json[JSONKeys.name.rawValue] as? String else { + return nil + } + + let description = json[JSONKeys.description.rawValue] as? String ?? nil + let parentId = json[JSONKeys.parentId.rawValue] as? Id ?? nil + let suiteId = json[JSONKeys.suiteId.rawValue] as? Suite.Id ?? nil + + self.init(depth: depth, description: description, displayOrder: displayOrder, id: id, name: name, parentId: parentId, suiteId: suiteId) + } + +} + +extension Section: JSONSerializable { + + func serialized() -> JSONDictionary { + return [JSONKeys.depth.rawValue: depth, + JSONKeys.description.rawValue: description as Any, + JSONKeys.displayOrder.rawValue: displayOrder, + JSONKeys.id.rawValue: id, + JSONKeys.name.rawValue: name, + JSONKeys.parentId.rawValue: parentId as Any, + JSONKeys.suiteId.rawValue: suiteId as Any] + } + +} + +extension Section: UpdateRequestJSON { } diff --git a/QuizTrain/Models/Status.swift b/QuizTrain/Models/Status.swift new file mode 100644 index 0000000..52a9f55 --- /dev/null +++ b/QuizTrain/Models/Status.swift @@ -0,0 +1,87 @@ +public struct Status: Identifiable { + public typealias Id = Int + public let colorBright: Int + public let colorDark: Int + public let colorMedium: Int + public let id: Id + public let isFinal: Bool + public let isSystem: Bool + public let isUntested: Bool + public let label: String + public let name: String +} + +// MARK: - Equatable + +extension Status: Equatable { + + public static func==(lhs: Status, rhs: Status) -> Bool { + return (lhs.colorBright == rhs.colorBright && + lhs.colorDark == rhs.colorDark && + lhs.colorMedium == rhs.colorMedium && + lhs.id == rhs.id && + lhs.isFinal == rhs.isFinal && + lhs.isSystem == rhs.isSystem && + lhs.isUntested == rhs.isUntested && + lhs.label == rhs.label && + lhs.name == rhs.name) + } + +} + +// MARK: - JSON Keys + +extension Status { + + enum JSONKeys: JSONKey { + case colorBright = "color_bright" + case colorDark = "color_dark" + case colorMedium = "color_medium" + case id + case isFinal = "is_final" + case isSystem = "is_system" + case isUntested = "is_untested" + case label + case name + } + +} + +// MARK: - Serialization + +extension Status: JSONDeserializable { + + init?(json: JSONDictionary) { + + guard let colorBright = json[JSONKeys.colorBright.rawValue] as? Int, + let colorDark = json[JSONKeys.colorDark.rawValue] as? Int, + let colorMedium = json[JSONKeys.colorMedium.rawValue] as? Int, + let id = json[JSONKeys.id.rawValue] as? Id, + let isFinal = json[JSONKeys.isFinal.rawValue] as? Bool, + let isSystem = json[JSONKeys.isSystem.rawValue] as? Bool, + let isUntested = json[JSONKeys.isUntested.rawValue] as? Bool, + let label = json[JSONKeys.label.rawValue] as? String, + let name = json[JSONKeys.name.rawValue] as? String else { + return nil + } + + self.init(colorBright: colorBright, colorDark: colorDark, colorMedium: colorMedium, id: id, isFinal: isFinal, isSystem: isSystem, isUntested: isUntested, label: label, name: name) + } + +} + +extension Status: JSONSerializable { + + func serialized() -> JSONDictionary { + return [JSONKeys.colorBright.rawValue: colorBright, + JSONKeys.colorDark.rawValue: colorDark, + JSONKeys.colorMedium.rawValue: colorMedium, + JSONKeys.id.rawValue: id, + JSONKeys.isFinal.rawValue: isFinal, + JSONKeys.isSystem.rawValue: isSystem, + JSONKeys.isUntested.rawValue: isUntested, + JSONKeys.label.rawValue: label, + JSONKeys.name.rawValue: name] + } + +} diff --git a/QuizTrain/Models/Suite.swift b/QuizTrain/Models/Suite.swift new file mode 100644 index 0000000..da28e31 --- /dev/null +++ b/QuizTrain/Models/Suite.swift @@ -0,0 +1,118 @@ +public struct Suite: Identifiable { + public typealias Id = Int + public let completedOn: Date? + public var description: String? + public let id: Id + public let isBaseline: Bool + public let isCompleted: Bool + public let isMaster: Bool + public var name: String + public let projectId: Project.Id + public let url: URL +} + +// MARK: - Foward Relationships (ObjectAPI) + +extension Suite { + + public func project(_ objectAPI: ObjectAPI, completionHandler: @escaping (Outcome) -> Void) { + objectAPI.project(self, completionHandler: completionHandler) + } + +} + +// MARK: - Equatable + +extension Suite: Equatable { + + public static func==(lhs: Suite, rhs: Suite) -> Bool { + return (lhs.completedOn?.secondsSince1970 == rhs.completedOn?.secondsSince1970 && + lhs.description == rhs.description && + lhs.id == rhs.id && + lhs.isBaseline == rhs.isBaseline && + lhs.isCompleted == rhs.isCompleted && + lhs.isMaster == rhs.isMaster && + lhs.name == rhs.name && + lhs.projectId == rhs.projectId && + lhs.url == rhs.url) + } + +} + +// MARK: - JSON Keys + +extension Suite { + + enum JSONKeys: JSONKey { + case completedOn = "completed_on" + case description = "description" + case id + case isBaseline = "is_baseline" + case isCompleted = "is_completed" + case isMaster = "is_master" + case name + case projectId = "project_id" + case url + } + +} + +extension Suite: UpdateRequestJSONKeys { + + var updateRequestJSONKeys: [JSONKey] { + return [ + JSONKeys.description.rawValue, + JSONKeys.name.rawValue + ] + } + +} + +// MARK: - Serialization + +extension Suite: JSONDeserializable { + + init?(json: JSONDictionary) { + + guard let id = json[JSONKeys.id.rawValue] as? Id, + let isBaseline = json[JSONKeys.isBaseline.rawValue] as? Bool, + let isCompleted = json[JSONKeys.isCompleted.rawValue] as? Bool, + let isMaster = json[JSONKeys.isMaster.rawValue] as? Bool, + let name = json[JSONKeys.name.rawValue] as? String, + let projectId = json[JSONKeys.projectId.rawValue] as? Project.Id, + let urlString = json[JSONKeys.url.rawValue] as? String, + let url = URL(string: urlString) else { + return nil + } + + let completedOn: Date? + if let seconds = json[JSONKeys.completedOn.rawValue] as? Int { + completedOn = Date(secondsSince1970: seconds) + } else { + completedOn = nil + } + + let description = json[JSONKeys.description.rawValue] as? String ?? nil + + self.init(completedOn: completedOn, description: description, id: id, isBaseline: isBaseline, isCompleted: isCompleted, isMaster: isMaster, name: name, projectId: projectId, url: url) + } + +} + +extension Suite: JSONSerializable { + + func serialized() -> JSONDictionary { + return [JSONKeys.completedOn.rawValue: completedOn?.secondsSince1970 as Any, + JSONKeys.description.rawValue: description as Any, + JSONKeys.id.rawValue: id, + JSONKeys.isBaseline.rawValue: isBaseline, + JSONKeys.isCompleted.rawValue: isCompleted, + JSONKeys.isMaster.rawValue: isMaster, + JSONKeys.name.rawValue: name, + JSONKeys.projectId.rawValue: projectId, + JSONKeys.url.rawValue: url.absoluteString] + } + +} + +extension Suite: UpdateRequestJSON { } diff --git a/QuizTrain/Models/Template.swift b/QuizTrain/Models/Template.swift new file mode 100644 index 0000000..402b124 --- /dev/null +++ b/QuizTrain/Models/Template.swift @@ -0,0 +1,57 @@ +public struct Template: Identifiable { + public typealias Id = Int + public let isDefault: Bool + public let id: Id + public let name: String +} + +// MARK: - Equatable + +extension Template: Equatable { + + public static func==(lhs: Template, rhs: Template) -> Bool { + return (lhs.isDefault == rhs.isDefault && + lhs.id == rhs.id && + lhs.name == rhs.name) + } + +} + +// MARK: - JSON Keys + +extension Template { + + enum JSONKeys: JSONKey { + case isDefault = "is_default" + case id + case name + } + +} + +// MARK: - Serialization + +extension Template: JSONDeserializable { + + init?(json: JSONDictionary) { + + guard let isDefault = json[JSONKeys.isDefault.rawValue] as? Bool, + let id = json[JSONKeys.id.rawValue] as? Id, + let name = json[JSONKeys.name.rawValue] as? String else { + return nil + } + + self.init(isDefault: isDefault, id: id, name: name) + } + +} + +extension Template: JSONSerializable { + + func serialized() -> JSONDictionary { + return [JSONKeys.isDefault.rawValue: isDefault, + JSONKeys.id.rawValue: id, + JSONKeys.name.rawValue: name] + } + +} diff --git a/QuizTrain/Models/Test.swift b/QuizTrain/Models/Test.swift new file mode 100644 index 0000000..6922d77 --- /dev/null +++ b/QuizTrain/Models/Test.swift @@ -0,0 +1,156 @@ +public struct Test: CustomFields, Identifiable { + public typealias Id = Int + public let assignedtoId: User.Id? + public let caseId: Case.Id + public let estimate: String? + public let estimateForecast: String? + public let id: Id + public let milestoneId: Milestone.Id? + public let priorityId: Priority.Id + public let refs: String? + public let runId: Run.Id + public let statusId: Status.Id + public let templateId: Template.Id + public let title: String + public let typeId: CaseType.Id + let customFieldsContainer: CustomFieldsContainer +} + +// MARK: - Foward Relationships (ObjectAPI) + +extension Test { + + public func assignedto(_ objectAPI: ObjectAPI, completionHandler: @escaping (Outcome) -> Void) { + objectAPI.assignedto(self, completionHandler: completionHandler) + } + + public func `case`(_ objectAPI: ObjectAPI, completionHandler: @escaping (Outcome) -> Void) { + objectAPI.`case`(self, completionHandler: completionHandler) + } + + public func milestone(_ objectAPI: ObjectAPI, completionHandler: @escaping (Outcome) -> Void) { + objectAPI.milestone(self, completionHandler: completionHandler) + } + + public func priority(_ objectAPI: ObjectAPI, completionHandler: @escaping (Outcome, ObjectAPI.GetError>>) -> Void) { + objectAPI.priority(self, completionHandler: completionHandler) + } + + public func run(_ objectAPI: ObjectAPI, completionHandler: @escaping (Outcome) -> Void) { + objectAPI.run(self, completionHandler: completionHandler) + } + + public func status(_ objectAPI: ObjectAPI, completionHandler: @escaping (Outcome, ObjectAPI.GetError>>) -> Void) { + objectAPI.status(self, completionHandler: completionHandler) + } + + public func template(_ objectAPI: ObjectAPI, completionHandler: @escaping (Outcome, ErrorContainer>>) -> Void) { + objectAPI.template(self, completionHandler: completionHandler) + } + + public func type(_ objectAPI: ObjectAPI, completionHandler: @escaping (Outcome, ObjectAPI.GetError>>) -> Void) { + objectAPI.type(self, completionHandler: completionHandler) + } + +} + +// MARK: - Equatable + +extension Test: Equatable { + + public static func==(lhs: Test, rhs: Test) -> Bool { + return (lhs.assignedtoId == rhs.assignedtoId && + lhs.caseId == rhs.caseId && + lhs.estimate == rhs.estimate && + lhs.estimateForecast == rhs.estimateForecast && + lhs.id == rhs.id && + lhs.milestoneId == rhs.milestoneId && + lhs.priorityId == rhs.priorityId && + lhs.refs == rhs.refs && + lhs.runId == rhs.runId && + lhs.statusId == rhs.statusId && + lhs.templateId == rhs.templateId && + lhs.title == rhs.title && + lhs.typeId == rhs.typeId && + lhs.customFieldsContainer == rhs.customFieldsContainer) + } + +} + +// MARK: - JSON Keys + +extension Test { + + enum JSONKeys: JSONKey { + case assignedtoId = "assignedto_id" + case caseId = "case_id" + case estimate + case estimateForecast = "estimate_forecast" + case id + case milestoneId = "milestone_id" + case priorityId = "priority_id" + case refs + case runId = "run_id" + case statusId = "status_id" + case templateId = "template_id" + case title + case typeId = "type_id" + } + +} + +// MARK: - Serialization + +extension Test: JSONDeserializable { + + init?(json: JSONDictionary) { + + guard let caseId = json[JSONKeys.caseId.rawValue] as? Case.Id, + let id = json[JSONKeys.id.rawValue] as? Id, + let priorityId = json[JSONKeys.priorityId.rawValue] as? Priority.Id, + let runId = json[JSONKeys.runId.rawValue] as? Run.Id, + let statusId = json[JSONKeys.statusId.rawValue] as? Status.Id, + let templateId = json[JSONKeys.templateId.rawValue] as? Template.Id, + let title = json[JSONKeys.title.rawValue] as? String, + let typeId = json[JSONKeys.typeId.rawValue] as? CaseType.Id else { + return nil + } + + let assignedtoId = json[JSONKeys.assignedtoId.rawValue] as? User.Id ?? nil + let estimate = json[JSONKeys.estimate.rawValue] as? String ?? nil + let estimateForecast = json[JSONKeys.estimateForecast.rawValue] as? String ?? nil + let milestoneId = json[JSONKeys.milestoneId.rawValue] as? Milestone.Id ?? nil + let refs = json[JSONKeys.refs.rawValue] as? String ?? nil + + let customFieldsContainer = CustomFieldsContainer(json: json) + + self.init(assignedtoId: assignedtoId, caseId: caseId, estimate: estimate, estimateForecast: estimateForecast, id: id, milestoneId: milestoneId, priorityId: priorityId, refs: refs, runId: runId, statusId: statusId, templateId: templateId, title: title, typeId: typeId, customFieldsContainer: customFieldsContainer) + } + +} + +extension Test: JSONSerializable { + + private var serializedProperties: JSONDictionary { + return [JSONKeys.assignedtoId.rawValue: assignedtoId as Any, + JSONKeys.caseId.rawValue: caseId, + JSONKeys.estimate.rawValue: estimate as Any, + JSONKeys.estimateForecast.rawValue: estimateForecast as Any, + JSONKeys.id.rawValue: id, + JSONKeys.milestoneId.rawValue: milestoneId as Any, + JSONKeys.priorityId.rawValue: priorityId, + JSONKeys.refs.rawValue: refs as Any, + JSONKeys.runId.rawValue: runId, + JSONKeys.statusId.rawValue: statusId, + JSONKeys.templateId.rawValue: templateId, + JSONKeys.title.rawValue: title, + JSONKeys.typeId.rawValue: typeId] + } + + func serialized() -> JSONDictionary { + var json = serializedProperties + customFields.forEach { item in json[item.key] = item.value } + return json + } + +} diff --git a/QuizTrain/Models/Types/CustomFieldType.swift b/QuizTrain/Models/Types/CustomFieldType.swift new file mode 100644 index 0000000..bd23d85 --- /dev/null +++ b/QuizTrain/Models/Types/CustomFieldType.swift @@ -0,0 +1,48 @@ +public enum CustomFieldType: Int { + case string = 1 + case integer = 2 + case text = 3 + case url = 4 + case checkbox = 5 + case dropdown = 6 + case user = 7 + case date = 8 + case milestone = 9 + case steps = 10 + case stepResults = 11 + case multiSelect = 12 +} + +extension CustomFieldType { + + // swiftlint:disable:next cyclomatic_complexity + public func description() -> String { + switch self { + case .string: + return "String" // String? + case .integer: + return "Integer" // Int? + case .text: + return "Text" // String? + case .url: + return "URL" // String? ("http://www.venmo.com/") + case .checkbox: + return "Checkbox" // Bool + case .dropdown: + return "Dropdown" // Int? + case .user: + return "User" // Int? + case .date: + return "Date" // String? ("10/17/2017") + case .milestone: + return "Milestone" // Int? + case .steps: + return "Steps" // String? + case .stepResults: + return "Step Results" // [[String: Any]] (unknown if this can be optional) + case .multiSelect: + return "Multi-Select" // [Int] + } + } + +} diff --git a/QuizTrain/Models/Types/Project.SuiteMode.swift b/QuizTrain/Models/Types/Project.SuiteMode.swift new file mode 100644 index 0000000..584b000 --- /dev/null +++ b/QuizTrain/Models/Types/Project.SuiteMode.swift @@ -0,0 +1,20 @@ +extension Project { + public enum SuiteMode: Int { + case singleSuite = 1 + case singleSuitePlusBaselines = 2 + case multipleSuites = 3 + } +} + +extension Project.SuiteMode { + public func description() -> String { + switch self { + case .singleSuite: + return "Single Suite" + case .singleSuitePlusBaselines: + return "Single Suite Plus Baselines" + case .multipleSuites: + return "Multiple Suites" + } + } +} diff --git a/QuizTrain/Models/User.swift b/QuizTrain/Models/User.swift new file mode 100644 index 0000000..1096a77 --- /dev/null +++ b/QuizTrain/Models/User.swift @@ -0,0 +1,62 @@ +public struct User: Identifiable { + public typealias Id = Int + public let email: String + public let id: Id + public let isActive: Bool + public let name: String +} + +// MARK: - Equatable + +extension User: Equatable { + + public static func==(lhs: User, rhs: User) -> Bool { + return (lhs.email == rhs.email && + lhs.id == rhs.id && + lhs.isActive == rhs.isActive && + lhs.name == rhs.name) + } + +} + +// MARK: - JSON Keys + +extension User { + + enum JSONKeys: JSONKey { + case email + case id + case isActive = "is_active" + case name + } + +} + +// MARK: - Serialization + +extension User: JSONDeserializable { + + init?(json: JSONDictionary) { + + guard let email = json[JSONKeys.email.rawValue] as? String, + let id = json[JSONKeys.id.rawValue] as? Id, + let isActive = json[JSONKeys.isActive.rawValue] as? Bool, + let name = json[JSONKeys.name.rawValue] as? String else { + return nil + } + + self.init(email: email, id: id, isActive: isActive, name: name) + } + +} + +extension User: JSONSerializable { + + func serialized() -> JSONDictionary { + return [JSONKeys.email.rawValue: email, + JSONKeys.id.rawValue: id, + JSONKeys.isActive.rawValue: isActive, + JSONKeys.name.rawValue: name] + } + +} diff --git a/QuizTrain/Network/API.swift b/QuizTrain/Network/API.swift new file mode 100644 index 0000000..c4a608e --- /dev/null +++ b/QuizTrain/Network/API.swift @@ -0,0 +1,676 @@ +import Foundation + +/* + Low-level interface to TestRail's API. Handles authentication, API endpoint + configuration, creating requests, and returning Outcome's to the consumer of + this API. This API is dumb and contains no error-handling logic, passing all + errors to its consumer. The consumer is responsible for handling all errors. + + Interfaces in this class accept basic types such as String, Int, Bool, and Data + to create and execute calls to the TestRail API. Naming conventions map to url + schemes rather than Swift naming conventions to make it easier to comprehend + API documentation. + + For a higher level of abstraction use ObjectAPI. + */ +final public class API { + + // MARK: - Properties + + public var username: String // your@email.com + public var secret: String // Password or API Key + public var hostname: String // yourinstance.testrail.net + public var port: Int // 443, 80, 8080, etc + public var scheme: String // "https" or "http" + private let session = URLSession(configuration: .`default`) + + // MARK: - Init + + public init(username: String, secret: String, hostname: String, port: Int = 443, scheme: String = "https") { + self.username = username + self.secret = secret + self.hostname = hostname + self.port = port + self.scheme = scheme + } + + // MARK: - Deinit + + deinit { + session.invalidateAndCancel() + } + + // MARK: - Request Options + + public enum HttpMethod: String { + case get = "GET" + case post = "POST" + } + + // MARK: - URL Components + + private var baseURLComponents: URLComponents { + var components = URLComponents() + components.scheme = scheme + components.host = hostname + components.path = "/index.php" + components.port = port + return components + } + + private func urlComponents(for uri: String, queryItems: [URLQueryItem]? = nil) -> URLComponents { + + var allQueryItems = [URLQueryItem]() + let firstQueryItem = URLQueryItem(name: "/api/v2/" + uri, value: nil) + allQueryItems.append(firstQueryItem) + + if let queryItems = queryItems { + allQueryItems += queryItems + } + + var urlComponents = baseURLComponents + urlComponents.queryItems = allQueryItems + + return urlComponents + } + + // MARK: - Header Fields + + private func authorization() -> String { + let usernamePassword = username + ":" + secret + let usernamePasswordBase64 = Data(usernamePassword.utf8).base64EncodedString() + return "Basic \(usernamePasswordBase64)" + } + + // MARK: - URLRequests + + private func testRailRequest(url: URL, cachePolicy: URLRequest.CachePolicy = .reloadIgnoringLocalAndRemoteCacheData, timeoutInterval: TimeInterval = 60.0) -> URLRequest { + var request = URLRequest(url: url, cachePolicy: cachePolicy, timeoutInterval: timeoutInterval) + request.setValue("application/json", forHTTPHeaderField: "Content-Type") // All requests require "application/json" Content-Type, including GETs. + request.setValue(authorization(), forHTTPHeaderField: "Authorization") // All requests require HTTP Basic Authentication. + return request + } + + // MARL: - Errors + + public enum RequestError: Error { + case error(request: URLRequest, error: Error) + case nilResponse(request: URLRequest) + case invalidResponse(request: URLRequest, response: URLResponse) + } + + // MARK: - Results + + public struct RequestResult { + public let request: URLRequest // Initial unaltered request. Note this will contain the HTTP Basic Authentication credentials. This info is useful for troubleshooting. + public let response: HTTPURLResponse // Response. + public let data: Data // Response data. This will be empty if none was returned. + } + + // MARK: - Outcomes + + public typealias RequestOutcome = Outcome + + // MARK: - Base Requests + + @discardableResult public func get(_ uri: String, queryItems: [URLQueryItem]? = nil, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return request(uri, queryItems: queryItems, httpMethod: .get, completionHandler: completionHandler) + } + + @discardableResult public func post(_ uri: String, queryItems: [URLQueryItem]? = nil, data: Data? = nil, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return request(uri, queryItems: queryItems, httpMethod: .post, data: data, completionHandler: completionHandler) + } + + @discardableResult public func request(_ uri: String, queryItems: [URLQueryItem]? = nil, httpMethod: HttpMethod, data: Data? = nil, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + let url = urlComponents(for: uri, queryItems: queryItems).url! + return request(url: url, httpMethod: httpMethod, data: data, completionHandler: completionHandler) + } + + private func request(url: URL, httpMethod: HttpMethod, data: Data? = nil, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + + var request = testRailRequest(url: url) + request.httpMethod = httpMethod.rawValue + request.httpBody = data + + let task = session.dataTask(with: request) { (data, response, error) in + + var outcome: RequestOutcome + defer { + completionHandler(outcome) + } + + guard error == nil else { + outcome = .failed(.error(request: request, error: error!)) + return + } + + guard let response = response else { + outcome = .failed(.nilResponse(request: request)) + return + } + + guard let urlResponse = response as? HTTPURLResponse else { + outcome = .failed(.invalidResponse(request: request, response: response)) + return + } + + let data = data ?? Data() + + outcome = .succeeded(RequestResult(request: request, response: urlResponse, data: data)) + } + + task.resume() + return task + } + + // MARK: - Cases + + /* + http://docs.gurock.com/testrail-api2/reference-cases#add_case + */ + @discardableResult public func addCase(sectionId: Int, data: Data, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return post("add_case/\(sectionId)", data: data, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-cases#delete_case + */ + @discardableResult public func deleteCase(caseId: Int, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return post("delete_case/\(caseId)", completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-cases#get_case + */ + @discardableResult public func getCase(caseId: Int, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return get("get_case/\(caseId)", completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-cases#get_cases + */ + @discardableResult public func getCases(projectId: Int, suiteId: Int? = nil, sectionId: Int? = nil, filters: [Filter]? = nil, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + + var queryItems = [URLQueryItem]() + + if let suiteId = suiteId { + queryItems.append(URLQueryItem(name: "suite_id", value: String(suiteId))) + } + + if let sectionId = sectionId { + queryItems.append(URLQueryItem(name: "section_id", value: String(sectionId))) + } + + if let filters = filters { + _ = filters.flatMap { queryItems.append($0.queryItem) } + } + + return get("get_cases/\(projectId)", queryItems: queryItems, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-cases#update_case + */ + @discardableResult public func updateCase(caseId: Int, data: Data, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return post("update_case/\(caseId)", data: data, completionHandler: completionHandler) + } + + // MARK: - Case Fields + + /* + http://docs.gurock.com/testrail-api2/reference-cases-fields#get_case_fields + */ + @discardableResult public func getCaseFields(completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return get("get_case_fields", completionHandler: completionHandler) + } + + // MARK: - Case Types + + /* + http://docs.gurock.com/testrail-api2/reference-cases-types#get_case_types + */ + @discardableResult public func getCaseTypes(completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return get("get_case_types", completionHandler: completionHandler) + } + + // MARK: - Configurations + + /* + http://docs.gurock.com/testrail-api2/reference-configs#add_config + */ + @discardableResult public func addConfiguration(configurationGroupId: Int, data: Data, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return post("add_config/\(configurationGroupId)", data: data, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-configs#delete_config + */ + @discardableResult public func deleteConfiguration(configurationId: Int, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return post("delete_config/\(configurationId)", completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-configs#get_configs + */ + @discardableResult public func getConfigurations(projectId: Int, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return get("get_configs/\(projectId)", completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-configs#update_config + */ + @discardableResult public func updateConfiguration(configurationId: Int, data: Data, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return post("update_config/\(configurationId)", data: data, completionHandler: completionHandler) + } + + // MARK: - Configuration Groups + + /* + http://docs.gurock.com/testrail-api2/reference-configs#add_config_group + */ + @discardableResult public func addConfigurationGroup(projectId: Int, data: Data, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return post("add_config_group/\(projectId)", data: data, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-configs#delete_config_group + */ + @discardableResult public func deleteConfigurationGroup(configurationGroupId: Int, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return post("delete_config_group/\(configurationGroupId)", completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-configs#update_config_group + */ + @discardableResult public func updateConfigurationGroup(configurationGroupId: Int, data: Data, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return post("update_config_group/\(configurationGroupId)", data: data, completionHandler: completionHandler) + } + + // MARK: - Milestones + + /* + http://docs.gurock.com/testrail-api2/reference-milestones#add_milestone + */ + @discardableResult public func addMilestone(projectId: Int, data: Data, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return post("add_milestone/\(projectId)", data: data, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-milestones#delete_milestone + */ + @discardableResult public func deleteMilestone(milestoneId: Int, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return post("delete_milestone/\(milestoneId)", completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-milestones#get_milestone + */ + @discardableResult public func getMilestone(milestoneId: Int, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return get("get_milestone/\(milestoneId)", completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-milestones#get_milestones + */ + @discardableResult public func getMilestones(projectId: Int, filters: [Filter]? = nil, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return get("get_milestones/\(projectId)", queryItems: Filter.queryItems(for: filters), completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-milestones#update_milestone + */ + @discardableResult public func updateMilestone(milestoneId: Int, data: Data, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return post("update_milestone/\(milestoneId)", data: data, completionHandler: completionHandler) + } + + // MARK: - Plans + + /* + http://docs.gurock.com/testrail-api2/reference-plans#add_plan + */ + @discardableResult public func addPlan(projectId: Int, data: Data, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return post("add_plan/\(projectId)", data: data, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-plans#close_plan + */ + @discardableResult public func closePlan(planId: Int, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return post("close_plan/\(planId)", completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-plans#delete_plan + */ + @discardableResult public func deletePlan(planId: Int, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return post("delete_plan/\(planId)", completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-plans#get_plan + */ + @discardableResult public func getPlan(planId: Int, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return get("get_plan/\(planId)", completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-plans#get_plans + */ + @discardableResult public func getPlans(projectId: Int, filters: [Filter]? = nil, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return get("get_plans/\(projectId)", queryItems: Filter.queryItems(for: filters), completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-plans#update_plan + */ + @discardableResult public func updatePlan(planId: Int, data: Data, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return post("update_plan/\(planId)", data: data, completionHandler: completionHandler) + } + + // MARK: - Plan Entries + + /* + http://docs.gurock.com/testrail-api2/reference-plans#add_plan_entry + */ + @discardableResult public func addPlanEntry(planId: Int, data: Data, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return post("add_plan_entry/\(planId)", data: data, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-plans#delete_plan_entry + */ + @discardableResult public func deletePlanEntry(planId: Int, planEntryId: String, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return post("delete_plan_entry/\(planId)/\(planEntryId)", completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-plans#update_plan_entry + */ + @discardableResult public func updatePlanEntry(planId: Int, planEntryId: String, data: Data, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return post("update_plan_entry/\(planId)/\(planEntryId)", data: data, completionHandler: completionHandler) + } + + // MARK: - Priorities + + /* + http://docs.gurock.com/testrail-api2/reference-priorities#get_priorities + */ + @discardableResult public func getPriorities(completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return get("get_priorities", completionHandler: completionHandler) + } + + // MARK: - Projects + + /* + http://docs.gurock.com/testrail-api2/reference-projects#add_project + */ + @discardableResult public func addProject(data: Data, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return post("add_project", data: data, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-projects#delete_project + */ + @discardableResult public func deleteProject(projectId: Int, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return post("delete_project/\(projectId)", completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-projects#get_project + */ + @discardableResult public func getProject(projectId: Int, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return get("get_project/\(projectId)", completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-projects#get_projects + */ + @discardableResult public func getProjects(filters: [Filter]? = nil, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return get("get_projects", queryItems: Filter.queryItems(for: filters), completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-projects#update_project + */ + @discardableResult public func updateProject(projectId: Int, data: Data, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return post("update_project/\(projectId)", data: data, completionHandler: completionHandler) + } + + // MARK: - Results + + /* + http://docs.gurock.com/testrail-api2/reference-results#add_result + */ + @discardableResult public func addResult(testId: Int, data: Data, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return post("add_result/\(testId)", data: data, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-results#add_result_for_case + */ + @discardableResult public func addResultForCase(runId: Int, caseId: Int, data: Data, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return post("add_result_for_case/\(runId)/\(caseId)", data: data, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-results#add_results + */ + @discardableResult public func addResults(runId: Int, data: Data, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return post("add_results/\(runId)", data: data, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-results#add_results_for_cases + */ + @discardableResult public func addResultsForCases(runId: Int, data: Data, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return post("add_results_for_cases/\(runId)", data: data, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-results#get_results + */ + @discardableResult public func getResults(testId: Int, filters: [Filter]? = nil, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return get("get_results/\(testId)", queryItems: Filter.queryItems(for: filters), completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-results#get_results_for_case + */ + @discardableResult public func getResultsForCase(runId: Int, caseId: Int, filters: [Filter]? = nil, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return get("get_results_for_case/\(runId)/\(caseId)", queryItems: Filter.queryItems(for: filters), completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-results#get_results_for_run + */ + @discardableResult public func getResultsForRun(runId: Int, filters: [Filter]? = nil, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return get("get_results_for_run/\(runId)", queryItems: Filter.queryItems(for: filters), completionHandler: completionHandler) + } + + // MARK: - Result Fields + + /* + http://docs.gurock.com/testrail-api2/reference-results-fields#get_result_fields + */ + @discardableResult public func getResultFields(completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return get("get_result_fields", completionHandler: completionHandler) + } + + // MARK: - Runs + + /* + http://docs.gurock.com/testrail-api2/reference-runs#add_run + */ + @discardableResult public func addRun(projectId: Int, data: Data, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return post("add_run/\(projectId)", data: data, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-runs#close_run + */ + @discardableResult public func closeRun(runId: Int, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return post("close_run/\(runId)", completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-runs#delete_run + */ + @discardableResult public func deleteRun(runId: Int, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return post("delete_run/\(runId)", completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-runs#get_run + */ + @discardableResult public func getRun(runId: Int, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return get("get_run/\(runId)", completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-runs#get_runs + */ + @discardableResult public func getRuns(projectId: Int, filters: [Filter]? = nil, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return get("get_runs/\(projectId)", queryItems: Filter.queryItems(for: filters), completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-runs#update_run + */ + @discardableResult public func updateRun(runId: Int, data: Data, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return post("update_run/\(runId)", data: data, completionHandler: completionHandler) + } + + // MARK: - Sections + + /* + http://docs.gurock.com/testrail-api2/reference-sections#add_section + */ + @discardableResult public func addSection(projectId: Int, data: Data, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return post("add_section/\(projectId)", data: data, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-sections#delete_section + */ + @discardableResult public func deleteSection(sectionId: Int, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return post("delete_section/\(sectionId)", completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-sections#get_section + */ + @discardableResult public func getSection(sectionId: Int, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return get("get_section/\(sectionId)", completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-sections#get_sections + */ + @discardableResult public func getSections(projectId: Int, suiteId: Int? = nil, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + let queryItems = suiteId != nil ? [URLQueryItem(name: "suite_id", value: String(suiteId!))] : nil + return get("get_sections/\(projectId)", queryItems: queryItems, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-sections#update_section + */ + @discardableResult public func updateSection(sectionId: Int, data: Data, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return post("update_section/\(sectionId)", data: data, completionHandler: completionHandler) + } + + // MARK: - Statuses + + /* + http://docs.gurock.com/testrail-api2/reference-statuses#get_statuses + */ + @discardableResult public func getStatuses(completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return get("get_statuses", completionHandler: completionHandler) + } + + // MARK: - Suites + + /* + http://docs.gurock.com/testrail-api2/reference-suites#add_suite + */ + @discardableResult public func addSuite(projectId: Int, data: Data, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return post("add_suite/\(projectId)", data: data, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-suites#delete_suite + */ + @discardableResult public func deleteSuite(suiteId: Int, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return post("delete_suite/\(suiteId)", completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-suites#get_suite + */ + @discardableResult public func getSuite(suiteId: Int, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return get("get_suite/\(suiteId)", completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-suites#get_suites + */ + @discardableResult public func getSuites(projectId: Int, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return get("get_suites/\(projectId)", completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-suites#update_suite + */ + @discardableResult public func updateSuite(suiteId: Int, data: Data, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return post("update_suite/\(suiteId)", data: data, completionHandler: completionHandler) + } + + // MARK: - Templates + + /* + http://docs.gurock.com/testrail-api2/reference-templates#get_templates + */ + @discardableResult public func getTemplates(projectId: Int, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return get("get_templates/\(projectId)", completionHandler: completionHandler) + } + + // MARK: - Tests + + /* + http://docs.gurock.com/testrail-api2/reference-tests#get_test + */ + @discardableResult public func getTest(testId: Int, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return get("get_test/\(testId)", completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-tests#get_tests + */ + @discardableResult public func getTests(runId: Int, filters: [Filter]? = nil, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return get("get_tests/\(runId)", queryItems: Filter.queryItems(for: filters), completionHandler: completionHandler) + } + + // MARK: - Users + + /* + http://docs.gurock.com/testrail-api2/reference-users#get_user + */ + @discardableResult public func getUser(userId: Int, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return get("get_user/\(userId)", completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-users#get_user_by_email + */ + @discardableResult public func getUserByEmail(_ email: String, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + let queryItems = [URLQueryItem(name: "email", value: email)] + return get("get_user_by_email", queryItems: queryItems, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-users#get_users + */ + @discardableResult public func getUsers(completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return get("get_users", completionHandler: completionHandler) + } + +} diff --git a/QuizTrain/Network/Extensions/API/API.RequestErrorDebug.swift b/QuizTrain/Network/Extensions/API/API.RequestErrorDebug.swift new file mode 100644 index 0000000..5b115d3 --- /dev/null +++ b/QuizTrain/Network/Extensions/API/API.RequestErrorDebug.swift @@ -0,0 +1,71 @@ +extension API.RequestError: DebugDescription { + + public var debugDescription: String { + + var description: String = "API.RequestError" + + switch self { + case .error(_, _): + description += ".error:\n\n\(debugDetails)\n" + case .invalidResponse(_, _): + description += ".invalidResponse:\n\n\(debugDetails)\n" + case .nilResponse(_): + description += ".nilResponse:\n\n\(debugDetails)\n" + } + + return description + } + +} + +extension API.RequestError: DebugDetails { + + public var debugDetails: String { + + let details: String + + switch self { + case .error(let request, let error): + details = """ + _____ERROR_____ + + \(error) + + _____REQUEST_____ + + \(request.httpMethod ?? "") \(request.url?.absoluteString ?? "") + + \(request.httpHeaderFieldsAsMultiLineString(omittingHeaders: ["AUTHORIZATION"]) ?? "") + + \(request.httpBodyAsUTF8 ?? "") + """ + case .invalidResponse(let request, let response): + details = """ + _____REQUEST_____ + + \(request.httpMethod ?? "") \(request.url?.absoluteString ?? "") + + \(request.httpHeaderFieldsAsMultiLineString(omittingHeaders: ["AUTHORIZATION"]) ?? "") + + \(request.httpBodyAsUTF8 ?? "") + + _____RESPONSE_____ + + \(response) + """ + case .nilResponse(let request): + details = """ + _____REQUEST_____ + + \(request.httpMethod ?? "") \(request.url?.absoluteString ?? "") + + \(request.httpHeaderFieldsAsMultiLineString(omittingHeaders: ["AUTHORIZATION"]) ?? "") + + \(request.httpBodyAsUTF8 ?? "") + """ + } + + return details + } + +} diff --git a/QuizTrain/Network/Extensions/API/API.RequestResultDebug.swift b/QuizTrain/Network/Extensions/API/API.RequestResultDebug.swift new file mode 100644 index 0000000..c3388d1 --- /dev/null +++ b/QuizTrain/Network/Extensions/API/API.RequestResultDebug.swift @@ -0,0 +1,29 @@ +extension API.RequestResult: DebugDescription { + + public var debugDescription: String { + return "API.RequestResult:\n\n\(debugDetails)\n" + } + +} + +extension API.RequestResult: DebugDetails { + + public var debugDetails: String { + return """ + _____REQUEST_____ + + \(request.httpMethod ?? "") \(request.url?.absoluteString ?? "") + + \(request.httpHeaderFieldsAsMultiLineString(omittingHeaders: ["AUTHORIZATION"]) ?? "") + + \(request.httpBodyAsUTF8 ?? "") + + _____RESPONSE_____ + + \(response) + + \(String(data: data, encoding: .utf8) ?? "") + """ + } + +} diff --git a/QuizTrain/Network/Extensions/ObjectAPI/ObjectAPI.ClientErrorDebug.swift b/QuizTrain/Network/Extensions/ObjectAPI/ObjectAPI.ClientErrorDebug.swift new file mode 100644 index 0000000..2f143bc --- /dev/null +++ b/QuizTrain/Network/Extensions/ObjectAPI/ObjectAPI.ClientErrorDebug.swift @@ -0,0 +1,23 @@ +extension ObjectAPI.ClientError: DebugDescription { + + public var debugDescription: String { + return "ObjectAPI.ClientError:\n\n\(debugDetails)\n" + } + +} + +extension ObjectAPI.ClientError: DebugDetails { + + public var debugDetails: String { + return """ + _____DETAILS_____ + + CODE: \(statusCode) + + MESSAGE: \(message) + + \(requestResult.debugDetails) + """ + } + +} diff --git a/QuizTrain/Network/Extensions/ObjectAPI/ObjectAPI.DataProcessingErrorDebug.swift b/QuizTrain/Network/Extensions/ObjectAPI/ObjectAPI.DataProcessingErrorDebug.swift new file mode 100644 index 0000000..e6697ff --- /dev/null +++ b/QuizTrain/Network/Extensions/ObjectAPI/ObjectAPI.DataProcessingErrorDebug.swift @@ -0,0 +1,56 @@ +extension ObjectAPI.DataProcessingError: DebugDescription { + + public var debugDescription: String { + var description = "ObjectAPI.DataProcessingError" + switch self { + case .couldNotConvertDataToJSON(_, _): + description += ".couldNotConvertDataToJSON:\n\n\(debugDetails)\n" + case .couldNotDeserializeFromJSON(_, _): + description += ".couldNotDeserializeFromJSON:\n\n\(debugDetails)\n" + case .invalidJSONFormat(_): + description += ".invalidJSONFormat:\n\n\(debugDetails)\n" + } + return description + } + +} + +extension ObjectAPI.DataProcessingError: DebugDetails { + + public var debugDetails: String { + + let details: String + + switch self { + case .couldNotConvertDataToJSON(let data, let error): + details = """ + _____DATA_____ + + \(data) + + _____ERROR_____ + + \(error) + """ + case .couldNotDeserializeFromJSON(let objectType, let json): + details = """ + _____DETAILS_____ + + Object Type: \(objectType) + + _____JSON_____ + + \(json) + """ + case .invalidJSONFormat(let json): + details = """ + _____JSON_____ + + \(json) + """ + } + + return details + } + +} diff --git a/QuizTrain/Network/Extensions/ObjectAPI/ObjectAPI.DataRequestErrorDebug.swift b/QuizTrain/Network/Extensions/ObjectAPI/ObjectAPI.DataRequestErrorDebug.swift new file mode 100644 index 0000000..5aaa19f --- /dev/null +++ b/QuizTrain/Network/Extensions/ObjectAPI/ObjectAPI.DataRequestErrorDebug.swift @@ -0,0 +1,33 @@ +extension ObjectAPI.DataRequestError: DebugDescription { + + public var debugDescription: String { + var description = "ObjectAPI.DataRequestError" + switch self { + case .apiError(_): + description += ".apiError:\n\n\(debugDetails)\n" + case .dataProcessingError(_): + description += ".dataProcessingError:\n\n\(debugDetails)\n" + case .statusCodeError(_): + description += ".statusCodeError:\n\n\(debugDetails)\n" + } + return description + } + +} + +extension ObjectAPI.DataRequestError: DebugDetails { + + public var debugDetails: String { + let details: String + switch self { + case .apiError(let error): + details = error.debugDetails + case .dataProcessingError(let error): + details = error.debugDetails + case .statusCodeError(let error): + details = error.debugDetails + } + return details + } + +} diff --git a/QuizTrain/Network/Extensions/ObjectAPI/ObjectAPI.MatchErrorDebug.swift b/QuizTrain/Network/Extensions/ObjectAPI/ObjectAPI.MatchErrorDebug.swift new file mode 100644 index 0000000..d66e5c5 --- /dev/null +++ b/QuizTrain/Network/Extensions/ObjectAPI/ObjectAPI.MatchErrorDebug.swift @@ -0,0 +1,59 @@ +extension ObjectAPI.MatchError: DebugDescription { + + var debugDescription: String { + var description = "ObjectAPI.MatchError" + switch self { + case .matchError(_): + description += ".matchError:\n\n\(debugDetails)\n" + case .otherError(_): + description += ".otherError:\n\n\(debugDetails)\n" + } + return description + } + +} + +extension ObjectAPI.MatchError where MatchErrorType: DebugDescription, OtherErrorType: DebugDescription { + + var debugDescription: String { + var description = "ObjectAPI.MatchError" + switch self { + case .matchError(let error): + description += ".matchError: \(error.debugDescription)\n" + case .otherError(let error): + description += ".otherError: \(error.debugDescription)\n" + } + return description + } + +} + +extension ObjectAPI.MatchError: DebugDetails { + + var debugDetails: String { + let details: String + switch self { + case .matchError(let error): + details = "\(error)" + case .otherError(let error): + details = "\(error)" + } + return details + } + +} + +extension ObjectAPI.MatchError where MatchErrorType: DebugDetails, OtherErrorType: DebugDetails { + + var debugDetails: String { + let details: String + switch self { + case .matchError(let error): + details = "\(error.debugDetails)" + case .otherError(let error): + details = "\(error.debugDetails)" + } + return details + } + +} diff --git a/QuizTrain/Network/Extensions/ObjectAPI/ObjectAPI.ObjectConversionErrorDebug.swift b/QuizTrain/Network/Extensions/ObjectAPI/ObjectAPI.ObjectConversionErrorDebug.swift new file mode 100644 index 0000000..28a4d8d --- /dev/null +++ b/QuizTrain/Network/Extensions/ObjectAPI/ObjectAPI.ObjectConversionErrorDebug.swift @@ -0,0 +1,56 @@ +extension ObjectAPI.ObjectConversionError: DebugDescription { + + public var debugDescription: String { + var description = "ObjectAPI.ObjectConversionError" + switch self { + case .couldNotConvertObjectToData(_, _, _): + description += ".couldNotConvertObjectToData:\n\n\(debugDetails)\n" + case .couldNotConvertObjectsToData(_, _, _): + description += ".couldNotConvertObjectsToData:\n\n\(debugDetails)\n" + } + return description + } + +} + +extension ObjectAPI.ObjectConversionError: DebugDetails { + + public var debugDetails: String { + + let details: String + + switch self { + case .couldNotConvertObjectToData(let object, let json, let error): + details = """ + _____OBJECT_____ + + \(object) + + _____JSON_____ + + \(json) + + _____ERROR_____ + + \(error) + """ + case .couldNotConvertObjectsToData(let objects, let json, let error): + details = """ + _____OBJECTS_____ + + \(objects) + + _____JSON_____ + + \(json) + + _____ERROR_____ + + \(error) + """ + } + + return details + } + +} diff --git a/QuizTrain/Network/Extensions/ObjectAPI/ObjectAPI.RequestErrorDebug.swift b/QuizTrain/Network/Extensions/ObjectAPI/ObjectAPI.RequestErrorDebug.swift new file mode 100644 index 0000000..53fba09 --- /dev/null +++ b/QuizTrain/Network/Extensions/ObjectAPI/ObjectAPI.RequestErrorDebug.swift @@ -0,0 +1,29 @@ +extension ObjectAPI.RequestError: DebugDescription { + + public var debugDescription: String { + var description = "ObjectAPI.RequestError" + switch self { + case .apiError(_): + description += ".apiError:\n\n\(debugDetails)\n" + case .statusCodeError(_): + description += ".statusCodeError:\n\n\(debugDetails)\n" + } + return description + } + +} + +extension ObjectAPI.RequestError: DebugDetails { + + public var debugDetails: String { + let details: String + switch self { + case .apiError(let error): + details = error.debugDetails + case .statusCodeError(let error): + details = error.debugDetails + } + return details + } + +} diff --git a/QuizTrain/Network/Extensions/ObjectAPI/ObjectAPI.ServerErrorDebug.swift b/QuizTrain/Network/Extensions/ObjectAPI/ObjectAPI.ServerErrorDebug.swift new file mode 100644 index 0000000..65a7d34 --- /dev/null +++ b/QuizTrain/Network/Extensions/ObjectAPI/ObjectAPI.ServerErrorDebug.swift @@ -0,0 +1,23 @@ +extension ObjectAPI.ServerError: DebugDescription { + + public var debugDescription: String { + return "ObjectAPI.ServerError:\n\n\(debugDetails)\n" + } + +} + +extension ObjectAPI.ServerError: DebugDetails { + + public var debugDetails: String { + return """ + _____DETAILS_____ + + CODE: \(statusCode) + + MESSAGE: \(message) + + \(requestResult.debugDetails) + """ + } + +} diff --git a/QuizTrain/Network/Extensions/ObjectAPI/ObjectAPI.StatusCodeErrorDebug.swift b/QuizTrain/Network/Extensions/ObjectAPI/ObjectAPI.StatusCodeErrorDebug.swift new file mode 100644 index 0000000..8902ec7 --- /dev/null +++ b/QuizTrain/Network/Extensions/ObjectAPI/ObjectAPI.StatusCodeErrorDebug.swift @@ -0,0 +1,33 @@ +extension ObjectAPI.StatusCodeError: DebugDescription { + + public var debugDescription: String { + var description = "ObjectAPI.StatusCodeError" + switch self { + case .clientError(_): + description += ".clientError:\n\n\(debugDetails)\n" + case .otherError(_): + description += ".otherError:\n\n\(debugDetails)\n" + case .serverError(_): + description += ".serverError:\n\n\(debugDetails)\n" + } + return description + } + +} + +extension ObjectAPI.StatusCodeError: DebugDetails { + + public var debugDetails: String { + let details: String + switch self { + case .clientError(let clientError): + details = clientError.debugDetails + case .otherError(let requestResult): + details = requestResult.debugDetails + case .serverError(let serverError): + details = serverError.debugDetails + } + return details + } + +} diff --git a/QuizTrain/Network/Extensions/ObjectAPI/ObjectAPI.UpdateRequestErrorDebug.swift b/QuizTrain/Network/Extensions/ObjectAPI/ObjectAPI.UpdateRequestErrorDebug.swift new file mode 100644 index 0000000..448b76f --- /dev/null +++ b/QuizTrain/Network/Extensions/ObjectAPI/ObjectAPI.UpdateRequestErrorDebug.swift @@ -0,0 +1,37 @@ +extension ObjectAPI.UpdateRequestError: DebugDescription { + + public var debugDescription: String { + var description = "ObjectAPI.UpdateRequestError" + switch self { + case .apiError(_): + description += ".apiError:\n\n\(debugDetails)\n" + case .dataProcessingError(_): + description += ".dataProcessingError:\n\n\(debugDetails)\n" + case .objectConversionError(_): + description += ".objectConversionError:\n\n\(debugDetails)\n" + case .statusCodeError(_): + description += ".statusCodeError:\n\n\(debugDetails)\n" + } + return description + } + +} + +extension ObjectAPI.UpdateRequestError: DebugDetails { + + public var debugDetails: String { + let details: String + switch self { + case .apiError(let error): + details = error.debugDetails + case .dataProcessingError(let error): + details = error.debugDetails + case .objectConversionError(error: let error): + details = error.debugDetails + case .statusCodeError(let error): + details = error.debugDetails + } + return details + } + +} diff --git a/QuizTrain/Network/Extensions/URLRequestDebug.swift b/QuizTrain/Network/Extensions/URLRequestDebug.swift new file mode 100644 index 0000000..2a2e10c --- /dev/null +++ b/QuizTrain/Network/Extensions/URLRequestDebug.swift @@ -0,0 +1,44 @@ +extension URLRequest { + + public var httpBodyAsUTF8: String? { + guard let httpBody = self.httpBody else { + return nil + } + return String(data: httpBody, encoding: .utf8) ?? nil + } + + public var httpHeaderFieldsAsMultiLineString: String? { + return httpHeaderFieldsAsMultiLineString(omittingHeaders: []) + } + + /* + Pass |omittedHeaders| to omit any headers. For example ["AUTHORIZATION"] + if you wish to display/print to logs without leaking sensitive credentials. + Comparison is performed lowercased() per RFC 7230. + */ + public func httpHeaderFieldsAsMultiLineString(omittingHeaders omittedHeaders: [String]) -> String? { + + guard let allHTTPHeaderFields = self.allHTTPHeaderFields else { + return nil + } + + var httpHeaderFields = "" + + for (field, value) in allHTTPHeaderFields { + + // Headers are case-insensitive per RFC 7230. + guard omittedHeaders.filter({ $0.lowercased() == field.lowercased() }).count == 0 else { + continue + } + + if httpHeaderFields.count > 0 { + httpHeaderFields += "\n" + } + + httpHeaderFields += "\(field): \(value)" + } + + return httpHeaderFields + } + +} diff --git a/QuizTrain/Network/Filters/Filter.Value.swift b/QuizTrain/Network/Filters/Filter.Value.swift new file mode 100644 index 0000000..77a2ae4 --- /dev/null +++ b/QuizTrain/Network/Filters/Filter.Value.swift @@ -0,0 +1,59 @@ +extension Filter { + + /* + Value types accepted by the TestRail API used in filters. + */ + public enum Value { + case bool(Bool) + case int(Int) + case intList([Int]) + case timestamp(Date) + } + +} + +extension Filter.Value: Equatable { + + public static func==(lhs: Filter.Value, rhs: Filter.Value) -> Bool { + switch lhs { + case .bool(let lhsBool): + guard case let .bool(rhsBool) = rhs else { + return false + } + return lhsBool == rhsBool + case .int(let lhsInt): + guard case let .int(rhsInt) = rhs else { + return false + } + return lhsInt == rhsInt + case .intList(let lhsIntList): + guard case let .intList(rhsIntList) = rhs else { + return false + } + return lhsIntList == rhsIntList + case .timestamp(let lhsDate): + guard case let .timestamp(rhsDate) = rhs else { + return false + } + return lhsDate.secondsSince1970 == rhsDate.secondsSince1970 + } + } + +} + +extension Filter.Value { + + public var string: String { + switch self { + case .bool(let bool): + return String(bool ? 1 : 0) + case .int(let int): + return String(int) + case .intList(let intList): + return intList.flatMap({String($0)}).joined(separator: ",") // [38, 208, 21, 324] ---> "38,208,21,324" + case .timestamp(let date): + return String(date.secondsSince1970) // Unix Timestamp as a whole number + } + } + +} diff --git a/QuizTrain/Network/Filters/Filter.swift b/QuizTrain/Network/Filters/Filter.swift new file mode 100644 index 0000000..ac37bb7 --- /dev/null +++ b/QuizTrain/Network/Filters/Filter.swift @@ -0,0 +1,54 @@ +/* + Filter results returned by the TestRail API. + + See TestRail's API documentation for allowed name/value pairs, chaining of + filters, and general usage: http://docs.gurock.com/testrail-api2/start + */ +public struct Filter { + + public var name: String + public var value: Filter.Value + + fileprivate init(name: String, value: Filter.Value) { + self.name = name + self.value = value + } + +} + +extension Filter { + + public init(named name: String, matching value: Bool) { + self.init(name: name, value: .bool(value)) + } + + public init(named name: String, matching value: Date) { + self.init(name: name, value: .timestamp(value)) + } + + public init(named name: String, matching value: Int) { + self.init(name: name, value: .int(value)) + } + + public init(named name: String, matching value: [Int]) { + self.init(name: name, value: .intList(value)) + } + +} + +extension Filter: Equatable { + + public static func==(lhs: Filter, rhs: Filter) -> Bool { + return (lhs.name == rhs.name && + lhs.value == rhs.value) + } + +} + +extension Filter: QueryItemProvider { + + public var queryItem: URLQueryItem { + return URLQueryItem(name: name, value: value.string) + } + +} diff --git a/QuizTrain/Network/Models/Add/NewCase.swift b/QuizTrain/Network/Models/Add/NewCase.swift new file mode 100644 index 0000000..24d1f1b --- /dev/null +++ b/QuizTrain/Network/Models/Add/NewCase.swift @@ -0,0 +1,102 @@ +public struct NewCase: MutableCustomFields { + + // MARK: Properties + + public var estimate: String? + public var milestoneId: Milestone.Id? + public var priorityId: Priority.Id? + public var refs: String? + public var templateId: Template.Id? + public var title: String + public var typeId: CaseType.Id? + var customFieldsContainer = CustomFieldsContainer.empty() + + // MARK: Init + + public init(estimate: String? = nil, milestoneId: Milestone.Id? = nil, priorityId: Priority.Id? = nil, refs: String? = nil, templateId: Template.Id? = nil, title: String, typeId: CaseType.Id? = nil, customFields: JSONDictionary? = nil) { + self.estimate = estimate + self.milestoneId = milestoneId + self.priorityId = priorityId + self.refs = refs + self.templateId = templateId + self.title = title + self.typeId = typeId + if let customFields = customFields { + customFieldsContainer.customFields = customFields + } + } + +} + +// MARK: - Equatable + +extension NewCase: Equatable { + + public static func==(lhs: NewCase, rhs: NewCase) -> Bool { + return (lhs.estimate == rhs.estimate && + lhs.milestoneId == rhs.milestoneId && + lhs.priorityId == rhs.priorityId && + lhs.refs == rhs.refs && + lhs.templateId == rhs.templateId && + lhs.title == rhs.title && + lhs.typeId == rhs.typeId && + lhs.customFieldsContainer == rhs.customFieldsContainer) + } + +} + +// MARK: - JSON Keys + +extension NewCase { + + enum JSONKeys: JSONKey { + case estimate + case milestoneId = "milestone_id" + case priorityId = "priority_id" + case refs + case templateId = "template_id" + case title + case typeId = "type_id" + } + +} + +extension NewCase: AddRequestJSONKeys { + + var addRequestJSONKeys: [JSONKey] { + var keys = [JSONKeys.estimate.rawValue, + JSONKeys.milestoneId.rawValue, + JSONKeys.priorityId.rawValue, + JSONKeys.refs.rawValue, + JSONKeys.templateId.rawValue, + JSONKeys.title.rawValue, + JSONKeys.typeId.rawValue] + customFields.forEach { item in keys.append(item.key) } + return keys + } + +} + +// MARK: - Serialization + +extension NewCase: JSONSerializable { + + private var serializedProperties: JSONDictionary { + return [JSONKeys.estimate.rawValue: estimate as Any, + JSONKeys.milestoneId.rawValue: milestoneId as Any, + JSONKeys.priorityId.rawValue: priorityId as Any, + JSONKeys.refs.rawValue: refs as Any, + JSONKeys.templateId.rawValue: templateId as Any, + JSONKeys.title.rawValue: title, + JSONKeys.typeId.rawValue: typeId as Any] + } + + func serialized() -> JSONDictionary { + var json = serializedProperties + customFields.forEach { item in json[item.key] = item.value } + return json + } + +} + +extension NewCase: AddRequestJSON { } diff --git a/QuizTrain/Network/Models/Add/NewCaseResults.Result.swift b/QuizTrain/Network/Models/Add/NewCaseResults.Result.swift new file mode 100644 index 0000000..566e9b0 --- /dev/null +++ b/QuizTrain/Network/Models/Add/NewCaseResults.Result.swift @@ -0,0 +1,120 @@ +extension NewCaseResults { + + public struct Result: MutableCustomFields { + + // MARK: Properties + + public var assignedtoId: User.Id? + public var caseId: Case.Id + public var comment: String? + public var defects: String? + public var elapsed: String? + public var statusId: Status.Id? + public var version: String? + var customFieldsContainer = CustomFieldsContainer.empty() + + // MARK: Init + + public init(assignedtoId: User.Id? = nil, caseId: Case.Id, comment: String? = nil, defects: String? = nil, elapsed: String? = nil, statusId: Status.Id? = nil, version: String? = nil, customFields: JSONDictionary? = nil) { + self.assignedtoId = assignedtoId + self.caseId = caseId + self.comment = comment + self.defects = defects + self.elapsed = elapsed + self.statusId = statusId + self.version = version + if let customFields = customFields { + customFieldsContainer.customFields = customFields + } + } + + } + +} + +// MARK: - Equatable + +extension NewCaseResults.Result: Equatable { + + public static func==(lhs: NewCaseResults.Result, rhs: NewCaseResults.Result) -> Bool { + return (lhs.assignedtoId == rhs.assignedtoId && + lhs.caseId == rhs.caseId && + lhs.comment == rhs.comment && + lhs.defects == rhs.defects && + lhs.elapsed == rhs.elapsed && + lhs.statusId == rhs.statusId && + lhs.version == rhs.version && + lhs.customFieldsContainer == rhs.customFieldsContainer) + } + +} + +// MARK: - Validatable + +extension NewCaseResults.Result: Validatable { + + /* + A result is valid if it's assigned and/or commented on and/or is given a + status. + */ + var isValid: Bool { + return (assignedtoId != nil || comment != nil || statusId != nil) + } + +} + +// MARK: - JSON Keys + +extension NewCaseResults.Result { + + enum JSONKeys: JSONKey { + case assignedtoId = "assignedto_id" + case caseId = "case_id" + case comment + case defects + case elapsed + case statusId = "status_id" + case version + } + +} + +extension NewCaseResults.Result: AddRequestJSONKeys { + + var addRequestJSONKeys: [JSONKey] { + var keys = [JSONKeys.assignedtoId.rawValue, + JSONKeys.caseId.rawValue, + JSONKeys.comment.rawValue, + JSONKeys.defects.rawValue, + JSONKeys.elapsed.rawValue, + JSONKeys.statusId.rawValue, + JSONKeys.version.rawValue] + customFields.forEach { item in keys.append(item.key) } + return keys + } + +} + +// MARK: - Serialization + +extension NewCaseResults.Result: JSONSerializable { + + private var serializedProperties: JSONDictionary { + return [JSONKeys.assignedtoId.rawValue: assignedtoId as Any, + JSONKeys.caseId.rawValue: caseId, + JSONKeys.comment.rawValue: comment as Any, + JSONKeys.defects.rawValue: defects as Any, + JSONKeys.elapsed.rawValue: elapsed as Any, + JSONKeys.statusId.rawValue: statusId as Any, + JSONKeys.version.rawValue: version as Any] + } + + func serialized() -> JSONDictionary { + var json = serializedProperties + customFields.forEach { item in json[item.key] = item.value } + return json + } + +} + +extension NewCaseResults.Result: AddRequestJSON { } diff --git a/QuizTrain/Network/Models/Add/NewCaseResults.swift b/QuizTrain/Network/Models/Add/NewCaseResults.swift new file mode 100644 index 0000000..d297949 --- /dev/null +++ b/QuizTrain/Network/Models/Add/NewCaseResults.swift @@ -0,0 +1,62 @@ +/* + Use to bulk-add multiple results associated with Cases. + */ +public struct NewCaseResults { + + public var results: [NewCaseResults.Result] + + public init(results: [NewCaseResults.Result]) { + self.results = results + } + +} + +// MARK: - Equatable + +extension NewCaseResults: Equatable { + + public static func==(lhs: NewCaseResults, rhs: NewCaseResults) -> Bool { + return (lhs.results.contentsAreEqual(to: rhs.results)) + } + +} + +// MARK: - Validatable + +extension NewCaseResults: Validatable { + + var isValid: Bool { + return (results.filter({ $0.isValid == false }).count == 0) + } + +} + +// MARK: - JSON Keys + +extension NewCaseResults { + + enum JSONKeys: JSONKey { + case results + } + +} + +extension NewCaseResults: AddRequestJSONKeys { + + var addRequestJSONKeys: [JSONKey] { + return [JSONKeys.results.rawValue] + } + +} + +// MARK: - Serialization + +extension NewCaseResults: JSONSerializable { + + func serialized() -> JSONDictionary { + return [JSONKeys.results.rawValue: NewCaseResults.Result.serialized(results)] + } + +} + +extension NewCaseResults: AddRequestJSON { } diff --git a/QuizTrain/Network/Models/Add/NewConfiguration.swift b/QuizTrain/Network/Models/Add/NewConfiguration.swift new file mode 100644 index 0000000..86b9c58 --- /dev/null +++ b/QuizTrain/Network/Models/Add/NewConfiguration.swift @@ -0,0 +1,49 @@ +public struct NewConfiguration { + + public var name: String + + public init(name: String) { + self.name = name + } + +} + +// MARK: - Equatable + +extension NewConfiguration: Equatable { + + public static func==(lhs: NewConfiguration, rhs: NewConfiguration) -> Bool { + return (lhs.name == rhs.name) + } + +} + +// MARK: - JSON Keys + +extension NewConfiguration { + + enum JSONKeys: JSONKey { + case name + } + +} + +extension NewConfiguration: AddRequestJSONKeys { + + var addRequestJSONKeys: [JSONKey] { + return [JSONKeys.name.rawValue] + } + +} + +// MARK: - Serialization + +extension NewConfiguration: JSONSerializable { + + func serialized() -> JSONDictionary { + return [JSONKeys.name.rawValue: name] + } + +} + +extension NewConfiguration: AddRequestJSON { } diff --git a/QuizTrain/Network/Models/Add/NewConfigurationGroup.swift b/QuizTrain/Network/Models/Add/NewConfigurationGroup.swift new file mode 100644 index 0000000..bfbd401 --- /dev/null +++ b/QuizTrain/Network/Models/Add/NewConfigurationGroup.swift @@ -0,0 +1,49 @@ +public struct NewConfigurationGroup { + + public var name: String + + public init(name: String) { + self.name = name + } + +} + +// MARK: - Equatable + +extension NewConfigurationGroup: Equatable { + + public static func==(lhs: NewConfigurationGroup, rhs: NewConfigurationGroup) -> Bool { + return (lhs.name == rhs.name) + } + +} + +// MARK: - JSON Keys + +extension NewConfigurationGroup { + + enum JSONKeys: JSONKey { + case name + } + +} + +extension NewConfigurationGroup: AddRequestJSONKeys { + + var addRequestJSONKeys: [JSONKey] { + return [JSONKeys.name.rawValue] + } + +} + +// MARK: - Serialization + +extension NewConfigurationGroup: JSONSerializable { + + func serialized() -> JSONDictionary { + return [JSONKeys.name.rawValue: name] + } + +} + +extension NewConfigurationGroup: AddRequestJSON { } diff --git a/QuizTrain/Network/Models/Add/NewMilestone.swift b/QuizTrain/Network/Models/Add/NewMilestone.swift new file mode 100644 index 0000000..0face03 --- /dev/null +++ b/QuizTrain/Network/Models/Add/NewMilestone.swift @@ -0,0 +1,73 @@ +public struct NewMilestone { + + public var description: String? + public var dueOn: Date? + public var name: String + public var parentId: Milestone.Id? + public var startOn: Date? + + public init(description: String? = nil, dueOn: Date? = nil, name: String, parentId: Milestone.Id? = nil, startOn: Date? = nil) { + self.description = description + self.dueOn = dueOn + self.name = name + self.parentId = parentId + self.startOn = startOn + } + +} + +// MARK: - Equatable + +extension NewMilestone: Equatable { + + public static func==(lhs: NewMilestone, rhs: NewMilestone) -> Bool { + return (lhs.description == rhs.description && + lhs.dueOn?.secondsSince1970 == rhs.dueOn?.secondsSince1970 && + lhs.name == rhs.name && + lhs.parentId == rhs.parentId && + lhs.startOn?.secondsSince1970 == rhs.startOn?.secondsSince1970) + } + +} + +// MARK: - JSON Keys + +extension NewMilestone { + + enum JSONKeys: JSONKey { + case description + case dueOn = "due_on" + case name + case parentId = "parent_id" + case startOn = "start_on" + } + +} + +extension NewMilestone: AddRequestJSONKeys { + + var addRequestJSONKeys: [JSONKey] { + return [JSONKeys.description.rawValue, + JSONKeys.dueOn.rawValue, + JSONKeys.name.rawValue, + JSONKeys.parentId.rawValue, + JSONKeys.startOn.rawValue] + } + +} + +// MARK: - Serialization + +extension NewMilestone: JSONSerializable { + + func serialized() -> JSONDictionary { + return [JSONKeys.description.rawValue: description as Any, + JSONKeys.dueOn.rawValue: dueOn?.secondsSince1970 as Any, + JSONKeys.name.rawValue: name, + JSONKeys.parentId.rawValue: parentId as Any, + JSONKeys.startOn.rawValue: startOn?.secondsSince1970 as Any] + } + +} + +extension NewMilestone: AddRequestJSON { } diff --git a/QuizTrain/Network/Models/Add/NewPlan.Entry.Run.swift b/QuizTrain/Network/Models/Add/NewPlan.Entry.Run.swift new file mode 100644 index 0000000..8802f3a --- /dev/null +++ b/QuizTrain/Network/Models/Add/NewPlan.Entry.Run.swift @@ -0,0 +1,120 @@ +/* + Each NewPlan.Entry.Run can be assigned to zero or more Configuration's so long + as there is no more than one Configuration per ConfigurationGroup. For example: + + - Group1 + - ConfigurationA.id = 100 + - ConfigurationB.id = 101 + - Group2 + - ConfigurationC.id = 200 + - ConfigurationD.id = 201 + + Valid NewPlan.Entry.Run.configIds values include: + + nil // No configurations will be used. + [] // No configurations will be used. + [100] // ConfigurationA will be used. + [100, 200] // ConfigurationA and ConfigurationC will be used. + [100, 201] // ConfigurationA and ConfigurationD will be used. + + Invalid values include: + + [100, 101] // Both are from Group1. + [200, 202] // Both are from Group2. + [100, 200, 201] // 200 and 201 are from Group2. + */ +extension NewPlan.Entry { + + public struct Run { + + public var assignedtoId: User.Id? // Overrides NewPlan.Entry.assignedtoId. + public var caseIds: [Case.Id]? // Overrides NewPlan.Entry.caseIds. + public var configIds: [Configuration.Id]? // Zero, one, or many Configuration.id's. Only one Configuration.id per ConfigurationGroup is allowed. + public var description: String? // Overrides NewPlan.Entry.caseIds.description. + public var includeAll: Bool? // Overrides NewPlan.Entry.includeAll. + public var milestoneId: Milestone.Id? // Milestone for the run. + public var name: String? // Overrides NewPlan.Entry.name + public var suiteId: Suite.Id? // Overrides NewPlan.Entry.suiteId + + public init(assignedtoId: User.Id? = nil, caseIds: [Case.Id]? = nil, configIds: [Configuration.Id]? = nil, description: String? = nil, includeAll: Bool? = nil, milestoneId: Milestone.Id? = nil, name: String? = nil, suiteId: Suite.Id? = nil) { + self.assignedtoId = assignedtoId + self.caseIds = caseIds + self.configIds = configIds + self.description = description + self.includeAll = includeAll + self.milestoneId = milestoneId + self.name = name + self.suiteId = suiteId + } + + } + +} + +// MARK: - Equatable + +extension NewPlan.Entry.Run: Equatable { + + public static func==(lhs: NewPlan.Entry.Run, rhs: NewPlan.Entry.Run) -> Bool { + return (lhs.assignedtoId == rhs.assignedtoId && + lhs.caseIds?.sorted() == rhs.caseIds?.sorted() && + lhs.configIds?.sorted() == rhs.configIds?.sorted() && + lhs.description == rhs.description && + lhs.includeAll == rhs.includeAll && + lhs.milestoneId == rhs.milestoneId && + lhs.name == rhs.name && + lhs.suiteId == rhs.suiteId) + } + +} + +// MARK: - JSON Keys + +extension NewPlan.Entry.Run { + + enum JSONKeys: JSONKey { + case assignedtoId = "assignedto_id" + case caseIds = "case_ids" + case configIds = "config_ids" + case description + case includeAll = "include_all" + case milestoneId = "milestone_id" + case name + case suiteId = "suite_id" + } + +} + +extension NewPlan.Entry.Run: AddRequestJSONKeys { + + var addRequestJSONKeys: [JSONKey] { + return [JSONKeys.assignedtoId.rawValue, + JSONKeys.caseIds.rawValue, + JSONKeys.configIds.rawValue, + JSONKeys.description.rawValue, + JSONKeys.includeAll.rawValue, + JSONKeys.milestoneId.rawValue, + JSONKeys.name.rawValue, + JSONKeys.suiteId.rawValue] + } + +} + +// MARK: - Serialization + +extension NewPlan.Entry.Run: JSONSerializable { + + func serialized() -> JSONDictionary { + return [JSONKeys.assignedtoId.rawValue: assignedtoId as Any, + JSONKeys.caseIds.rawValue: caseIds as Any, + JSONKeys.configIds.rawValue: configIds as Any, + JSONKeys.description.rawValue: description as Any, + JSONKeys.includeAll.rawValue: includeAll as Any, + JSONKeys.milestoneId.rawValue: milestoneId as Any, + JSONKeys.name.rawValue: name as Any, + JSONKeys.suiteId.rawValue: suiteId as Any] + } + +} + +extension NewPlan.Entry.Run: AddRequestJSON { } diff --git a/QuizTrain/Network/Models/Add/NewPlan.Entry.swift b/QuizTrain/Network/Models/Add/NewPlan.Entry.swift new file mode 100644 index 0000000..bc1def4 --- /dev/null +++ b/QuizTrain/Network/Models/Add/NewPlan.Entry.swift @@ -0,0 +1,131 @@ +extension NewPlan { + + public struct Entry { + + public var assignedtoId: User.Id? // Default for all runs with a nil assignedtoId. + public var caseIds: [Case.Id]? // Default for all runs with a nil or empty caseIds. + public var description: String? // Default for all runs with a nil description. + public var includeAll: Bool? // Default for all runs with a nil includeAll. + public var name: String? // Default for all runs with a nil name. + public var runs: [NewPlan.Entry.Run]? // If nil TestRail will return a single run including either all tests (includeAll), or specific tests (caseIds), from the suiteId without using configurations. + public var suiteId: Suite.Id // Default for all runs with a nil suiteId. + + public init(assignedtoId: User.Id? = nil, caseIds: [Case.Id]? = nil, description: String? = nil, includeAll: Bool? = nil, name: String? = nil, runs: [NewPlan.Entry.Run]? = nil, suiteId: Suite.Id) { + self.assignedtoId = assignedtoId + self.caseIds = caseIds + self.description = description + self.includeAll = includeAll + self.name = name + self.runs = runs + self.suiteId = suiteId + } + + /* + Contains every Configuration.id from all runs. + http://docs.gurock.com/testrail-api2/reference-plans#add_plan_entry + */ + public var configIds: [Int]? { + + guard let runs = self.runs else { + return nil + } + + var ids = [Int]() + var allRunConfigIdsAreNil = true + + for run in runs { + guard let runConfigIds = run.configIds else { + continue + } + allRunConfigIdsAreNil = false + ids.append(contentsOf: runConfigIds) + } + + guard allRunConfigIdsAreNil == false else { + return nil // If every run has a nil value for configIds nil is returned match the runs. + } + + ids = Array(Set(ids)) // Remove duplicates. + ids.sort() // Maintain a consistent order for Equatable. + + return ids + } + } + +} + +// MARK: - Equatable + +extension NewPlan.Entry: Equatable { + + public static func==(lhs: NewPlan.Entry, rhs: NewPlan.Entry) -> Bool { + return (lhs.assignedtoId == rhs.assignedtoId && + lhs.caseIds?.sorted() == rhs.caseIds?.sorted() && + lhs.configIds?.sorted() == rhs.configIds?.sorted() && + lhs.description == rhs.description && + lhs.includeAll == rhs.includeAll && + lhs.name == rhs.name && + Array.contentsAreEqual(lhs.runs, rhs.runs) && + lhs.suiteId == rhs.suiteId) + } + +} + +// MARK: - JSON Keys + +extension NewPlan.Entry { + + enum JSONKeys: JSONKey { + case assignedtoId = "assignedto_id" + case caseIds = "case_ids" + case configIds = "config_ids" + case description + case includeAll = "include_all" + case name + case runs + case suiteId = "suite_id" + } + +} + +extension NewPlan.Entry: AddRequestJSONKeys { + + var addRequestJSONKeys: [JSONKey] { + return [JSONKeys.assignedtoId.rawValue, + JSONKeys.caseIds.rawValue, + JSONKeys.configIds.rawValue, + JSONKeys.description.rawValue, + JSONKeys.includeAll.rawValue, + JSONKeys.name.rawValue, + JSONKeys.runs.rawValue, + JSONKeys.suiteId.rawValue] + } + +} + +// MARK: - Serialization + +extension NewPlan.Entry: JSONSerializable { + + func serialized() -> JSONDictionary { + + let runsSerialized: [JSONDictionary]? + if let runs = self.runs { + runsSerialized = NewRun.serialized(runs) + } else { + runsSerialized = nil + } + + return [JSONKeys.assignedtoId.rawValue: assignedtoId as Any, + JSONKeys.caseIds.rawValue: caseIds as Any, + JSONKeys.configIds.rawValue: configIds as Any, + JSONKeys.description.rawValue: description as Any, + JSONKeys.includeAll.rawValue: includeAll as Any, + JSONKeys.name.rawValue: name as Any, + JSONKeys.runs.rawValue: runsSerialized as Any, + JSONKeys.suiteId.rawValue: suiteId] + } + +} + +extension NewPlan.Entry: AddRequestJSON { } diff --git a/QuizTrain/Network/Models/Add/NewPlan.swift b/QuizTrain/Network/Models/Add/NewPlan.swift new file mode 100644 index 0000000..44fe95c --- /dev/null +++ b/QuizTrain/Network/Models/Add/NewPlan.swift @@ -0,0 +1,75 @@ +public struct NewPlan { + + public var description: String? + public var entries: [NewPlan.Entry]? + public var milestoneId: Milestone.Id? + public var name: String + + public init(description: String? = nil, entries: [NewPlan.Entry]? = nil, milestoneId: Milestone.Id? = nil, name: String) { + self.description = description + self.entries = entries + self.milestoneId = milestoneId + self.name = name + } + +} + +// MARK: - Equatable + +extension NewPlan: Equatable { + + public static func==(lhs: NewPlan, rhs: NewPlan) -> Bool { + return (lhs.description == rhs.description && + Array.contentsAreEqual(lhs.entries, rhs.entries) && + lhs.milestoneId == rhs.milestoneId && + lhs.name == rhs.name) + } + +} + +// MARK: - JSON Keys + +extension NewPlan { + + enum JSONKeys: JSONKey { + case description + case entries + case milestoneId = "milestone_id" + case name + } + +} + +extension NewPlan: AddRequestJSONKeys { + + var addRequestJSONKeys: [JSONKey] { + return [JSONKeys.description.rawValue, + JSONKeys.entries.rawValue, + JSONKeys.milestoneId.rawValue, + JSONKeys.name.rawValue] + } + +} + +// MARK: - Serialization + +extension NewPlan: JSONSerializable { + + func serialized() -> JSONDictionary { + + let entriesSerialized: [JSONDictionary]? + if let entries = entries { + entriesSerialized = NewPlan.Entry.serialized(entries) + } else { + entriesSerialized = nil + } + + return [JSONKeys.description.rawValue: description as Any, + JSONKeys.entries.rawValue: entriesSerialized as Any, + JSONKeys.milestoneId.rawValue: milestoneId as Any, + JSONKeys.name.rawValue: name] + } + +} + +extension NewPlan: AddRequestJSON { } diff --git a/QuizTrain/Network/Models/Add/NewProject.swift b/QuizTrain/Network/Models/Add/NewProject.swift new file mode 100644 index 0000000..8588ab3 --- /dev/null +++ b/QuizTrain/Network/Models/Add/NewProject.swift @@ -0,0 +1,67 @@ +public struct NewProject { + + public var announcement: String? + public var name: String + public var showAnnouncement: Bool + public var suiteMode: Project.SuiteMode + + public init(announcement: String? = nil, name: String, showAnnouncement: Bool, suiteMode: Project.SuiteMode) { + self.announcement = announcement + self.name = name + self.showAnnouncement = showAnnouncement + self.suiteMode = suiteMode + } + +} + +// MARK: - Equatable + +extension NewProject: Equatable { + + public static func==(lhs: NewProject, rhs: NewProject) -> Bool { + return (lhs.announcement == rhs.announcement && + lhs.name == rhs.name && + lhs.showAnnouncement == rhs.showAnnouncement && + lhs.suiteMode == rhs.suiteMode) + } + +} + +// MARK: - JSON Keys + +extension NewProject { + + enum JSONKeys: JSONKey { + case announcement + case name + case showAnnouncement = "show_announcement" + case suiteMode = "suite_mode" + } + +} + +extension NewProject: AddRequestJSONKeys { + + var addRequestJSONKeys: [JSONKey] { + return [JSONKeys.announcement.rawValue, + JSONKeys.name.rawValue, + JSONKeys.showAnnouncement.rawValue, + JSONKeys.suiteMode.rawValue] + } + +} + +// MARK: - Serialization + +extension NewProject: JSONSerializable { + + func serialized() -> JSONDictionary { + return [JSONKeys.announcement.rawValue: announcement as Any, + JSONKeys.name.rawValue: name, + JSONKeys.showAnnouncement.rawValue: showAnnouncement, + JSONKeys.suiteMode.rawValue: suiteMode.rawValue] + } + +} + +extension NewProject: AddRequestJSON { } diff --git a/QuizTrain/Network/Models/Add/NewResult.swift b/QuizTrain/Network/Models/Add/NewResult.swift new file mode 100644 index 0000000..24c741c --- /dev/null +++ b/QuizTrain/Network/Models/Add/NewResult.swift @@ -0,0 +1,113 @@ +/* + Used to add a single result. + */ +public struct NewResult: MutableCustomFields { + + // MARK: Properties + + public var assignedtoId: User.Id? + public var comment: String? + public var defects: String? + public var elapsed: String? + public var statusId: Status.Id? + public var version: String? + var customFieldsContainer = CustomFieldsContainer.empty() + + // MARK: Init + + public init(assignedtoId: User.Id? = nil, comment: String? = nil, defects: String? = nil, elapsed: String? = nil, statusId: Status.Id? = nil, version: String? = nil, customFields: JSONDictionary? = nil) { + self.assignedtoId = assignedtoId + self.comment = comment + self.defects = defects + self.elapsed = elapsed + self.statusId = statusId + self.version = version + if let customFields = customFields { + customFieldsContainer.customFields = customFields + } + } + +} + +// MARK: - Equatable + +extension NewResult: Equatable { + + public static func==(lhs: NewResult, rhs: NewResult) -> Bool { + return (lhs.assignedtoId == rhs.assignedtoId && + lhs.comment == rhs.comment && + lhs.defects == rhs.defects && + lhs.elapsed == rhs.elapsed && + lhs.statusId == rhs.statusId && + lhs.version == rhs.version && + lhs.customFieldsContainer == rhs.customFieldsContainer) + } + +} + +// MARK: - Validatable + +extension NewResult: Validatable { + + /* + A result is valid if it's assigned and/or commented on and/or is given a + status. + */ + var isValid: Bool { + return (assignedtoId != nil || comment != nil || statusId != nil) + } + +} + +// MARK: - JSON Keys + +extension NewResult { + + enum JSONKeys: JSONKey { + case assignedtoId = "assignedto_id" + case comment + case defects + case elapsed + case statusId = "status_id" + case version + } + +} + +extension NewResult: AddRequestJSONKeys { + + var addRequestJSONKeys: [JSONKey] { + var keys = [JSONKeys.assignedtoId.rawValue, + JSONKeys.comment.rawValue, + JSONKeys.defects.rawValue, + JSONKeys.elapsed.rawValue, + JSONKeys.statusId.rawValue, + JSONKeys.version.rawValue] + customFields.forEach { item in keys.append(item.key) } + return keys + } + +} + +// MARK: - Serialization + +extension NewResult: JSONSerializable { + + private var serializedProperties: JSONDictionary { + return [JSONKeys.assignedtoId.rawValue: assignedtoId as Any, + JSONKeys.comment.rawValue: comment as Any, + JSONKeys.defects.rawValue: defects as Any, + JSONKeys.elapsed.rawValue: elapsed as Any, + JSONKeys.statusId.rawValue: statusId as Any, + JSONKeys.version.rawValue: version as Any] + } + + func serialized() -> JSONDictionary { + var json = serializedProperties + customFields.forEach { item in json[item.key] = item.value } + return json + } + +} + +extension NewResult: AddRequestJSON { } diff --git a/QuizTrain/Network/Models/Add/NewRun.swift b/QuizTrain/Network/Models/Add/NewRun.swift new file mode 100644 index 0000000..ea08a23 --- /dev/null +++ b/QuizTrain/Network/Models/Add/NewRun.swift @@ -0,0 +1,85 @@ +public struct NewRun { + + public var assignedtoId: User.Id? + public var caseIds: [Case.Id]? + public var description: String? + public var includeAll: Bool? + public var milestoneId: Milestone.Id? + public var name: String + public var suiteId: Suite.Id? // Optional if project is running in single suite mode, otherwise required. + + public init(assignedtoId: User.Id? = nil, caseIds: [Case.Id]? = nil, description: String? = nil, includeAll: Bool? = nil, milestoneId: Milestone.Id? = nil, name: String, suiteId: Suite.Id? = nil) { + self.assignedtoId = assignedtoId + self.caseIds = caseIds + self.description = description + self.includeAll = includeAll + self.milestoneId = milestoneId + self.name = name + self.suiteId = suiteId + } + +} + +// MARK: - Equatable + +extension NewRun: Equatable { + + public static func==(lhs: NewRun, rhs: NewRun) -> Bool { + return (lhs.assignedtoId == rhs.assignedtoId && + lhs.caseIds?.sorted() == rhs.caseIds?.sorted() && + lhs.description == rhs.description && + lhs.includeAll == rhs.includeAll && + lhs.milestoneId == rhs.milestoneId && + lhs.name == rhs.name && + lhs.suiteId == rhs.suiteId) + } + +} + +// MARK: - JSON Keys + +extension NewRun { + + enum JSONKeys: JSONKey { + case assignedtoId = "assignedto_id" + case caseIds = "case_ids" + case description + case includeAll = "include_all" + case milestoneId = "milestone_id" + case name + case suiteId = "suite_id" + } + +} + +extension NewRun: AddRequestJSONKeys { + + var addRequestJSONKeys: [JSONKey] { + return [JSONKeys.assignedtoId.rawValue, + JSONKeys.caseIds.rawValue, + JSONKeys.description.rawValue, + JSONKeys.includeAll.rawValue, + JSONKeys.milestoneId.rawValue, + JSONKeys.name.rawValue, + JSONKeys.suiteId.rawValue] + } + +} + +// MARK: - Serialization + +extension NewRun: JSONSerializable { + + func serialized() -> JSONDictionary { + return [JSONKeys.assignedtoId.rawValue: assignedtoId as Any, + JSONKeys.caseIds.rawValue: caseIds as Any, + JSONKeys.description.rawValue: description as Any, + JSONKeys.includeAll.rawValue: includeAll as Any, + JSONKeys.milestoneId.rawValue: milestoneId as Any, + JSONKeys.name.rawValue: name, + JSONKeys.suiteId.rawValue: suiteId as Any] + } + +} + +extension NewRun: AddRequestJSON { } diff --git a/QuizTrain/Network/Models/Add/NewSection.swift b/QuizTrain/Network/Models/Add/NewSection.swift new file mode 100644 index 0000000..e094ed2 --- /dev/null +++ b/QuizTrain/Network/Models/Add/NewSection.swift @@ -0,0 +1,67 @@ +public struct NewSection { + + public var description: String? + public var name: String + public var parentId: Section.Id? + public var suiteId: Suite.Id? // Optional/ignored if project is running in single suite mode, otherwise required. + + public init(description: String? = nil, name: String, parentId: Section.Id? = nil, suiteId: Suite.Id? = nil) { + self.description = description + self.name = name + self.parentId = parentId + self.suiteId = suiteId + } + +} + +// MARK: - Equatable + +extension NewSection: Equatable { + + public static func==(lhs: NewSection, rhs: NewSection) -> Bool { + return (lhs.description == rhs.description && + lhs.name == rhs.name && + lhs.parentId == rhs.parentId && + lhs.suiteId == rhs.suiteId) + } + +} + +// MARK: - JSON Keys + +extension NewSection { + + enum JSONKeys: JSONKey { + case description + case name + case parentId = "parent_id" + case suiteId = "suite_id" + } + +} + +extension NewSection: AddRequestJSONKeys { + + var addRequestJSONKeys: [JSONKey] { + return [JSONKeys.description.rawValue, + JSONKeys.name.rawValue, + JSONKeys.parentId.rawValue, + JSONKeys.suiteId.rawValue] + } + +} + +// MARK: - Serialization + +extension NewSection: JSONSerializable { + + func serialized() -> JSONDictionary { + return [JSONKeys.description.rawValue: description as Any, + JSONKeys.name.rawValue: name, + JSONKeys.parentId.rawValue: parentId as Any, + JSONKeys.suiteId.rawValue: suiteId as Any] + } + +} + +extension NewSection: AddRequestJSON { } diff --git a/QuizTrain/Network/Models/Add/NewSuite.swift b/QuizTrain/Network/Models/Add/NewSuite.swift new file mode 100644 index 0000000..3d04d0b --- /dev/null +++ b/QuizTrain/Network/Models/Add/NewSuite.swift @@ -0,0 +1,55 @@ +public struct NewSuite { + + public var description: String? + public var name: String + + public init(description: String? = nil, name: String) { + self.description = description + self.name = name + } + +} + +// MARK: - Equatable + +extension NewSuite: Equatable { + + public static func==(lhs: NewSuite, rhs: NewSuite) -> Bool { + return (lhs.description == rhs.description && + lhs.name == rhs.name) + } + +} + +// MARK: - JSON Keys + +extension NewSuite { + + enum JSONKeys: JSONKey { + case description + case name + } + +} + +extension NewSuite: AddRequestJSONKeys { + + var addRequestJSONKeys: [JSONKey] { + return [JSONKeys.description.rawValue, + JSONKeys.name.rawValue] + } + +} + +// MARK: - Serialization + +extension NewSuite: JSONSerializable { + + func serialized() -> JSONDictionary { + return [JSONKeys.description.rawValue: description as Any, + JSONKeys.name.rawValue: name] + } + +} + +extension NewSuite: AddRequestJSON { } diff --git a/QuizTrain/Network/Models/Add/NewTestResults.Result.swift b/QuizTrain/Network/Models/Add/NewTestResults.Result.swift new file mode 100644 index 0000000..6f38e65 --- /dev/null +++ b/QuizTrain/Network/Models/Add/NewTestResults.Result.swift @@ -0,0 +1,120 @@ +extension NewTestResults { + + public struct Result: MutableCustomFields { + + // MARK: Properties + + public var assignedtoId: User.Id? + public var comment: String? + public var defects: String? + public var elapsed: String? + public var statusId: Status.Id? + public var testId: Test.Id + public var version: String? + var customFieldsContainer = CustomFieldsContainer.empty() + + // MARK: Init + + public init(assignedtoId: User.Id? = nil, comment: String? = nil, defects: String? = nil, elapsed: String? = nil, statusId: Status.Id? = nil, testId: Test.Id, version: String? = nil, customFields: JSONDictionary? = nil) { + self.assignedtoId = assignedtoId + self.comment = comment + self.defects = defects + self.elapsed = elapsed + self.statusId = statusId + self.testId = testId + self.version = version + if let customFields = customFields { + customFieldsContainer.customFields = customFields + } + } + + } + +} + +// MARK: - Equatable + +extension NewTestResults.Result: Equatable { + + public static func==(lhs: NewTestResults.Result, rhs: NewTestResults.Result) -> Bool { + return (lhs.assignedtoId == rhs.assignedtoId && + lhs.comment == rhs.comment && + lhs.defects == rhs.defects && + lhs.elapsed == rhs.elapsed && + lhs.statusId == rhs.statusId && + lhs.testId == rhs.testId && + lhs.version == rhs.version && + lhs.customFieldsContainer == rhs.customFieldsContainer) + } + +} + +// MARK: - Validatable + +extension NewTestResults.Result: Validatable { + + /* + A result is valid if it's assigned and/or commented on and/or is given a + status. + */ + var isValid: Bool { + return (assignedtoId != nil || comment != nil || statusId != nil) + } + +} + +// MARK: - JSON Keys + +extension NewTestResults.Result { + + enum JSONKeys: JSONKey { + case assignedtoId = "assignedto_id" + case comment + case defects + case elapsed + case statusId = "status_id" + case testId = "test_id" + case version + } + +} + +extension NewTestResults.Result: AddRequestJSONKeys { + + var addRequestJSONKeys: [JSONKey] { + var keys = [JSONKeys.assignedtoId.rawValue, + JSONKeys.comment.rawValue, + JSONKeys.defects.rawValue, + JSONKeys.elapsed.rawValue, + JSONKeys.statusId.rawValue, + JSONKeys.testId.rawValue, + JSONKeys.version.rawValue] + customFields.forEach { item in keys.append(item.key) } + return keys + } + +} + +// MARK: - Serialization + +extension NewTestResults.Result: JSONSerializable { + + private var serializedProperties: JSONDictionary { + return [JSONKeys.assignedtoId.rawValue: assignedtoId as Any, + JSONKeys.comment.rawValue: comment as Any, + JSONKeys.defects.rawValue: defects as Any, + JSONKeys.elapsed.rawValue: elapsed as Any, + JSONKeys.statusId.rawValue: statusId as Any, + JSONKeys.testId.rawValue: testId, + JSONKeys.version.rawValue: version as Any] + } + + func serialized() -> JSONDictionary { + var json = serializedProperties + customFields.forEach { item in json[item.key] = item.value } + return json + } + +} + +extension NewTestResults.Result: AddRequestJSON { } diff --git a/QuizTrain/Network/Models/Add/NewTestResults.swift b/QuizTrain/Network/Models/Add/NewTestResults.swift new file mode 100644 index 0000000..b38271a --- /dev/null +++ b/QuizTrain/Network/Models/Add/NewTestResults.swift @@ -0,0 +1,62 @@ +/* + Use to bulk-add multiple results associated with Tests. + */ +public struct NewTestResults { + + public var results: [NewTestResults.Result] + + public init(results: [NewTestResults.Result]) { + self.results = results + } + +} + +// MARK: - Equatable + +extension NewTestResults: Equatable { + + public static func==(lhs: NewTestResults, rhs: NewTestResults) -> Bool { + return (lhs.results.contentsAreEqual(to: rhs.results)) + } + +} + +// MARK: - Validatable + +extension NewTestResults: Validatable { + + var isValid: Bool { + return (results.filter({ $0.isValid == false }).count == 0) + } + +} + +// MARK: - JSON Keys + +extension NewTestResults { + + enum JSONKeys: JSONKey { + case results + } + +} + +extension NewTestResults: AddRequestJSONKeys { + + var addRequestJSONKeys: [JSONKey] { + return [JSONKeys.results.rawValue] + } + +} + +// MARK: - Serialization + +extension NewTestResults: JSONSerializable { + + func serialized() -> JSONDictionary { + return [JSONKeys.results.rawValue: NewTestResults.Result.serialized(results)] + } + +} + +extension NewTestResults: AddRequestJSON { } diff --git a/QuizTrain/Network/Models/Update/UpdatePlanEntryRuns.swift b/QuizTrain/Network/Models/Update/UpdatePlanEntryRuns.swift new file mode 100644 index 0000000..55e45d8 --- /dev/null +++ b/QuizTrain/Network/Models/Update/UpdatePlanEntryRuns.swift @@ -0,0 +1,70 @@ +/* + Applies to all Run's within Plan.Entry.runs. + */ +public struct UpdatePlanEntryRuns { + + public var assignedtoId: User.Id? + public var caseIds: [Case.Id]? + public var description: String? + public var includeAll: Bool? + + public init(assignedtoId: User.Id? = nil, caseIds: [Case.Id]? = nil, description: String? = nil, includeAll: Bool? = nil) { + self.assignedtoId = assignedtoId + self.caseIds = caseIds + self.description = description + self.includeAll = includeAll + } + +} + +// MARK: - Equatable + +extension UpdatePlanEntryRuns: Equatable { + + public static func==(lhs: UpdatePlanEntryRuns, rhs: UpdatePlanEntryRuns) -> Bool { + return (lhs.assignedtoId == rhs.assignedtoId && + lhs.caseIds == rhs.caseIds && + lhs.description == rhs.description && + lhs.includeAll == rhs.includeAll) + } + +} + +// MARK: - JSON Keys + +extension UpdatePlanEntryRuns { + + enum JSONKeys: JSONKey { + case assignedtoId = "assignedto_id" + case caseIds = "case_ids" + case description + case includeAll = "include_all" + } + +} + +extension UpdatePlanEntryRuns: UpdateRequestJSONKeys { + + var updateRequestJSONKeys: [JSONKey] { + return [JSONKeys.assignedtoId.rawValue, + JSONKeys.caseIds.rawValue, + JSONKeys.description.rawValue, + JSONKeys.includeAll.rawValue] + } + +} + +// MARK: - Serialization + +extension UpdatePlanEntryRuns: JSONSerializable { + + func serialized() -> JSONDictionary { + return [JSONKeys.assignedtoId.rawValue: assignedtoId as Any, + JSONKeys.caseIds.rawValue: caseIds as Any, + JSONKeys.description.rawValue: description as Any, + JSONKeys.includeAll.rawValue: includeAll as Any] + } + +} + +extension UpdatePlanEntryRuns: UpdateRequestJSON { } diff --git a/QuizTrain/Network/ObjectAPI.swift b/QuizTrain/Network/ObjectAPI.swift new file mode 100644 index 0000000..c26de74 --- /dev/null +++ b/QuizTrain/Network/ObjectAPI.swift @@ -0,0 +1,3032 @@ +import Foundation + +// MARK: - Properties & Init + +/* + Mid-level interface to TestRail's API. This class builds on top of API by + working with model objects and provides 429 Too Many Requests retry handling + when rate limits are reached when handle429TooManyRequestErrors is true. It + abstracts you away from having to deal with Data, JSON, and serialization & + deserialization of objects. In some cases it handles async API calls where a + single call is not possible. A completion handler is called either with a + succeeded or failed outcome on an undefined queue. + + Succeeded outcomes: + + - Add requests return a newly created single or multiple objects. + - Close requests return a newly created closed object. + - Delete requests return nil. + - Get requests return a single or multiple objects. They can be nil in some + cases. + - Update requests return a newly updated object. + + Failed outcomes: + + Failed outcomes return an error or ErrorContainer if multiple errors can occur. + See "Errors" extension for details. The consumer of this class is responsible + for handling all errors. However this class will handle 429 Too Many Request + errors automatically when handle429TooManyRequestErrors is true. + + NOTE: Certain API calls will return nil or partial-data for some object + properties. For example getPlans(...) will always return nil for Plan.entries, + but getPlan(...) will not if the plan has any entries. While this type of + behavior is documented here when known, the single source of truth will always + be in TestRail's official API docs: http://docs.gurock.com/testrail-api2/start + */ +final public class ObjectAPI { + + public let api: API + + fileprivate let asyncRequestQueue: OperationQueue + + public var asyncRequestQueueQoS: QualityOfService { + get { return asyncRequestQueue.qualityOfService } + set { asyncRequestQueue.qualityOfService = newValue } + } + + public var asyncRequestQueueMaxConcurrentOperationCount: NSInteger { + get { return asyncRequestQueue.maxConcurrentOperationCount } + set { asyncRequestQueue.maxConcurrentOperationCount = newValue } + } + + public var handle429TooManyRequestErrors: Bool = true + fileprivate let retryQueue: DispatchQueue + + public init(api: API, retryQueueQoS: DispatchQoS = .`default`) { + self.api = api + self.asyncRequestQueue = OperationQueue() + self.asyncRequestQueue.name = "ObjectAPI.asyncRequestQueue" + self.retryQueue = DispatchQueue(label: "ObjectAPI.retryQueue", qos: retryQueueQoS) + } + + public convenience init(username: String, secret: String, hostname: String, port: Int = 443, scheme: String = "https", retryQueueQoS: DispatchQoS = .`default`) { + let api = API(username: username, secret: secret, hostname: hostname, port: port, scheme: scheme) + self.init(api: api, retryQueueQoS: retryQueueQoS) + } + + deinit { + asyncRequestQueue.cancelAllOperations() + } + +} + +// MARK: - Errors + +extension ObjectAPI { + + // MARK: Status Code + + public struct ClientError { + public var statusCode: Int { return requestResult.response.statusCode } // 400...499 + public let message: String // Body or error message extracted from response. Could be empty. + public let requestResult: API.RequestResult + } + + public struct ServerError { + public var statusCode: Int { return requestResult.response.statusCode } // 500...599 + public let message: String // Body extracted from response. Could be empty. + public let requestResult: API.RequestResult + } + + // MARK: Base + + public enum StatusCodeError: Error { + case clientError(ClientError) // 400...499 + case serverError(ServerError) // 500...599 + case otherError(API.RequestResult) // Any other status code which is not any of the above or 200...299. + } + + public enum DataProcessingError: Error { + case couldNotConvertDataToJSON(data: Data, error: Error) // TestRail returned data which is not valid UTF8 encoded JSON. + case invalidJSONFormat(json: Any) // TestRail returned valid UTF8 encoded JSON but it could not be converted into an expected JSON format. + case couldNotDeserializeFromJSON(objectType: Any.Type, json: Any) // TestRail returned valid UTF8 encoded JSON but it could not be deserialized into an object or objects. This might mean a model needs to be updated. + } + + public enum ObjectConversionError: Error { + case couldNotConvertObjectToData(object: Any, json: JSONDictionary, error: Error) // An error occurred converting the object to Data from its JSON representation. + case couldNotConvertObjectsToData(objects: [Any], json: JSONDictionary, error: Error) // An error occurred converting the objects to Data from their JSON representations. + } + + // MARK: Low Level + + public enum RequestError: Error { + case apiError(API.RequestError) + case statusCodeError(StatusCodeError) + } + + public enum DataRequestError: Error { + case apiError(API.RequestError) + case statusCodeError(StatusCodeError) + case dataProcessingError(DataProcessingError) + } + + public enum UpdateRequestError: Error { + case objectConversionError(ObjectConversionError) + case apiError(API.RequestError) + case statusCodeError(StatusCodeError) + case dataProcessingError(DataProcessingError) + } + + // MARK: High Level + + public typealias AddError = UpdateRequestError + public typealias CloseError = DataRequestError + public typealias DeleteError = RequestError + public typealias GetError = DataRequestError + public typealias UpdateError = UpdateRequestError + + // MARK: Matching + + public enum MatchError: Error { + case matchError(MatchErrorType) + case otherError(OtherErrorType) + } + +} + +// MARK: - API.RequestOutcome Handling + +extension ObjectAPI { + + // MARK: RequestOutcome + + private typealias RequestOutcome = Outcome + + /* + Converts an API.RequestOutcome into a more strongly-defined RequestOutcome. + */ + private static func requestOutcome(from apiRequestOutcome: API.RequestOutcome) -> RequestOutcome { + + let requestResult: API.RequestResult + switch apiRequestOutcome { + case .succeeded(let aRequestResult): + requestResult = aRequestResult + case .failed(let error): + return .failed(.apiError(error)) + } + + switch requestResult.response.statusCode { + case 200...299: + return .succeeded(requestResult) + case 400...499: + let message: String + let json = try? JSONSerialization.jsonObject(with: requestResult.data, options: []) + if let json = json as? JSONDictionary, let jsonError = json["error"] as? String { // TestRail typically returns {"error": "Some error message."} as JSON. + message = jsonError + } else { + message = String(data: requestResult.data, encoding: .utf8) ?? "" // If no JSON error can be extracted attempt to decode as UTF8. + } + let clientError = ClientError(message: message, requestResult: requestResult) + return .failed(.statusCodeError(.clientError(clientError))) + case 500...599: + let message = String(data: requestResult.data, encoding: .utf8) ?? "" + let serverError = ServerError(message: message, requestResult: requestResult) + return .failed(.statusCodeError(.serverError(serverError))) + default: + return .failed(.statusCodeError(.otherError(requestResult))) + } + } + + // MARK: JSON + + private typealias JSONOutcome = Outcome + + /* + Attempts to extract JSON from `apiRequestOutcome` and return it. If + extraction fails a failed JSONOutcome is returned. + */ + private static func json(from apiRequestOutcome: API.RequestOutcome) -> JSONOutcome { + + let requestOutcome = ObjectAPI.requestOutcome(from: apiRequestOutcome) + + let data: Data + switch requestOutcome { + case .failed(let error): + switch error { + case .apiError(let apiError): + return .failed(.apiError(apiError)) + case .statusCodeError(let statusCodeError): + return .failed(.statusCodeError(statusCodeError)) + } + case .succeeded(let requestResult): + data = requestResult.data + } + + let json: Any + do { + json = try JSONSerialization.jsonObject(with: data) + } catch { + return .failed(.dataProcessingError(.couldNotConvertDataToJSON(data: data, error: error))) + } + + return .succeeded(json) + } + + // MARK: Object(s) + + private typealias ObjectOutcome = Outcome + private typealias ObjectsOutcome = Outcome<[ObjectType], DataRequestError> + + /* + Attempts to deserialize and return an object of ObjectType from + `apiRequestOutcome`. If deserializing fails a failed ObjectOutcome is + returned. + */ + private static func object(from apiRequestOutcome: API.RequestOutcome) -> ObjectOutcome { + + let jsonOutcome = ObjectAPI.json(from: apiRequestOutcome) + + let json: Any + switch jsonOutcome { + case .failed(let error): + return .failed(error) + case .succeeded(let aJson): + json = aJson + } + + guard let jsonDictionary = json as? JSONDictionary else { + return .failed(.dataProcessingError(.invalidJSONFormat(json: json))) + } + + guard let object: ObjectType = ObjectType.deserialized(jsonDictionary) else { + return .failed(.dataProcessingError(.couldNotDeserializeFromJSON(objectType: ObjectType.self, json: jsonDictionary))) + } + + return .succeeded(object) + } + + /* + Attempts to deserialize and return 0+ objects of ObjectType from + `apiRequestOutcome`. If deserializing fails a failed ObjectsOutcome is + returned. + */ + private static func object(from apiRequestOutcome: API.RequestOutcome) -> ObjectsOutcome { + + let jsonOutcome = ObjectAPI.json(from: apiRequestOutcome) + + let json: Any + switch jsonOutcome { + case .failed(let error): + return .failed(error) + case .succeeded(let aJson): + json = aJson + } + + guard let jsonArray = json as? [JSONDictionary] else { + return .failed(.dataProcessingError(.invalidJSONFormat(json: json))) + } + + guard let objects: [ObjectType] = ObjectType.deserialized(jsonArray) else { + return .failed(.dataProcessingError(.couldNotDeserializeFromJSON(objectType: ObjectType.self, json: jsonArray))) + } + + return .succeeded(objects) + } + + // MARK: Rate Limit + + /* + Helper for processing 429 Too Many Requests errors. + */ + private struct RateLimitReached { + + let retryAfter: UInt + + init?(clientError: ClientError) { + guard clientError.statusCode == 429 else { + return nil + } + self.retryAfter = clientError.requestResult.response.allHeaderFields["Retry-After"] as? UInt ?? 5 // Default to 5 seconds if Retry-After is missing. + } + + } + + // MARK: Processing + + /* + These methods handle processing an API.RequestOutcome calling one of the + handler closures when complete. Swift inference auto-detects which + process(...) method to call. + + The value passed to a Outcome.succeeded and .failed cases varies. See + method comments for details. + + If handle429TooManyRequestErrors is true and the API returned a 429 Too + Many Requests error, the `retryHandler` will be called after waiting the + API-specified wait time. The caller of a method should re-call itself in + this closure passing itself the same arguments it received for proper retry + behavior. + + In all other cases the `completionHandler` is called when complete. + */ + + /* + Process API.RequestOutcome's to delete an object. + + - Outcome.succeeded receives an API.DataResponse. + - Outcome.failed receives a RequestError. + */ + fileprivate func process(_ apiRequestOutcome: API.RequestOutcome, retryHandler: @escaping (() -> Void), completionHandler: @escaping (Outcome) -> Void) { + + let requestOutcome = ObjectAPI.requestOutcome(from: apiRequestOutcome) + + switch requestOutcome { + case .failed(let error): + switch error { + case .statusCodeError(.clientError(let clientError)): + if let rateLimitReached = RateLimitReached(clientError: clientError), handle429TooManyRequestErrors { + retryQueue.asyncAfter(deadline: DispatchTime.now() + Double(rateLimitReached.retryAfter)) { + retryHandler() + } + } else { + completionHandler(.failed(error)) + } + default: + completionHandler(.failed(error)) + } + case .succeeded(let success): + completionHandler(.succeeded(success)) + } + } + + /* + Process API.RequestOutcome's to get a single object. + + - Outcome.succeeded receives an object deserialized from + API.RequestOutcome. + - Outcome.failed receives a DataRequestError. + */ + fileprivate func process(_ apiRequestOutcome: API.RequestOutcome, retryHandler: @escaping (() -> Void), completionHandler: @escaping (Outcome) -> Void) { + + let objectOutcome: ObjectOutcome = ObjectAPI.object(from: apiRequestOutcome) + + switch objectOutcome { + case .failed(let error): + switch error { + case .statusCodeError(.clientError(let clientError)): + if let rateLimitReached = RateLimitReached(clientError: clientError), handle429TooManyRequestErrors { + retryQueue.asyncAfter(deadline: DispatchTime.now() + Double(rateLimitReached.retryAfter)) { + retryHandler() + } + } else { + completionHandler(.failed(error)) + } + default: + completionHandler(.failed(error)) + } + case .succeeded(let success): + completionHandler(.succeeded(success)) + } + } + + /* + Process API.RequestOutcome's to get multiple objects. + + - Outcome.succeeded receives 0+ object(s) deserialized from + API.RequestOutcome. + - Outcome.failed receives a DataRequestError. + */ + fileprivate func process(_ apiRequestOutcome: API.RequestOutcome, retryHandler: @escaping (() -> Void), completionHandler: @escaping (Outcome<[ObjectType], DataRequestError>) -> Void) { + + let objectOutcome: ObjectOutcome<[ObjectType]> = ObjectAPI.object(from: apiRequestOutcome) + + switch objectOutcome { + case .failed(let error): + switch error { + case .statusCodeError(.clientError(let clientError)): + if let rateLimitReached = RateLimitReached(clientError: clientError), handle429TooManyRequestErrors { + retryQueue.asyncAfter(deadline: DispatchTime.now() + Double(rateLimitReached.retryAfter)) { + retryHandler() + } + } else { + completionHandler(.failed(error)) + } + default: + completionHandler(.failed(error)) + } + case .succeeded(let success): + completionHandler(.succeeded(success)) + } + } + + /* + Process API.RequestOutcome's to add or update an object returning a new + added/updated object if successful. + + - Outcome.succeeded receives an object deserialized from + API.RequestOutcome. + - Outcome.failed receives an UpdateRequestError. + */ + fileprivate func process(_ apiRequestOutcome: API.RequestOutcome, retryHandler: @escaping (() -> Void), completionHandler: @escaping (Outcome) -> Void) { + + let objectOutcome: ObjectOutcome = ObjectAPI.object(from: apiRequestOutcome) + + switch objectOutcome { + case .failed(let error): + switch error { + case .apiError(let apiError): + completionHandler(.failed(.apiError(apiError))) + case .statusCodeError(.clientError(let clientError)): + if let rateLimitReached = RateLimitReached(clientError: clientError), handle429TooManyRequestErrors { + retryQueue.asyncAfter(deadline: DispatchTime.now() + Double(rateLimitReached.retryAfter)) { + retryHandler() + } + } else { + completionHandler(.failed(.statusCodeError(.clientError(clientError)))) + } + case .statusCodeError(let statusCodeError): + completionHandler(.failed(.statusCodeError(statusCodeError))) + case .dataProcessingError(let dataProcessingError): + completionHandler(.failed(.dataProcessingError(dataProcessingError))) + } + case .succeeded(let success): + completionHandler(.succeeded(success)) + } + } + + /* + Process API.RequestOutcome's to add or update multiple objects returning an + array of the added/updated objects if successful. + + - Outcome.succeeded receives an array of objects deserialized from + API.RequestOutcome. + - Outcome.failed receives an UpdateRequestError. + */ + fileprivate func process(_ apiRequestOutcome: API.RequestOutcome, retryHandler: @escaping (() -> Void), completionHandler: @escaping (Outcome<[ObjectType], UpdateRequestError>) -> Void) { + + let objectOutcome: ObjectOutcome<[ObjectType]> = ObjectAPI.object(from: apiRequestOutcome) + + switch objectOutcome { + case .failed(let error): + switch error { + case .apiError(let apiError): + completionHandler(.failed(.apiError(apiError))) + case .statusCodeError(.clientError(let clientError)): + if let rateLimitReached = RateLimitReached(clientError: clientError), handle429TooManyRequestErrors { + retryQueue.asyncAfter(deadline: DispatchTime.now() + Double(rateLimitReached.retryAfter)) { + retryHandler() + } + } else { + completionHandler(.failed(.statusCodeError(.clientError(clientError)))) + } + case .statusCodeError(let statusCodeError): + completionHandler(.failed(.statusCodeError(statusCodeError))) + case .dataProcessingError(let dataProcessingError): + completionHandler(.failed(.dataProcessingError(dataProcessingError))) + } + case .succeeded(let success): + completionHandler(.succeeded(success)) + } + } +} + +// MARK: - Object to JSON to Data + +extension ObjectAPI { + + fileprivate typealias DataOutcome = Outcome + + fileprivate func data(from object: AddRequestJSON) -> DataOutcome { + + let json = object.addRequestJSON + + let data: Data + do { + data = try JSONSerialization.data(withJSONObject: json, options: []) + } catch { + return .failed(.couldNotConvertObjectToData(object: object, json: json, error: error)) + } + + return .succeeded(data) + } + + fileprivate func data(from object: UpdateRequestJSON) -> DataOutcome { + + let json = object.updateRequestJSON + + let data: Data + do { + data = try JSONSerialization.data(withJSONObject: json, options: []) + } catch { + return .failed(.couldNotConvertObjectToData(object: object, json: json, error: error)) + } + + return .succeeded(data) + } + + /* + Combines JSON from all `objects` and returns Data from it. Any overlapping + JSON keys are overriden by the last-most object in `objects`. + */ + fileprivate func data(from objects: [UpdateRequestJSON]) -> DataOutcome { + + var json: JSONDictionary = [:] + for object in objects { + let objectJson = object.updateRequestJSON + objectJson.forEach { item in json[item.key] = item.value } + } + + let data: Data + do { + data = try JSONSerialization.data(withJSONObject: json, options: []) + } catch { + return .failed(.couldNotConvertObjectsToData(objects: objects, json: json, error: error)) + } + + return .succeeded(data) + } + +} + +// MARK: - Objects (Fileprivate Outcomes) + +extension ObjectAPI { + + // MARK: - ConfigurationGroup + + /* + Queries all Project's matching |projectIds| asynchronously to get their + ConfigurationGroups. Passes a dictionary of outcomes to the + completionHandler mapping projectIds to each outcome when complete: + + [projectId1: outcome1, projectId2: outcome2, ...] + + The totality of outcomes can be in three states: + + 1. All outcomes succeeded. + 2. All outcomes failed. + 3. Outcomes are a mixture of succeeded/failed states. + + It is up to the consumer of this method to determine how to handle #2/3. + */ + fileprivate func getConfigurationGroups(inProjectsWithIds projectIds: Set, completionHandler: @escaping ([Project.Id: Outcome<[ConfigurationGroup], GetError>]) -> Void) { + + // For every project create an operation to get all of its configuration groups. + var operations = [GetConfigurationGroupsOperation]() + for projectId in projectIds { + let operation = GetConfigurationGroupsOperation(api: self, projectId: projectId) + operations.append(operation) + } + + let completedOperation = BlockOperation { + + // Nothing should be cancelled. The asyncRequestQueue is fileprivate + // and will only be cancelled if the ObjectAPI is deinit'ed. + guard operations.filter({ $0.isCancelled == true }).count == 0 else { + return + } + + // Everything must have finished. + guard operations.filter({ $0.isFinished == true }).count == operations.count else { + return + } + + var allOutcomes = [Project.Id: Outcome<[ConfigurationGroup], GetError>]() + for operation in operations { + allOutcomes[operation.projectId] = operation.outcome + } + + completionHandler(allOutcomes) + } + + for operation in operations { + completedOperation.addDependency(operation) + } + + var allOperations: [Operation] = operations + allOperations.append(completedOperation) + + asyncRequestQueue.addOperations(allOperations, waitUntilFinished: false) + } + + // MARK: - Project + + /* + Asynchronously GETs each project in |projectIds|. Passes a dictionary of + outcomes to the completionHandler mapping projectIds to each outcome when + complete: + + [projectId1: outcome1, projectId2: outcome2, ...] + + The totality of outcomes can be in three states: + + 1. All outcomes succeeded. + 2. All outcomes failed. + 3. Outcomes are a mixture of succeeded/failed states. + + It is up to the consumer of this method to determine how to handle #2/3. + + 403 failure errors indicate an authorization issue (you do not have at + least "Read-only" access to that project). It is possible for a project to + have "No Access" set preventing anyone from accessing it. + */ + fileprivate func getProjects(_ projectIds: Set, completionHandler: @escaping ([Project.Id: Outcome]) -> Void) { + + // Create an operation to get each project. + var operations = [GetProjectOperation]() + for projectId in projectIds { + let operation = GetProjectOperation(api: self, projectId: projectId) + operations.append(operation) + } + + let completedOperation = BlockOperation { + + // Nothing should be cancelled. The asyncRequestQueue is fileprivate + // and will only be cancelled if the ObjectAPI is deinit'ed. + guard operations.filter({ $0.isCancelled == true }).count == 0 else { + return + } + + // Everything must have finished. + guard operations.filter({ $0.isFinished == true }).count == operations.count else { + return + } + + var allOutcomes = [Project.Id: Outcome]() + for operation in operations { + allOutcomes[operation.projectId] = operation.outcome + } + + completionHandler(allOutcomes) + } + + for operation in operations { + completedOperation.addDependency(operation) + } + + var allOperations: [Operation] = operations + allOperations.append(completedOperation) + + asyncRequestQueue.addOperations(allOperations, waitUntilFinished: false) + } + + // MARK: - Template + + /* + Queries all Project's matching |projectIds| asynchronously to get their + Templates. Passes a dictionary of outcomes to the completionHandler + mapping projectIds to each outcome when complete: + + [projectId1: outcome1, projectId2: outcome2, ...] + + The totality of outcomes can be in three states: + + 1. All outcomes succeeded. + 2. All outcomes failed. + 3. Outcomes are a mixture of succeeded/failed states. + + It is up to the consumer of this method to determine how to handle #2/3. + */ + fileprivate func getTemplates(inProjectsWithIds projectIds: Set, completionHandler: @escaping ([Project.Id: Outcome<[Template], GetError>]) -> Void) { + + // For every project create an operation to get all of its templates. + var operations = [GetTemplatesOperation]() + for projectId in projectIds { + let operation = GetTemplatesOperation(api: self, projectId: projectId) + operations.append(operation) + } + + let completedOperation = BlockOperation { + + // Nothing should be cancelled. The asyncRequestQueue is fileprivate + // and will only be cancelled if the ObjectAPI is deinit'ed. + guard operations.filter({ $0.isCancelled == true }).count == 0 else { + return + } + + // Everything must have finished. + guard operations.filter({ $0.isFinished == true }).count == operations.count else { + return + } + + var allOutcomes = [Project.Id: Outcome<[Template], GetError>]() + for operation in operations { + allOutcomes[operation.projectId] = operation.outcome + } + + completionHandler(allOutcomes) + } + + for operation in operations { + completedOperation.addDependency(operation) + } + + var allOperations: [Operation] = operations + allOperations.append(completedOperation) + + asyncRequestQueue.addOperations(allOperations, waitUntilFinished: false) + } + +} + +// MARK: - Objects + +extension ObjectAPI { + + // MARK: - Case + + /* + http://docs.gurock.com/testrail-api2/reference-cases#add_case + */ + public func addCase(_ newCase: NewCase, to section: Section, completionHandler: @escaping (Outcome) -> Void) { + addCase(newCase, toSectionWithId: section.id, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-cases#add_case + */ + public func addCase(_ newCase: NewCase, toSectionWithId sectionId: Section.Id, completionHandler: @escaping (Outcome) -> Void) { + + let dataOutcome = self.data(from: newCase) + let data: Data + switch dataOutcome { + case .failed(let error): + completionHandler(.failed(.objectConversionError(error))) + return + case .succeeded(let aData): + data = aData + } + + api.addCase(sectionId: sectionId, data: data) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.addCase(newCase, toSectionWithId: sectionId, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + /* + http://docs.gurock.com/testrail-api2/reference-cases#delete_case + */ + public func deleteCase(_ case: Case, completionHandler: @escaping (Outcome) -> Void) { + deleteCase(`case`.id, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-cases#delete_case + */ + public func deleteCase(_ caseId: Case.Id, completionHandler: @escaping (Outcome) -> Void) { + api.deleteCase(caseId: caseId) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.deleteCase(caseId, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + switch processedOutcome { + case .failed(let error): + completionHandler(.failed(error)) + case .succeeded(_): + completionHandler(.succeeded(nil)) + } + }) + } + } + + /* + http://docs.gurock.com/testrail-api2/reference-cases#get_case + */ + public func getCase(_ caseId: Case.Id, completionHandler: @escaping (Outcome) -> Void) { + api.getCase(caseId: caseId) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.getCase(caseId, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + /* + http://docs.gurock.com/testrail-api2/reference-cases#get_cases + + suite is required if the project is not running in single suite mode. + */ + public func getCases(in project: Project, in suite: Suite? = nil, in section: Section? = nil, filteredBy filters: [Filter]? = nil, completionHandler: @escaping (Outcome<[Case], GetError>) -> Void) { + getCases(inProjectWithId: project.id, inSuiteWithId: suite?.id, inSectionWithId: section?.id, filteredBy: filters, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-cases#get_cases + + suiteId is required if the project is not running in single suite mode. + */ + public func getCases(inProjectWithId projectId: Project.Id, inSuiteWithId suiteId: Suite.Id? = nil, inSectionWithId sectionId: Section.Id? = nil, filteredBy filters: [Filter]? = nil, completionHandler: @escaping (Outcome<[Case], GetError>) -> Void) { + api.getCases(projectId: projectId, suiteId: suiteId, sectionId: sectionId, filters: filters) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.getCases(inProjectWithId: projectId, inSuiteWithId: suiteId, inSectionWithId: sectionId, filteredBy: filters, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + /* + http://docs.gurock.com/testrail-api2/reference-cases#update_case + */ + public func updateCase(_ case: Case, completionHandler: @escaping (Outcome) -> Void) { + + let dataOutcome = self.data(from: `case`) + let data: Data + switch dataOutcome { + case .failed(let error): + completionHandler(.failed(.objectConversionError(error))) + return + case .succeeded(let aData): + data = aData + } + + api.updateCase(caseId: `case`.id, data: data) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.updateCase(`case`, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + // MARK: - CaseField + + /* + http://docs.gurock.com/testrail-api2/reference-cases-fields#get_case_fields + */ + public func getCaseFields(completionHandler: @escaping (Outcome<[CaseField], GetError>) -> Void) { + api.getCaseFields { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.getCaseFields(completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + // MARK: - CaseType + + /* + http://docs.gurock.com/testrail-api2/reference-cases-types#get_case_types + */ + public func getCaseTypes(completionHandler: @escaping (Outcome<[CaseType], GetError>) -> Void) { + api.getCaseTypes { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.getCaseTypes(completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + // MARK: - Configuration + + /* + http://docs.gurock.com/testrail-api2/reference-configs#add_config + */ + public func addConfiguration(_ newConfiguration: NewConfiguration, to configurationGroup: ConfigurationGroup, completionHandler: @escaping (Outcome) -> Void) { + addConfiguration(newConfiguration, toConfigurationGroupWithId: configurationGroup.id, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-configs#add_config + */ + public func addConfiguration(_ newConfiguration: NewConfiguration, toConfigurationGroupWithId configurationGroupId: ConfigurationGroup.Id, completionHandler: @escaping (Outcome) -> Void) { + + let dataOutcome = self.data(from: newConfiguration) + let data: Data + switch dataOutcome { + case .failed(let error): + completionHandler(.failed(.objectConversionError(error))) + return + case .succeeded(let aData): + data = aData + } + + api.addConfiguration(configurationGroupId: configurationGroupId, data: data) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.addConfiguration(newConfiguration, toConfigurationGroupWithId: configurationGroupId, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + /* + http://docs.gurock.com/testrail-api2/reference-configs#delete_config + */ + public func deleteConfiguration(_ configuration: Configuration, completionHandler: @escaping (Outcome) -> Void) { + deleteConfiguration(configuration.id, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-configs#delete_config + */ + public func deleteConfiguration(_ configurationId: Configuration.Id, completionHandler: @escaping (Outcome) -> Void) { + api.deleteConfiguration(configurationId: configurationId) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.deleteConfiguration(configurationId, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + switch processedOutcome { + case .failed(let error): + completionHandler(.failed(error)) + case .succeeded(_): + completionHandler(.succeeded(nil)) + } + }) + } + } + + /* + http://docs.gurock.com/testrail-api2/reference-configs#update_config + */ + public func updateConfiguration(_ configuration: Configuration, completionHandler: @escaping (Outcome) -> Void) { + + let dataOutcome = self.data(from: configuration) + let data: Data + switch dataOutcome { + case .failed(let error): + completionHandler(.failed(.objectConversionError(error))) + return + case .succeeded(let aData): + data = aData + } + + api.updateConfiguration(configurationId: configuration.id, data: data) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.updateConfiguration(configuration, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + // MARK: - ConfigurationGroup + + /* + http://docs.gurock.com/testrail-api2/reference-configs#add_config_group + */ + public func addConfigurationGroup(_ newConfigurationGroup: NewConfigurationGroup, to project: Project, completionHandler: @escaping (Outcome) -> Void) { + addConfigurationGroup(newConfigurationGroup, toProjectWithId: project.id, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-configs#add_config_group + */ + public func addConfigurationGroup(_ newConfigurationGroup: NewConfigurationGroup, toProjectWithId projectId: Project.Id, completionHandler: @escaping (Outcome) -> Void) { + + let dataOutcome = self.data(from: newConfigurationGroup) + let data: Data + switch dataOutcome { + case .failed(let error): + completionHandler(.failed(.objectConversionError(error))) + return + case .succeeded(let aData): + data = aData + } + + api.addConfigurationGroup(projectId: projectId, data: data) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.addConfigurationGroup(newConfigurationGroup, toProjectWithId: projectId, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + /* + http://docs.gurock.com/testrail-api2/reference-configs#delete_config_group + */ + public func deleteConfigurationGroup(_ configurationGroup: ConfigurationGroup, completionHandler: @escaping (Outcome) -> Void) { + deleteConfigurationGroup(configurationGroup.id, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-configs#delete_config_group + */ + public func deleteConfigurationGroup(_ configurationGroupId: ConfigurationGroup.Id, completionHandler: @escaping (Outcome) -> Void) { + api.deleteConfigurationGroup(configurationGroupId: configurationGroupId) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.deleteConfigurationGroup(configurationGroupId, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + switch processedOutcome { + case .failed(let error): + completionHandler(.failed(error)) + case .succeeded(_): + completionHandler(.succeeded(nil)) + } + }) + } + } + + /* + Gets all ConfigurationGroups from all Projects. + + - Returns a de-duped array of 0+ ConfigurationGroups from all Projects upon + success. + - Returns an ErrorContainer of 1+ errors if any of the GET requests failed. + + This method makes multiple async API calls across all accessible projects + to gather all ConfigurationGroups. It may be expensive/slow to run if you + have many projects. + */ + public func getConfigurationGroups(completionHandler: @escaping (Outcome<[ConfigurationGroup], ErrorContainer>) -> Void) { + + getProjects { [weak self] (projectsOutcome) in + + switch projectsOutcome { + case .failed(let error): + completionHandler(.failed(ErrorContainer(error))) + case .succeeded(let projects): + + let projectIds = Set(projects.flatMap({ $0.id })) + self?.getConfigurationGroups(inProjectsWithIds: projectIds) { (configurationGroupsOutcomes) in + + let outcomes = configurationGroupsOutcomes.flatMap { $1 } // Discard projectId keys. + var allConfigurationGroups = [ConfigurationGroup]() + var allErrors = [GetError]() + + // Extract all ConfigurationGroups/errors from outcomes. + for outcome in outcomes { + switch outcome { + case .failed(let error): + allErrors.append(error) + case .succeeded(let configurationGroups): + for configurationGroup in configurationGroups { + guard allConfigurationGroups.filter({ $0.id == configurationGroup.id }).count == 0 else { // Skip duplicates. + continue + } + allConfigurationGroups.append(configurationGroup) + } + } + } + + if let errorContainer = ErrorContainer(allErrors) { + completionHandler(.failed(errorContainer)) // Fail if there are any errors. + } else { + completionHandler(.succeeded(allConfigurationGroups)) + } + } + } + } + } + + /* + http://docs.gurock.com/testrail-api2/reference-configs#get_configs + */ + public func getConfigurationGroups(in project: Project, completionHandler: @escaping (Outcome<[ConfigurationGroup], GetError>) -> Void) { + getConfigurationGroups(inProjectWithId: project.id, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-configs#get_configs + */ + public func getConfigurationGroups(inProjectWithId projectId: Project.Id, completionHandler: @escaping (Outcome<[ConfigurationGroup], GetError>) -> Void) { + api.getConfigurations(projectId: projectId) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.getConfigurationGroups(inProjectWithId: projectId, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + /* + http://docs.gurock.com/testrail-api2/reference-configs#update_config_group + */ + public func updateConfigurationGroup(_ configurationGroup: ConfigurationGroup, completionHandler: @escaping (Outcome) -> Void) { + + let dataOutcome = self.data(from: configurationGroup) + let data: Data + switch dataOutcome { + case .failed(let error): + completionHandler(.failed(.objectConversionError(error))) + return + case .succeeded(let aData): + data = aData + } + + api.updateConfigurationGroup(configurationGroupId: configurationGroup.id, data: data) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.updateConfigurationGroup(configurationGroup, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + // MARK: - Milestone + + /* + http://docs.gurock.com/testrail-api2/reference-milestones#add_milestone + */ + public func addMilestone(_ newMilestone: NewMilestone, to project: Project, completionHandler: @escaping (Outcome) -> Void) { + addMilestone(newMilestone, toProjectWithId: project.id, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-milestones#add_milestone + */ + public func addMilestone(_ newMilestone: NewMilestone, toProjectWithId projectId: Project.Id, completionHandler: @escaping (Outcome) -> Void) { + + let dataOutcome = self.data(from: newMilestone) + let data: Data + switch dataOutcome { + case .failed(let error): + completionHandler(.failed(.objectConversionError(error))) + return + case .succeeded(let aData): + data = aData + } + + api.addMilestone(projectId: projectId, data: data) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.addMilestone(newMilestone, toProjectWithId: projectId, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + /* + http://docs.gurock.com/testrail-api2/reference-milestones#delete_milestone + */ + public func deleteMilestone(_ milestone: Milestone, completionHandler: @escaping (Outcome) -> Void) { + deleteMilestone(milestone.id, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-milestones#delete_milestone + */ + public func deleteMilestone(_ milestoneId: Milestone.Id, completionHandler: @escaping (Outcome) -> Void) { + api.deleteMilestone(milestoneId: milestoneId) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.deleteMilestone(milestoneId, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + switch processedOutcome { + case .failed(let error): + completionHandler(.failed(error)) + case .succeeded(_): + completionHandler(.succeeded(nil)) + } + }) + } + } + + /* + http://docs.gurock.com/testrail-api2/reference-milestones#get_milestone + */ + public func getMilestone(_ milestoneId: Milestone.Id, completionHandler: @escaping (Outcome) -> Void) { + api.getMilestone(milestoneId: milestoneId) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.getMilestone(milestoneId, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + /* + http://docs.gurock.com/testrail-api2/reference-milestones#get_milestones + + NOTE: This API call does not include .milestones for the Milestone's. For + that behavior use getMilestone(...). + */ + public func getMilestones(in project: Project, filteredBy filters: [Filter]? = nil, completionHandler: @escaping (Outcome<[Milestone], GetError>) -> Void) { + getMilestones(inProjectWithId: project.id, filteredBy: filters, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-milestones#get_milestones + + NOTE: This API call does not include .milestones for the Milestone's. For + that behavior use getMilestone(...). + */ + public func getMilestones(inProjectWithId projectId: Project.Id, filteredBy filters: [Filter]? = nil, completionHandler: @escaping (Outcome<[Milestone], GetError>) -> Void) { + api.getMilestones(projectId: projectId, filters: filters) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.getMilestones(inProjectWithId: projectId, filteredBy: filters, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + /* + http://docs.gurock.com/testrail-api2/reference-milestones#update_milestone + */ + public func updateMilestone(_ milestone: Milestone, completionHandler: @escaping (Outcome) -> Void) { + + let dataOutcome = self.data(from: milestone) + let data: Data + switch dataOutcome { + case .failed(let error): + completionHandler(.failed(.objectConversionError(error))) + return + case .succeeded(let aData): + data = aData + } + + api.updateMilestone(milestoneId: milestone.id, data: data) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.updateMilestone(milestone, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + // MARK: - Plan + + /* + http://docs.gurock.com/testrail-api2/reference-plans#add_plan + */ + public func addPlan(_ newPlan: NewPlan, to project: Project, completionHandler: @escaping (Outcome) -> Void) { + addPlan(newPlan, toProjectWithId: project.id, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-plans#add_plan + */ + public func addPlan(_ newPlan: NewPlan, toProjectWithId projectId: Project.Id, completionHandler: @escaping (Outcome) -> Void) { + + let dataOutcome = self.data(from: newPlan) + let data: Data + switch dataOutcome { + case .failed(let error): + completionHandler(.failed(.objectConversionError(error))) + return + case .succeeded(let aData): + data = aData + } + + api.addPlan(projectId: projectId, data: data) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.addPlan(newPlan, toProjectWithId: projectId, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + /* + http://docs.gurock.com/testrail-api2/reference-plans#close_plan + */ + public func closePlan(_ plan: Plan, completionHandler: @escaping (Outcome) -> Void) { + closePlan(plan.id, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-plans#close_plan + */ + public func closePlan(_ planId: Plan.Id, completionHandler: @escaping (Outcome) -> Void) { + api.closePlan(planId: planId) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.closePlan(planId, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + /* + http://docs.gurock.com/testrail-api2/reference-plans#delete_plan + */ + public func deletePlan(_ plan: Plan, completionHandler: @escaping (Outcome) -> Void) { + deletePlan(plan.id, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-plans#delete_plan + */ + public func deletePlan(_ planId: Plan.Id, completionHandler: @escaping (Outcome) -> Void) { + api.deletePlan(planId: planId) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.deletePlan(planId, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + switch processedOutcome { + case .failed(let error): + completionHandler(.failed(error)) + case .succeeded(_): + completionHandler(.succeeded(nil)) + } + }) + } + } + + /* + http://docs.gurock.com/testrail-api2/reference-plans#get_plan + */ + public func getPlan(_ planId: Plan.Id, completionHandler: @escaping (Outcome) -> Void) { + api.getPlan(planId: planId) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.getPlan(planId, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + /* + http://docs.gurock.com/testrail-api2/reference-plans#get_plans + + This API call does not include .entries for the Plan's. For that behavior + use getPlan(...). + */ + public func getPlans(in project: Project, filteredBy filters: [Filter]? = nil, completionHandler: @escaping (Outcome<[Plan], GetError>) -> Void) { + getPlans(inProjectWithId: project.id, filteredBy: filters, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-plans#get_plans + + This API call does not include .entries for the Plan's. For that behavior + use getPlan(...). + */ + public func getPlans(inProjectWithId projectId: Project.Id, filteredBy filters: [Filter]? = nil, completionHandler: @escaping (Outcome<[Plan], GetError>) -> Void) { + api.getPlans(projectId: projectId, filters: filters) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.getPlans(inProjectWithId: projectId, filteredBy: filters, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + /* + http://docs.gurock.com/testrail-api2/reference-plans#update_plan + */ + public func updatePlan(_ plan: Plan, completionHandler: @escaping (Outcome) -> Void) { + + let dataOutcome = self.data(from: plan) + let data: Data + switch dataOutcome { + case .failed(let error): + completionHandler(.failed(.objectConversionError(error))) + return + case .succeeded(let aData): + data = aData + } + + api.updatePlan(planId: plan.id, data: data) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.updatePlan(plan, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + // MARK: - Plan.Entry + + /* + http://docs.gurock.com/testrail-api2/reference-plans#add_plan_entry + + Upon success only the newly added run(s) are included in Plan.Entry.runs. + Any other runs are not included. To get all runs call getPlan() after this + method completes successfully. + + Upon success `plan`.entries will be stale; use getPlan() to get a fresh + Plan. + */ + public func addPlanEntry(_ newPlanEntry: NewPlan.Entry, to plan: Plan, completionHandler: @escaping (Outcome) -> Void) { + addPlanEntry(newPlanEntry, toPlanWithId: plan.id, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-plans#add_plan_entry + + Upon success only the newly added run(s) are included in Plan.Entry.runs. + Any other runs are not included. To get all runs call getPlan() after this + method completes successfully. + + Upon success Plan.entries for `planId` will be stale; use getPlan() to get + a fresh Plan. + */ + public func addPlanEntry(_ newPlanEntry: NewPlan.Entry, toPlanWithId planId: Plan.Id, completionHandler: @escaping (Outcome) -> Void) { + + let dataOutcome = self.data(from: newPlanEntry) + let data: Data + switch dataOutcome { + case .failed(let error): + completionHandler(.failed(.objectConversionError(error))) + return + case .succeeded(let aData): + data = aData + } + + api.addPlanEntry(planId: planId, data: data) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.addPlanEntry(newPlanEntry, toPlanWithId: planId, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + /* + http://docs.gurock.com/testrail-api2/reference-plans#delete_plan_entry + + Upon success `plan`.entries will be stale; use getPlan() to get a fresh + Plan. + */ + public func deletePlanEntry(_ planEntry: Plan.Entry, from plan: Plan, completionHandler: @escaping (Outcome) -> Void) { + deletePlanEntry(planEntry.id, fromPlanWithId: plan.id, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-plans#delete_plan_entry + + Upon success Plan.entries for `planId` will be stale; use getPlan() to get + a fresh Plan. + */ + public func deletePlanEntry(_ planEntryId: Plan.Entry.Id, fromPlanWithId planId: Plan.Id, completionHandler: @escaping (Outcome) -> Void) { + api.deletePlanEntry(planId: planId, planEntryId: planEntryId) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.deletePlanEntry(planEntryId, fromPlanWithId: planId, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + switch processedOutcome { + case .failed(let error): + completionHandler(.failed(error)) + case .succeeded(_): + completionHandler(.succeeded(nil)) + } + }) + } + } + + /* + http://docs.gurock.com/testrail-api2/reference-plans#update_plan_entry + + Upon success this returns a new Plan.Entry including all of its test Run's. + `plan`.entries will be stale; use getPlan() to get a fresh Plan. + + Plan.Entry updates differ slightly from other API updates. You can: + + - Update variable properties affecting the Plan.Entry itself. + - Pass `planEntryRunsData` affecting all Plan.Entry.runs in bulk. + */ + public func updatePlanEntry(_ planEntry: Plan.Entry, in plan: Plan, with planEntryRuns: UpdatePlanEntryRuns? = nil, completionHandler: @escaping (Outcome) -> Void) { + updatePlanEntry(planEntry, inPlanWithId: plan.id, with: planEntryRuns, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-plans#update_plan_entry + + Upon success this returns a new Plan.Entry including all of its test Run's. + Plan.entries for `planId` will be stale; use getPlan() to get a fresh Plan. + + Plan.Entry updates differ slightly from other API updates. You can: + + - Update variable properties affecting the Plan.Entry itself. + - Pass `planEntryRunsData` affecting all Plan.Entry.runs in bulk. + */ + public func updatePlanEntry(_ planEntry: Plan.Entry, inPlanWithId planId: Plan.Id, with planEntryRuns: UpdatePlanEntryRuns? = nil, completionHandler: @escaping (Outcome) -> Void) { + + let dataOutcome: DataOutcome + if let planEntryRuns = planEntryRuns { + dataOutcome = self.data(from: [planEntry, planEntryRuns]) + } else { + dataOutcome = self.data(from: planEntry) + } + + let data: Data + switch dataOutcome { + case .failed(let error): + completionHandler(.failed(.objectConversionError(error))) + return + case .succeeded(let aData): + data = aData + } + + api.updatePlanEntry(planId: planId, planEntryId: planEntry.id, data: data) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.updatePlanEntry(planEntry, inPlanWithId: planId, with: planEntryRuns, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + // MARK: - Priority + + /* + http://docs.gurock.com/testrail-api2/reference-priorities#get_priorities + */ + public func getPriorities(completionHandler: @escaping (Outcome<[Priority], GetError>) -> Void) { + api.getPriorities { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.getPriorities(completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + // MARK: - Project + + /* + http://docs.gurock.com/testrail-api2/reference-projects#add_project + */ + public func addProject(_ newProject: NewProject, completionHandler: @escaping (Outcome) -> Void) { + + let dataOutcome = self.data(from: newProject) + let data: Data + switch dataOutcome { + case .failed(let error): + completionHandler(.failed(.objectConversionError(error))) + return + case .succeeded(let aData): + data = aData + } + + api.addProject(data: data) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.addProject(newProject, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + /* + http://docs.gurock.com/testrail-api2/reference-projects#delete_project + */ + public func deleteProject(_ project: Project, completionHandler: @escaping (Outcome) -> Void) { + deleteProject(project.id, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-projects#delete_project + */ + public func deleteProject(_ projectId: Project.Id, completionHandler: @escaping (Outcome) -> Void) { + api.deleteProject(projectId: projectId) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.deleteProject(projectId, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + switch processedOutcome { + case .failed(let error): + completionHandler(.failed(error)) + case .succeeded(_): + completionHandler(.succeeded(nil)) + } + }) + } + } + + /* + http://docs.gurock.com/testrail-api2/reference-projects#get_project + + You must have at least Read-only access to the project otherwise a 403 + error will be returned. + */ + public func getProject(_ projectId: Project.Id, completionHandler: @escaping (Outcome) -> Void) { + api.getProject(projectId: projectId) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.getProject(projectId, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + /* + http://docs.gurock.com/testrail-api2/reference-projects#get_projects + + Returns all projects which you have at least Read-only access to. All other + projects will be silently omitted. + + To determine if you have at least Read-only access to a project use + getProject() instead. It will return an explicit error if you do not have + access to a project. + */ + public func getProjects(filteredBy filters: [Filter]? = nil, completionHandler: @escaping (Outcome<[Project], GetError>) -> Void) { + api.getProjects(filters: filters) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.getProjects(filteredBy: filters, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + /* + http://docs.gurock.com/testrail-api2/reference-projects#update_project + */ + public func updateProject(_ project: Project, completionHandler: @escaping (Outcome) -> Void) { + + let dataOutcome = self.data(from: project) + let data: Data + switch dataOutcome { + case .failed(let error): + completionHandler(.failed(.objectConversionError(error))) + return + case .succeeded(let aData): + data = aData + } + + api.updateProject(projectId: project.id, data: data) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.updateProject(project, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + // MARK: - Result + + /* + http://docs.gurock.com/testrail-api2/reference-results#add_result + */ + public func addResult(_ newResult: NewResult, to test: Test, completionHandler: @escaping (Outcome) -> Void) { + addResult(newResult, toTestWithId: test.id, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-results#add_result + */ + public func addResult(_ newResult: NewResult, toTestWithId testId: Test.Id, completionHandler: @escaping (Outcome) -> Void) { + + let dataOutcome = self.data(from: newResult) + let data: Data + switch dataOutcome { + case .failed(let error): + completionHandler(.failed(.objectConversionError(error))) + return + case .succeeded(let aData): + data = aData + } + + api.addResult(testId: testId, data: data) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.addResult(newResult, toTestWithId: testId, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + /* + http://docs.gurock.com/testrail-api2/reference-results#add_result_for_case + */ + public func addResultForCase(_ newResult: NewResult, to run: Run, to case: Case, completionHandler: @escaping (Outcome) -> Void) { + addResultForCase(newResult, toRunWithId: run.id, toCaseWithId: `case`.id, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-results#add_result_for_case + */ + public func addResultForCase(_ newResult: NewResult, toRunWithId runId: Run.Id, toCaseWithId caseId: Case.Id, completionHandler: @escaping (Outcome) -> Void) { + + let dataOutcome = self.data(from: newResult) + let data: Data + switch dataOutcome { + case .failed(let error): + completionHandler(.failed(.objectConversionError(error))) + return + case .succeeded(let aData): + data = aData + } + + api.addResultForCase(runId: runId, caseId: caseId, data: data) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.addResultForCase(newResult, toRunWithId: runId, toCaseWithId: caseId, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + /* + http://docs.gurock.com/testrail-api2/reference-results#add_results + */ + public func addResults(_ newTestResults: NewTestResults, to run: Run, completionHandler: @escaping (Outcome<[Result], AddError>) -> Void) { + addResults(newTestResults, toRunWithId: run.id, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-results#add_results + */ + public func addResults(_ newTestResults: NewTestResults, toRunWithId runId: Run.Id, completionHandler: @escaping (Outcome<[Result], AddError>) -> Void) { + + let dataOutcome = self.data(from: newTestResults) + let data: Data + switch dataOutcome { + case .failed(let error): + completionHandler(.failed(.objectConversionError(error))) + return + case .succeeded(let aData): + data = aData + } + + api.addResults(runId: runId, data: data) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.addResults(newTestResults, toRunWithId: runId, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + /* + http://docs.gurock.com/testrail-api2/reference-results#add_results_for_cases + */ + public func addResultsForCases(_ newCaseResults: NewCaseResults, to run: Run, completionHandler: @escaping (Outcome<[Result], AddError>) -> Void) { + addResultsForCases(newCaseResults, toRunWithId: run.id, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-results#add_results_for_cases + */ + public func addResultsForCases(_ newCaseResults: NewCaseResults, toRunWithId runId: Run.Id, completionHandler: @escaping (Outcome<[Result], AddError>) -> Void) { + + let dataOutcome = self.data(from: newCaseResults) + let data: Data + switch dataOutcome { + case .failed(let error): + completionHandler(.failed(.objectConversionError(error))) + return + case .succeeded(let aData): + data = aData + } + + api.addResultsForCases(runId: runId, data: data) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.addResultsForCases(newCaseResults, toRunWithId: runId, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + /* + http://docs.gurock.com/testrail-api2/reference-results#get_results + */ + public func getResultsForTest(_ test: Test, filteredBy filters: [Filter]? = nil, completionHandler: @escaping (Outcome<[Result], GetError>) -> Void) { + getResultsForTest(test.id, filteredBy: filters, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-results#get_results + */ + public func getResultsForTest(_ testId: Test.Id, filteredBy filters: [Filter]? = nil, completionHandler: @escaping (Outcome<[Result], GetError>) -> Void) { + api.getResults(testId: testId, filters: filters) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.getResultsForTest(testId, filteredBy: filters, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + /* + http://docs.gurock.com/testrail-api2/reference-results#get_results_for_case + */ + public func getResultsForCase(_ case: Case, in run: Run, filteredBy filters: [Filter]? = nil, completionHandler: @escaping (Outcome<[Result], GetError>) -> Void) { + getResultsForCase(`case`.id, inRunWithId: run.id, filteredBy: filters, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-results#get_results_for_case + */ + public func getResultsForCase(_ caseId: Case.Id, inRunWithId runId: Run.Id, filteredBy filters: [Filter]? = nil, completionHandler: @escaping (Outcome<[Result], GetError>) -> Void) { + api.getResultsForCase(runId: runId, caseId: caseId, filters: filters) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.getResultsForCase(caseId, inRunWithId: runId, filteredBy: filters, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + /* + http://docs.gurock.com/testrail-api2/reference-results#get_results_for_run + */ + public func getResultsForRun(_ run: Run, filteredBy filters: [Filter]? = nil, completionHandler: @escaping (Outcome<[Result], GetError>) -> Void) { + getResultsForRun(run.id, filteredBy: filters, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-results#get_results_for_run + */ + public func getResultsForRun(_ runId: Run.Id, filteredBy filters: [Filter]? = nil, completionHandler: @escaping (Outcome<[Result], GetError>) -> Void) { + api.getResultsForRun(runId: runId, filters: filters) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.getResultsForRun(runId, filteredBy: filters, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + // MARK: - ResultField + + /* + http://docs.gurock.com/testrail-api2/reference-results-fields#get_result_fields + */ + public func getResultFields(completionHandler: @escaping (Outcome<[ResultField], GetError>) -> Void) { + api.getResultFields { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.getResultFields(completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + // MARK: - Run + + /* + http://docs.gurock.com/testrail-api2/reference-runs#add_run + */ + public func addRun(_ newRun: NewRun, to project: Project, completionHandler: @escaping (Outcome) -> Void) { + addRun(newRun, toProjectWithId: project.id, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-runs#add_run + */ + public func addRun(_ newRun: NewRun, toProjectWithId projectId: Project.Id, completionHandler: @escaping (Outcome) -> Void) { + + let dataOutcome = self.data(from: newRun) + let data: Data + switch dataOutcome { + case .failed(let error): + completionHandler(.failed(.objectConversionError(error))) + return + case .succeeded(let aData): + data = aData + } + + api.addRun(projectId: projectId, data: data) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.addRun(newRun, toProjectWithId: projectId, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + /* + http://docs.gurock.com/testrail-api2/reference-runs#close_run + */ + public func closeRun(_ run: Run, completionHandler: @escaping (Outcome) -> Void) { + closeRun(run.id, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-runs#close_run + */ + public func closeRun(_ runId: Run.Id, completionHandler: @escaping (Outcome) -> Void) { + api.closeRun(runId: runId) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.closeRun(runId, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + /* + http://docs.gurock.com/testrail-api2/reference-runs#delete_run + */ + public func deleteRun(_ run: Run, completionHandler: @escaping (Outcome) -> Void) { + deleteRun(run.id, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-runs#delete_run + */ + public func deleteRun(_ runId: Run.Id, completionHandler: @escaping (Outcome) -> Void) { + api.deleteRun(runId: runId) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.deleteRun(runId, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + switch processedOutcome { + case .failed(let error): + completionHandler(.failed(error)) + case .succeeded(_): + completionHandler(.succeeded(nil)) + } + }) + } + } + + /* + http://docs.gurock.com/testrail-api2/reference-runs#get_run + */ + public func getRun(_ runId: Run.Id, completionHandler: @escaping (Outcome) -> Void) { + api.getRun(runId: runId) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.getRun(runId, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + /* + http://docs.gurock.com/testrail-api2/reference-runs#get_runs + + This only returns Runs which are not part of a Plan. + */ + public func getRuns(in project: Project, filteredBy filters: [Filter]? = nil, completionHandler: @escaping (Outcome<[Run], GetError>) -> Void) { + getRuns(inProjectWithId: project.id, filteredBy: filters, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-runs#get_runs + + This only returns Runs which are not part of a Plan. + */ + public func getRuns(inProjectWithId projectId: Project.Id, filteredBy filters: [Filter]? = nil, completionHandler: @escaping (Outcome<[Run], GetError>) -> Void) { + api.getRuns(projectId: projectId, filters: filters) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.getRuns(inProjectWithId: projectId, filteredBy: filters, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + /* + http://docs.gurock.com/testrail-api2/reference-runs#update_run + */ + public func updateRun(_ run: Run, completionHandler: @escaping (Outcome) -> Void) { + + let dataOutcome = self.data(from: run) + let data: Data + switch dataOutcome { + case .failed(let error): + completionHandler(.failed(.objectConversionError(error))) + return + case .succeeded(let aData): + data = aData + } + + api.updateRun(runId: run.id, data: data) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.updateRun(run, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + // MARK: - Section + + /* + http://docs.gurock.com/testrail-api2/reference-sections#add_section + */ + public func addSection(_ newSection: NewSection, to project: Project, completionHandler: @escaping (Outcome) -> Void) { + addSection(newSection, toProjectWithId: project.id, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-sections#add_section + */ + public func addSection(_ newSection: NewSection, toProjectWithId projectId: Project.Id, completionHandler: @escaping (Outcome) -> Void) { + + let dataOutcome = self.data(from: newSection) + let data: Data + switch dataOutcome { + case .failed(let error): + completionHandler(.failed(.objectConversionError(error))) + return + case .succeeded(let aData): + data = aData + } + + api.addSection(projectId: projectId, data: data) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.addSection(newSection, toProjectWithId: projectId, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + /* + http://docs.gurock.com/testrail-api2/reference-sections#delete_section + */ + public func deleteSection(_ section: Section, completionHandler: @escaping (Outcome) -> Void) { + deleteSection(section.id, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-sections#delete_section + */ + public func deleteSection(_ sectionId: Section.Id, completionHandler: @escaping (Outcome) -> Void) { + api.deleteSection(sectionId: sectionId) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.deleteSection(sectionId, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + switch processedOutcome { + case .failed(let error): + completionHandler(.failed(error)) + case .succeeded(_): + completionHandler(.succeeded(nil)) + } + }) + } + } + + /* + http://docs.gurock.com/testrail-api2/reference-sections#get_section + */ + public func getSection(_ sectionId: Section.Id, completionHandler: @escaping (Outcome) -> Void) { + api.getSection(sectionId: sectionId) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.getSection(sectionId, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + /* + http://docs.gurock.com/testrail-api2/reference-sections#get_sections + */ + public func getSections(in project: Project, in suite: Suite? = nil, completionHandler: @escaping (Outcome<[Section], GetError>) -> Void) { + getSections(inProjectWithId: project.id, inSuiteWithId: suite?.id, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-sections#get_sections + */ + public func getSections(inProjectWithId projectId: Project.Id, inSuiteWithId suiteId: Suite.Id? = nil, completionHandler: @escaping (Outcome<[Section], GetError>) -> Void) { + api.getSections(projectId: projectId, suiteId: suiteId) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.getSections(inProjectWithId: projectId, inSuiteWithId: suiteId, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + /* + http://docs.gurock.com/testrail-api2/reference-sections#update_section + */ + public func updateSection(_ section: Section, completionHandler: @escaping (Outcome) -> Void) { + + let dataOutcome = self.data(from: section) + let data: Data + switch dataOutcome { + case .failed(let error): + completionHandler(.failed(.objectConversionError(error))) + return + case .succeeded(let aData): + data = aData + } + + api.updateSection(sectionId: section.id, data: data) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.updateSection(section, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + // MARK: - Status + + /* + http://docs.gurock.com/testrail-api2/reference-statuses#get_statuses + */ + public func getStatuses(completionHandler: @escaping (Outcome<[Status], GetError>) -> Void) { + api.getStatuses { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.getStatuses(completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + // MARK: - Suite + + /* + http://docs.gurock.com/testrail-api2/reference-suites#add_suite + */ + public func addSuite(_ newSuite: NewSuite, to project: Project, completionHandler: @escaping (Outcome) -> Void) { + addSuite(newSuite, toProjectWithId: project.id, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-suites#add_suite + */ + public func addSuite(_ newSuite: NewSuite, toProjectWithId projectId: Project.Id, completionHandler: @escaping (Outcome) -> Void) { + + let dataOutcome = self.data(from: newSuite) + let data: Data + switch dataOutcome { + case .failed(let error): + completionHandler(.failed(.objectConversionError(error))) + return + case .succeeded(let aData): + data = aData + } + + api.addSuite(projectId: projectId, data: data) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.addSuite(newSuite, toProjectWithId: projectId, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + /* + http://docs.gurock.com/testrail-api2/reference-suites#delete_suite + */ + public func deleteSuite(_ suite: Suite, completionHandler: @escaping (Outcome) -> Void) { + deleteSuite(suite.id, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-suites#delete_suite + */ + public func deleteSuite(_ suiteId: Suite.Id, completionHandler: @escaping (Outcome) -> Void) { + api.deleteSuite(suiteId: suiteId) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.deleteSuite(suiteId, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + switch processedOutcome { + case .failed(let error): + completionHandler(.failed(error)) + case .succeeded(_): + completionHandler(.succeeded(nil)) + } + }) + } + } + + /* + http://docs.gurock.com/testrail-api2/reference-suites#get_suite + */ + public func getSuite(_ suiteId: Suite.Id, completionHandler: @escaping (Outcome) -> Void) { + api.getSuite(suiteId: suiteId) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.getSuite(suiteId, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + /* + http://docs.gurock.com/testrail-api2/reference-suites#get_suites + */ + public func getSuites(in project: Project, completionHandler: @escaping (Outcome<[Suite], GetError>) -> Void) { + getSuites(inProjectWithId: project.id, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-suites#get_suites + */ + public func getSuites(inProjectWithId projectId: Project.Id, completionHandler: @escaping (Outcome<[Suite], GetError>) -> Void) { + api.getSuites(projectId: projectId) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.getSuites(inProjectWithId: projectId, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + /* + http://docs.gurock.com/testrail-api2/reference-suites#update_suite + */ + public func updateSuite(_ suite: Suite, completionHandler: @escaping (Outcome) -> Void) { + + let dataOutcome = self.data(from: suite) + let data: Data + switch dataOutcome { + case .failed(let error): + completionHandler(.failed(.objectConversionError(error))) + return + case .succeeded(let aData): + data = aData + } + + api.updateSuite(suiteId: suite.id, data: data) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.updateSuite(suite, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + // MARK: - Template + + /* + Gets all Templates from all Projects. A template may appear in some or all + projects. + + - Returns a de-duped array of 0+ Templates from all Projects upon success. + - Returns an ErrorContainer of 1+ errors if any of the GET requests failed. + + This method makes multiple async API calls across all accessible projects + to gather all templates. It may be expensive/slow to run if you have many + projects. + */ + public func getTemplates(completionHandler: @escaping (Outcome<[Template], ErrorContainer>) -> Void) { + + getProjects { [weak self] (projectsOutcome) in + + switch projectsOutcome { + case .failed(let error): + completionHandler(.failed(ErrorContainer(error))) + case .succeeded(let projects): + + let projectIds = Set(projects.flatMap({ $0.id })) + self?.getTemplates(inProjectsWithIds: projectIds) { (templatesOutcomes) in + + let outcomes = templatesOutcomes.flatMap { $1 } // Discard projectId keys. + var allTemplates = [Template]() + var allErrors = [GetError]() + + // Extract all templates/errors from outcomes. + for outcome in outcomes { + switch outcome { + case .failed(let error): + allErrors.append(error) + case .succeeded(let templates): + for template in templates { + guard allTemplates.filter({ $0.id == template.id }).count == 0 else { // Skip duplicates. + continue + } + allTemplates.append(template) + } + } + } + + if let errorContainer = ErrorContainer(allErrors) { + completionHandler(.failed(errorContainer)) // Fail if there are any errors. + } else { + completionHandler(.succeeded(allTemplates)) + } + } + } + } + } + + /* + http://docs.gurock.com/testrail-api2/reference-templates#get_templates + */ + public func getTemplates(in project: Project, completionHandler: @escaping (Outcome<[Template], GetError>) -> Void) { + getTemplates(inProjectWithId: project.id, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-templates#get_templates + */ + public func getTemplates(inProjectWithId projectId: Project.Id, completionHandler: @escaping (Outcome<[Template], GetError>) -> Void) { + api.getTemplates(projectId: projectId) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.getTemplates(inProjectWithId: projectId, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + // MARK: - Test + + /* + http://docs.gurock.com/testrail-api2/reference-tests#get_test + */ + public func getTest(_ testId: Test.Id, completionHandler: @escaping (Outcome) -> Void) { + api.getTest(testId: testId) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.getTest(testId, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + /* + http://docs.gurock.com/testrail-api2/reference-tests#get_tests + */ + public func getTests(in run: Run, filteredBy filters: [Filter]? = nil, completionHandler: @escaping (Outcome<[Test], GetError>) -> Void) { + getTests(inRunWithId: run.id, filteredBy: filters, completionHandler: completionHandler) + } + + /* + http://docs.gurock.com/testrail-api2/reference-tests#get_tests + */ + public func getTests(inRunWithId runId: Run.Id, filteredBy filters: [Filter]? = nil, completionHandler: @escaping (Outcome<[Test], GetError>) -> Void) { + api.getTests(runId: runId, filters: filters) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.getTests(inRunWithId: runId, filteredBy: filters, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + // MARK: - User + + /* + http://docs.gurock.com/testrail-api2/reference-users#get_user + */ + public func getUser(_ userId: User.Id, completionHandler: @escaping (Outcome) -> Void) { + api.getUser(userId: userId) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.getUser(userId, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + /* + http://docs.gurock.com/testrail-api2/reference-users#get_user_by_email + */ + public func getUserByEmail(_ email: String, completionHandler: @escaping (Outcome) -> Void) { + api.getUserByEmail(email) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.getUserByEmail(email, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + /* + http://docs.gurock.com/testrail-api2/reference-users#get_users + */ + public func getUsers(completionHandler: @escaping (Outcome<[User], GetError>) -> Void) { + api.getUsers { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.getUsers(completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + +} + +// MARK: - Objects (Fileprivate Matching) + +extension ObjectAPI { + + /* + - Upon success returns an Outcome with items from |items| matching all + |ids|. Requires every id in |ids| to match an item. + - Upon failure returns an Outcome indicating if no matches were found, or + if only partial matches were found. + */ + fileprivate static func matches(from items: [Item], matchingIds ids: Set) -> Outcome<[Item], MultipleMatchError> { + + // Find all matches. + var matches = [Item]() + var missing = Set() + + for id in ids { + guard let item = items.filter({ $0.id == id }).first else { + missing.insert(id) + continue + } + guard matches.contains(where: { $0.id == item.id }) == false else { + continue + } + matches.append(item) + } + + // Were all matches found? + guard missing.count == 0 else { + if matches.count == 0 { + return .failed(.noMatchesFound(missing: missing)) + } else { + return .failed(.partialMatchesFound(matches: matches, missing: missing)) + } + } + + // All matches found. + return .succeeded(matches) + } + +} + +// MARK: - Objects (Matching) + +extension ObjectAPI { + + // MARK: - CaseType + + public func getCaseType(matching id: CaseType.Id, completionHandler: @escaping (Outcome, GetError>>) -> Void) { + getCaseTypes { (outcome) in + switch outcome { + case .failed(let error): + completionHandler(.failed(.otherError(error))) + case .succeeded(let caseTypes): + if let caseType = caseTypes.filter({ $0.id == id }).first { + completionHandler(.succeeded(caseType)) + } else { + completionHandler(.failed(.matchError(.noMatchFound(missing: id)))) + } + } + } + } + + // MARK: - ConfigurationGroup + + public func getConfigurationGroup(matching id: ConfigurationGroup.Id, completionHandler: @escaping (Outcome, ErrorContainer>>) -> Void) { + getConfigurationGroups { (outcome) in + switch outcome { + case .failed(let error): + completionHandler(.failed(.otherError(error))) + case .succeeded(let configurationGroups): + if let configurationGroup = configurationGroups.filter({ $0.id == id }).first { + completionHandler(.succeeded(configurationGroup)) + } else { + completionHandler(.failed(.matchError(.noMatchFound(missing: id)))) + } + } + } + } + + // MARK: - Priority + + public func getPriority(matching id: Priority.Id, completionHandler: @escaping (Outcome, GetError>>) -> Void) { + getPriorities { (outcome) in + switch outcome { + case .failed(let error): + completionHandler(.failed(.otherError(error))) + case .succeeded(let priorities): + if let priority = priorities.filter({ $0.id == id }).first { + completionHandler(.succeeded(priority)) + } else { + completionHandler(.failed(.matchError(.noMatchFound(missing: id)))) + } + } + } + } + + // MARK: - Status + + public func getStatus(matching id: Status.Id, completionHandler: @escaping (Outcome, GetError>>) -> Void) { + getStatuses { (outcome) in + switch outcome { + case .failed(let error): + completionHandler(.failed(.otherError(error))) + case .succeeded(let statuses): + if let status = statuses.filter({ $0.id == id }).first { + completionHandler(.succeeded(status)) + } else { + completionHandler(.failed(.matchError(.noMatchFound(missing: id)))) + } + } + } + } + + // MARK: - Template + + public func getTemplate(matching id: Template.Id, completionHandler: @escaping (Outcome, ErrorContainer>>) -> Void) { + getTemplates { (outcome) in + switch outcome { + case .failed(let errors): + completionHandler(.failed(.otherError(errors))) + case .succeeded(let templates): + if let template = templates.filter({ $0.id == id }).first { + completionHandler(.succeeded(template)) + } else { + completionHandler(.failed(.matchError(.noMatchFound(missing: id)))) + } + } + } + } + + public func getTemplates(matching ids: [Template.Id], completionHandler: @escaping (Outcome<[Template], MatchError, ErrorContainer>>) -> Void) { + getTemplates(matching: Set(ids), completionHandler: completionHandler) // De-dupe ids + } + + private func getTemplates(matching ids: Set, completionHandler: @escaping (Outcome<[Template], MatchError, ErrorContainer>>) -> Void) { + getTemplates { (outcome) in + switch outcome { + case .failed(let errors): + completionHandler(.failed(.otherError(errors))) + case .succeeded(let templates): + let matchesOutcome = ObjectAPI.matches(from: templates, matchingIds: ids) + switch matchesOutcome { + case .failed(let error): + completionHandler(.failed(.matchError(error))) + case .succeeded(let matches): + completionHandler(.succeeded(matches)) + } + } + } + } + +} + +// MARK: - Objects (Forward Relationships) + +extension ObjectAPI { + + // MARK: - Case + + public func createdBy(_ `case`: Case, completionHandler: @escaping (Outcome) -> Void) { + getUser(`case`.createdBy, completionHandler: completionHandler) + } + + public func milestone(_ `case`: Case, completionHandler: @escaping (Outcome) -> Void) { + milestone(`case`.milestoneId, completionHandler: completionHandler) + } + + public func priority(_ `case`: Case, completionHandler: @escaping (Outcome, GetError>>) -> Void) { + getPriority(matching: `case`.priorityId, completionHandler: completionHandler) + } + + public func section(_ `case`: Case, completionHandler: @escaping (Outcome) -> Void) { + section(`case`.sectionId, completionHandler: completionHandler) + } + + public func suite(_ `case`: Case, completionHandler: @escaping (Outcome) -> Void) { + suite(`case`.suiteId, completionHandler: completionHandler) + } + + public func template(_ `case`: Case, completionHandler: @escaping (Outcome, ErrorContainer>>) -> Void) { + getTemplate(matching: `case`.templateId, completionHandler: completionHandler) + } + + public func type(_ `case`: Case, completionHandler: @escaping (Outcome, GetError>>) -> Void) { + getCaseType(matching: `case`.typeId, completionHandler: completionHandler) + } + + public func updatedBy(_ `case`: Case, completionHandler: @escaping (Outcome) -> Void) { + getUser(`case`.updatedBy, completionHandler: completionHandler) + } + + // MARK: - CaseField + + public func templates(_ caseField: CaseField, completionHandler: @escaping (Outcome<[Template], MatchError, ErrorContainer>>) -> Void) { + getTemplates(matching: caseField.templateIds, completionHandler: completionHandler) + } + + // MARK: - Config + + /* + Calls the projects() method handling/stripping the MultipleMatchError. Upon + success this effectively returns all projects accessible to the current API + user while silently omitting any which are missing. Failure only occurs if + one or more GetError's occurred. + */ + public func accessibleProjects(_ config: Config, completionHandler: @escaping(Outcome<[Project]?, ErrorContainer>) -> Void) { + projects(config) { (outcome) in + switch outcome { + case .failed(let error): + switch error { + case .matchError(let matchError): + switch matchError { + case .noMatchesFound(_): + completionHandler(.succeeded([])) + case .partialMatchesFound(let matches, _): + completionHandler(.succeeded(matches)) + } + case .otherError(let errorContainer): + completionHandler(.failed(errorContainer)) + } + case .succeeded(let projects): + completionHandler(.succeeded(projects)) + } + } + } + + /* + Asynchronously gets and returns projects for a Config. The outcome passed + to the completionHandler varies based on: + + 1. The value of Config.projectIds. + 2. The authorization level of api.username for each project. + + api.username can read projects it has Read-only or higher access to. + Project access can be limited to some or all users. This limitation makes + it impossible for this method to guarentee all projects will be returned + for a Config. Possible scenarios for Config.projectIds values: + + - .none always passes .succeeded(nil) to the handler. + - .all passes .succeeded(projects) to the handler upon success. This + includes all projects the user has access to while silently omitting any + they do not. .failed(errorContainer) will be passed if there were any + errors. + - .some is the same as .all except it will fail if any specified projectIds + return a 403 error. + + --------------------------------------------------------------------------- + + If .some returns a MultipleMatchError you can potentially recover from it. + This indicates all projectIds were valid but some/all returned 403 errors. + To recover: + + 1. Change the user used by the API to one which has access to some/all of + the inaccessible projectIds. + 2. Re-call this command again. + 3. Merge and de-duplicate results. + 4. Repeat until all projects have been received for projectIds. + + If any project has "No Access" set to all users it will not be possible for + any user to read it without changing its access level. + */ + // swiftlint:disable:next cyclomatic_complexity + public func projects(_ config: Config, completionHandler: @escaping(Outcome<[Project]?, MatchError, ErrorContainer>>) -> Void) { + switch config.projects { + case .none: + completionHandler(.succeeded(nil)) + case .all: + getProjects { (outcome) in + switch outcome { + case .failed(let error): + completionHandler(.failed(.otherError(ErrorContainer(error)))) + case .succeeded(let projects): + completionHandler(.succeeded(projects)) + } + } + case .some(let projectIds): + getProjects(projectIds) { (outcomes) in + + var projects = [Project]() + var inaccessibleProjectIds = Set() + var non403Errors = [GetError]() + + for (projectId, outcome) in outcomes { + switch outcome { + case .failed(let getError): + if case let .statusCodeError(.clientError(clientError)) = getError, clientError.statusCode == 403 { + inaccessibleProjectIds.insert(projectId) // 403 errors. + } else { + non403Errors.append(getError) + } + case .succeeded(let project): + projects.append(project) + } + } + + // Fail if there are any non-403 errors. + if let errorContainer = ErrorContainer(non403Errors) { + completionHandler(.failed(.otherError(errorContainer))) + return + } + + // If some/all of the projectIds were 403 return any matches and + // all missing. + guard inaccessibleProjectIds.count == 0 else { + if projects.count == 0 { + completionHandler(.failed(.matchError(.noMatchesFound(missing: inaccessibleProjectIds)))) + } else { + completionHandler(.failed(.matchError(.partialMatchesFound(matches: projects, missing: inaccessibleProjectIds)))) + } + return + } + + // All projectIds were found. + completionHandler(.succeeded(projects)) + } + } + } + + // MARK: - Configuration + + public func configurationGroup(_ configuration: Configuration, completionHandler: @escaping (Outcome, ErrorContainer>>) -> Void) { + getConfigurationGroup(matching: configuration.groupId, completionHandler: completionHandler) + } + + // MARK: - ConfigurationGroup + + public func project(_ configurationGroup: ConfigurationGroup, completionHandler: @escaping (Outcome) -> Void) { + getProject(configurationGroup.projectId, completionHandler: completionHandler) + } + + // MARK: - Milestone + + public func parent(_ milestone: Milestone, completionHandler: @escaping (Outcome) -> Void) { + parent(milestone.parentId, completionHandler: completionHandler) + } + + public func project(_ milestone: Milestone, completionHandler: @escaping (Outcome) -> Void) { + getProject(milestone.projectId, completionHandler: completionHandler) + } + + // MARK: - Plan + + public func assignedto(_ plan: Plan, completionHandler: @escaping (Outcome) -> Void) { + assignedto(plan.assignedtoId, completionHandler: completionHandler) + } + + public func createdBy(_ plan: Plan, completionHandler: @escaping (Outcome) -> Void) { + getUser(plan.createdBy, completionHandler: completionHandler) + } + + public func milestone(_ plan: Plan, completionHandler: @escaping (Outcome) -> Void) { + milestone(plan.milestoneId, completionHandler: completionHandler) + } + + public func project(_ plan: Plan, completionHandler: @escaping (Outcome) -> Void) { + getProject(plan.projectId, completionHandler: completionHandler) + } + + // MARK: - Plan.Entry + + public func suite(_ planEntry: Plan.Entry, completionHandler: @escaping (Outcome) -> Void) { + getSuite(planEntry.suiteId, completionHandler: completionHandler) + } + + // MARK: - Result + + public func assignedto(_ result: Result, completionHandler: @escaping (Outcome) -> Void) { + assignedto(result.assignedtoId, completionHandler: completionHandler) + } + + public func createdBy(_ result: Result, completionHandler: @escaping (Outcome) -> Void) { + getUser(result.createdBy, completionHandler: completionHandler) + } + + public func status(_ result: Result, completionHandler: @escaping (Outcome, GetError>>) -> Void) { + status(matching: result.statusId, completionHandler: completionHandler) + } + + public func test(_ result: Result, completionHandler: @escaping (Outcome) -> Void) { + getTest(result.testId, completionHandler: completionHandler) + } + + // MARK: - ResultField + + public func templates(_ resultField: ResultField, completionHandler: @escaping (Outcome<[Template], MatchError, ErrorContainer>>) -> Void) { + getTemplates(matching: resultField.templateIds, completionHandler: completionHandler) + } + + // MARK: - Run + + public func assignedto(_ run: Run, completionHandler: @escaping (Outcome) -> Void) { + assignedto(run.assignedtoId, completionHandler: completionHandler) + } + + public func configurations(_ run: Run, completionHandler: @escaping (Outcome<[Configuration]?, MatchError, GetError>>) -> Void) { + configurations(inProjectWithId: run.projectId, matching: run.configIds, completionHandler: completionHandler) + } + + public func createdBy(_ run: Run, completionHandler: @escaping (Outcome) -> Void) { + getUser(run.createdBy, completionHandler: completionHandler) + } + + public func milestone(_ run: Run, completionHandler: @escaping (Outcome) -> Void) { + milestone(run.milestoneId, completionHandler: completionHandler) + } + + public func plan(_ run: Run, completionHandler: @escaping (Outcome) -> Void) { + plan(run.planId, completionHandler: completionHandler) + } + + public func project(_ run: Run, completionHandler: @escaping (Outcome) -> Void) { + getProject(run.projectId, completionHandler: completionHandler) + } + + public func suite(_ run: Run, completionHandler: @escaping (Outcome) -> Void) { + suite(run.suiteId, completionHandler: completionHandler) + } + + // MARK: - Section + + public func parent(_ section: Section, completionHandler: @escaping (Outcome) -> Void) { + parent(section.parentId, completionHandler: completionHandler) + } + + public func suite(_ section: Section, completionHandler: @escaping (Outcome) -> Void) { + suite(section.suiteId, completionHandler: completionHandler) + } + + // MARK: - Suite + + public func project(_ suite: Suite, completionHandler: @escaping (Outcome) -> Void) { + getProject(suite.projectId, completionHandler: completionHandler) + } + + // MARK: - Test + + public func assignedto(_ test: Test, completionHandler: @escaping (Outcome) -> Void) { + assignedto(test.assignedtoId, completionHandler: completionHandler) + } + + public func `case`(_ test: Test, completionHandler: @escaping (Outcome) -> Void) { + getCase(test.caseId, completionHandler: completionHandler) + } + + public func milestone(_ test: Test, completionHandler: @escaping (Outcome) -> Void) { + milestone(test.milestoneId, completionHandler: completionHandler) + } + + public func priority(_ test: Test, completionHandler: @escaping (Outcome, GetError>>) -> Void) { + getPriority(matching: test.priorityId, completionHandler: completionHandler) + } + + public func run(_ test: Test, completionHandler: @escaping (Outcome) -> Void) { + getRun(test.runId, completionHandler: completionHandler) + } + + public func status(_ test: Test, completionHandler: @escaping (Outcome, GetError>>) -> Void) { + getStatus(matching: test.statusId, completionHandler: completionHandler) + } + + public func template(_ test: Test, completionHandler: @escaping (Outcome, ErrorContainer>>) -> Void) { + getTemplate(matching: test.templateId, completionHandler: completionHandler) + } + + public func type(_ test: Test, completionHandler: @escaping (Outcome, GetError>>) -> Void) { + getCaseType(matching: test.typeId, completionHandler: completionHandler) + } + + // MARK: - Private Helpers + + // MARK: GET + + private func assignedto(_ userId: User.Id?, completionHandler: @escaping (Outcome) -> Void) { + if let userId = userId { + getUser(userId) { (outcome) in + switch outcome { + case .failed(let error): + completionHandler(.failed(error)) + case .succeeded(let user): + completionHandler(.succeeded(user)) + } + } + } else { + completionHandler(.succeeded(nil)) + } + } + + private func milestone(_ milestoneId: Milestone.Id?, completionHandler: @escaping (Outcome) -> Void) { + if let milestoneId = milestoneId { + getMilestone(milestoneId) { (outcome) in + switch outcome { + case .failed(let error): + completionHandler(.failed(error)) + case .succeeded(let milestone): + completionHandler(.succeeded(milestone)) + } + } + } else { + completionHandler(.succeeded(nil)) + } + } + + private func milestone(_ milestoneId: Milestone.Id, completionHandler: @escaping (Outcome) -> Void) { + getMilestone(milestoneId) { (outcome) in + switch outcome { + case .failed(let error): + completionHandler(.failed(error)) + case .succeeded(let milestone): + completionHandler(.succeeded(milestone)) + } + } + } + + private func parent(_ milestoneId: Milestone.Id?, completionHandler: @escaping (Outcome) -> Void) { + milestone(milestoneId, completionHandler: completionHandler) + } + + private func parent(_ milestoneId: Milestone.Id, completionHandler: @escaping (Outcome) -> Void) { + milestone(milestoneId, completionHandler: completionHandler) + } + + private func parent(_ sectionId: Section.Id?, completionHandler: @escaping (Outcome) -> Void) { + section(sectionId, completionHandler: completionHandler) + } + + private func plan(_ planId: Plan.Id?, completionHandler: @escaping (Outcome) -> Void) { + if let planId = planId { + getPlan(planId) { (outcome) in + switch outcome { + case .failed(let error): + completionHandler(.failed(error)) + case .succeeded(let plan): + completionHandler(.succeeded(plan)) + } + } + } else { + completionHandler(.succeeded(nil)) + } + } + + private func section(_ sectionId: Section.Id?, completionHandler: @escaping (Outcome) -> Void) { + if let sectionId = sectionId { + getSection(sectionId) { (outcome) in + switch outcome { + case .failed(let error): + completionHandler(.failed(error)) + case .succeeded(let section): + completionHandler(.succeeded(section)) + } + } + } else { + completionHandler(.succeeded(nil)) + } + } + + private func section(_ sectionId: Section.Id, completionHandler: @escaping (Outcome) -> Void) { + getSection(sectionId) { (outcome) in + switch outcome { + case .failed(let error): + completionHandler(.failed(error)) + case .succeeded(let section): + completionHandler(.succeeded(section)) + } + } + } + + private func suite(_ suiteId: Suite.Id?, completionHandler: @escaping (Outcome) -> Void) { + if let suiteId = suiteId { + getSuite(suiteId) { (outcome) in + switch outcome { + case .failed(let error): + completionHandler(.failed(error)) + case .succeeded(let suite): + completionHandler(.succeeded(suite)) + } + } + } else { + completionHandler(.succeeded(nil)) + } + } + + // MARK: Matching + + private func configurations(inProjectWithId projectId: Project.Id, matching configurationIds: [Configuration.Id]?, completionHandler: @escaping (Outcome<[Configuration]?, MatchError, GetError>>) -> Void) { + let uniqueConfigurationIds: Set? + if let configurationIds = configurationIds { + uniqueConfigurationIds = Set(configurationIds) + } else { + uniqueConfigurationIds = nil + } + configurations(inProjectWithId: projectId, matching: uniqueConfigurationIds, completionHandler: completionHandler) + } + + private func configurations(inProjectWithId projectId: Project.Id, matching configurationIds: [Configuration.Id], completionHandler: @escaping (Outcome<[Configuration], MatchError, GetError>>) -> Void) { + let uniqueIds = Set(configurationIds) + configurations(inProjectWithId: projectId, matching: uniqueIds, completionHandler: completionHandler) + } + + private func configurations(inProjectWithId projectId: Project.Id, matching configurationIds: Set?, completionHandler: @escaping (Outcome<[Configuration]?, MatchError, GetError>>) -> Void) { + if let ids = configurationIds { + getConfigurationGroups(inProjectWithId: projectId) { (outcome) in + switch outcome { + case .failed(let error): + completionHandler(.failed(.otherError(error))) + case .succeeded(let configurationGroups): + let configurations = configurationGroups.flatMap { $0.configs } + let matchesOutcome = ObjectAPI.matches(from: configurations, matchingIds: ids) + switch matchesOutcome { + case .failed(let error): + completionHandler(.failed(.matchError(error))) + case .succeeded(let matches): + completionHandler(.succeeded(matches)) + } + } + } + } else { + completionHandler(.succeeded(nil)) + } + } + + private func configurations(inProjectWithId projectId: Project.Id, matching configurationIds: Set, completionHandler: @escaping (Outcome<[Configuration], MatchError, GetError>>) -> Void) { + getConfigurationGroups(inProjectWithId: projectId) { (outcome) in + switch outcome { + case .failed(let error): + completionHandler(.failed(.otherError(error))) + case .succeeded(let configurationGroups): + let configurations = configurationGroups.flatMap { $0.configs } + let matchesOutcome = ObjectAPI.matches(from: configurations, matchingIds: configurationIds) + switch matchesOutcome { + case .failed(let error): + completionHandler(.failed(.matchError(error))) + case .succeeded(let matches): + completionHandler(.succeeded(matches)) + } + } + } + } + + private func status(matching id: Status.Id?, completionHandler: @escaping (Outcome, GetError>>) -> Void) { + if let id = id { + getStatus(matching: id) { (outcome) in + switch outcome { + case .failed(let error): + completionHandler(.failed(error)) + case .succeeded(let status): + completionHandler(.succeeded(status)) + } + } + } else { + completionHandler(.succeeded(nil)) + } + } + + private func status(matching id: Status.Id, completionHandler: @escaping (Outcome, GetError>>) -> Void) { + getStatus(matching: id) { (outcome) in + switch outcome { + case .failed(let error): + completionHandler(.failed(error)) + case .succeeded(let status): + completionHandler(.succeeded(status)) + } + } + } + +} diff --git a/QuizTrain/Network/Operations/GetConfigurationGroupsOperation.swift b/QuizTrain/Network/Operations/GetConfigurationGroupsOperation.swift new file mode 100644 index 0000000..318d186 --- /dev/null +++ b/QuizTrain/Network/Operations/GetConfigurationGroupsOperation.swift @@ -0,0 +1,46 @@ +import Foundation + +class GetConfigurationGroupsOperation: AsyncOperation { + + // MARK: Properties + + private weak var _api: ObjectAPI? + var api: ObjectAPI? { return _api } + let projectId: Project.Id + + // MARK: Init + + init(api: ObjectAPI, projectId: Project.Id) { + self._api = api + self.projectId = projectId + } + + // MARK: Execution + + override func start() { + + guard isCancelled == false else { + state = .finished + return + } + + guard let api = api else { + cancel() + state = .finished + return + } + + state = .executing + + api.getConfigurationGroups(inProjectWithId: projectId) { [weak self] (outcome) in + self?._outcome = outcome + self?.state = .finished + } + } + + // MARK: Completion + + private var _outcome: Outcome<[ConfigurationGroup], ObjectAPI.GetError>? + var outcome: Outcome<[ConfigurationGroup], ObjectAPI.GetError>? { return _outcome } + +} diff --git a/QuizTrain/Network/Operations/GetProjectOperation.swift b/QuizTrain/Network/Operations/GetProjectOperation.swift new file mode 100644 index 0000000..9efa811 --- /dev/null +++ b/QuizTrain/Network/Operations/GetProjectOperation.swift @@ -0,0 +1,46 @@ +import Foundation + +class GetProjectOperation: AsyncOperation { + + // MARK: Properties + + private weak var _api: ObjectAPI? + var api: ObjectAPI? { return _api } + let projectId: Project.Id + + // MARK: Init + + init(api: ObjectAPI, projectId: Project.Id) { + self._api = api + self.projectId = projectId + } + + // MARK: Execution + + override func start() { + + guard isCancelled == false else { + state = .finished + return + } + + guard let api = api else { + cancel() + state = .finished + return + } + + state = .executing + + api.getProject(projectId) { [weak self] (outcome) in + self?._outcome = outcome + self?.state = .finished + } + } + + // MARK: Completion + + private var _outcome: Outcome? + var outcome: Outcome? { return _outcome } + +} diff --git a/QuizTrain/Network/Operations/GetTemplatesOperation.swift b/QuizTrain/Network/Operations/GetTemplatesOperation.swift new file mode 100644 index 0000000..41ae3f2 --- /dev/null +++ b/QuizTrain/Network/Operations/GetTemplatesOperation.swift @@ -0,0 +1,46 @@ +import Foundation + +class GetTemplatesOperation: AsyncOperation { + + // MARK: Properties + + private weak var _api: ObjectAPI? + var api: ObjectAPI? { return _api } + let projectId: Project.Id + + // MARK: Init + + init(api: ObjectAPI, projectId: Project.Id) { + self._api = api + self.projectId = projectId + } + + // MARK: Execution + + override func start() { + + guard isCancelled == false else { + state = .finished + return + } + + guard let api = api else { + cancel() + state = .finished + return + } + + state = .executing + + api.getTemplates(inProjectWithId: projectId) { [weak self] (outcome) in + self?._outcome = outcome + self?.state = .finished + } + } + + // MARK: Completion + + private var _outcome: Outcome<[Template], ObjectAPI.GetError>? + var outcome: Outcome<[Template], ObjectAPI.GetError>? { return _outcome } + +} diff --git a/QuizTrain/QuizTrain.h b/QuizTrain/QuizTrain.h new file mode 100644 index 0000000..4868660 --- /dev/null +++ b/QuizTrain/QuizTrain.h @@ -0,0 +1,37 @@ +// +// QuizTrain.h +// QuizTrain +// +// Created by David Gallagher (david.gallagher@venmo.com). +// Copyright © 2018 Venmo. All rights reserved. +// +// _____LICENSE_____ +// +// Copyright 2018 Venmo +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +#import + +//! Project version number for QuizTrain. +FOUNDATION_EXPORT double QuizTrainVersionNumber; + +//! Project version string for QuizTrain. +FOUNDATION_EXPORT const unsigned char QuizTrainVersionString[]; diff --git a/QuizTrainTests/.swiftlint.yml b/QuizTrainTests/.swiftlint.yml new file mode 100644 index 0000000..511bf9f --- /dev/null +++ b/QuizTrainTests/.swiftlint.yml @@ -0,0 +1,3 @@ +disabled_rules: + - force_cast + - type_name diff --git a/QuizTrainTests/Info.plist b/QuizTrainTests/Info.plist new file mode 100644 index 0000000..6c40a6c --- /dev/null +++ b/QuizTrainTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/QuizTrainTests/Misc/Containment/Containers/CustomFieldsContainerTests.swift b/QuizTrainTests/Misc/Containment/Containers/CustomFieldsContainerTests.swift new file mode 100644 index 0000000..557c7aa --- /dev/null +++ b/QuizTrainTests/Misc/Containment/Containers/CustomFieldsContainerTests.swift @@ -0,0 +1,228 @@ +import XCTest +@testable import QuizTrain + +// MARK: - Tests + +class CustomFieldsContainerTests: XCTestCase { + + typealias Object = CustomFieldsContainer + + func testInit() { + + XCTAssertEqual(Object(json: customFields).customFields.count, customFields.count) + + for (k, v) in customFields { + XCTAssertEqual(Object(json: [k: v]).customFields.count, 1) + } + } + + func testInitWithEmptyCustomFields() { + XCTAssertEqual(Object(json: emptyCustomFields).customFields.count, 0) + } + + func testInitWithInvalidCustomFields() { + + XCTAssertEqual(Object(json: invalidCustomFields).customFields.count, 0) + + for (k, v) in invalidCustomFields { + XCTAssertEqual(Object(json: [k: v]).customFields.count, 0) + } + } + + func testInitWithValidAndInvalidCustomFields() { + + let object = Object(json: validAndInvalidCustomFields) + XCTAssertEqual(object.customFields.count, customFields.count) + + for (k, _) in object.customFields { + XCTAssertTrue(customFieldsKeys.contains(k)) + XCTAssertFalse(invalidCustomFieldsKeys.contains(k)) + } + } + + func testInitOmittingCustomKeys() { + + XCTAssertEqual(Object(json: customFields, omittingKeys: []).customFields.count, customFields.count) + XCTAssertEqual(Object(json: customFields, omittingKeys: customFieldsKeys).customFields.count, 0) + + for key in customFieldsKeys { + let object = Object(json: customFields, omittingKeys: [key]) + XCTAssertEqual(object.customFields.count, customFields.count - 1) + XCTAssertNil(object.customFields[key]) + } + } + + func testInitOmittingCustomKeysWithEmptyCustomFields() { + XCTAssertEqual(Object(json: emptyCustomFields, omittingKeys: []).customFields.count, 0) + XCTAssertEqual(Object(json: emptyCustomFields, omittingKeys: customFieldsKeys).customFields.count, 0) + } + + func testInitOmittingCustomKeysWithInvalidCustomFields() { + XCTAssertEqual(Object(json: invalidCustomFields, omittingKeys: []).customFields.count, 0) + XCTAssertEqual(Object(json: invalidCustomFields, omittingKeys: customFieldsKeys).customFields.count, 0) + XCTAssertEqual(Object(json: invalidCustomFields, omittingKeys: invalidCustomFieldsKeys).customFields.count, 0) + } + + func testInitOmittingCustomKeysWithValidAndInvalidCustomFields() { + XCTAssertEqual(Object(json: validAndInvalidCustomFields, omittingKeys: []).customFields.count, customFields.count) + XCTAssertEqual(Object(json: validAndInvalidCustomFields, omittingKeys: invalidCustomFieldsKeys).customFields.count, customFields.count) + XCTAssertEqual(Object(json: validAndInvalidCustomFields, omittingKeys: customFieldsKeys).customFields.count, 0) + } + + func testJSONDeserializing() { + assertJSONDeserializing(type: Object.self, from: customFields) + assertJSONDeserializing(type: Object.self, from: [customFields, customFields, customFields]) + } + + func testJSONSerializing() { + + let objectA = Object(json: customFields) + + assertJSONSerializing(objectA) + assertJSONSerializing([objectA, objectA, objectA]) + + var objectB = Object(json: customFields) + objectB.customFields["custom_addingANewCustomField"] = "Howdy!" + + assertJSONSerializing(objectB) + assertJSONSerializing([objectB, objectB, objectB]) + } + + func testJSONTwoWaySerialization() { + + assertJSONTwoWaySerialization(customFields) + assertJSONTwoWaySerialization([customFields, customFields, customFields]) + + let objectA = Object(json: customFields) + + assertJSONTwoWaySerialization(objectA) + assertJSONTwoWaySerialization([objectA, objectA, objectA]) + + var objectB = Object(json: customFields) + objectB.customFields["custom_addingANewCustomField"] = "Howdy!" + + assertJSONTwoWaySerialization(objectB) + assertJSONTwoWaySerialization([objectB, objectB, objectB]) + } + + func testEquatable() { + + let objectA = Object(json: customFields) + var objectB = Object(json: customFields) + let objectC = Object(json: ["custom_field": "Hi"]) + + XCTAssertEqual(objectA, objectA) + XCTAssertEqual(objectA, objectB) + XCTAssertNotEqual(objectA, objectC) + + objectB.customFields["custom_addingANewCustomField"] = "Howdy!" + XCTAssertNotEqual(objectA, objectB) + objectB.customFields.removeValue(forKey: "custom_addingANewCustomField") + XCTAssertEqual(objectA, objectB) + + let key = customFieldsKeys.first! + objectB.customFields[key] = "New Value" + XCTAssertNotEqual(objectA, objectB) + objectB.customFields[key] = customFields[key] + XCTAssertEqual(objectA, objectB) + } + + func testAddingCustomFields() { + + // Valid + + var objectA = Object(json: customFields) + objectA.customFields["custom_validKey"] = 6000 + + XCTAssertEqual(objectA.customFields.count, customFields.count + 1) + XCTAssertNotNil(objectA.customFields["custom_validKey"]) + XCTAssertEqual(objectA.customFields["custom_validKey"] as! Int, 6000) + + // Invalid + + var objectB = Object(json: customFields) + objectB.customFields["This is not a valid custom_ key"] = "At least it better be!" + + XCTAssertEqual(objectB.customFields.count, customFields.count) + XCTAssertNil(objectA.customFields["This is not a valid custom_ key"]) + + // Overwrite + + var objectC = Object(json: customFields) + let key = customFieldsKeys.first! + objectC.customFields[key] = "New Value" + + XCTAssertEqual(objectC.customFields.count, customFields.count) + XCTAssertEqual(objectC.customFields[key] as! String, "New Value") + + objectC.customFields[key] = customFields[key] + XCTAssertEqual(objectC.customFields[key] as! Int, customFields[key] as! Int) + } + + func testAddingCustomFieldsWithOmittedKeys() { + + let omittedKeys: [JSONKey] = ["custom_hamsters", "custom_grubb"] + var object = Object(json: customFields, omittingKeys: omittedKeys) + + XCTAssertEqual(object.omittedKeys, omittedKeys) + XCTAssertEqual(object.customFields.count, customFields.count) + + object.customFields["custom_hamsters"] = "🐹🐹🐹" + XCTAssertNil(object.customFields["custom_hamsters"]) + XCTAssertEqual(object.customFields.count, customFields.count) + + object.customFields["custom_grubb"] = "🐛" + XCTAssertNil(object.customFields["custom_grubb"]) + XCTAssertEqual(object.customFields.count, customFields.count) + + object.customFields["custom_dolphin"] = "🐬" + XCTAssertNotNil(object.customFields["custom_dolphin"]) + XCTAssertEqual(object.customFields.count, customFields.count + 1) + } + + func testRemovingCustomFields() { + + var object = Object(json: customFields) + let key = customFieldsKeys.first! + + object.customFields.removeValue(forKey: key) + + XCTAssertNil(object.customFields[key]) + XCTAssertEqual(object.customFields.count, customFields.count - 1) + } + + func testRemovingAndAddingCustomFields() { + + var object = Object(json: customFields) + let key = customFieldsKeys.first! + let value = object.customFields[key]! + + object.customFields.removeValue(forKey: key) + + XCTAssertNil(object.customFields[key]) + XCTAssertNotEqual(object.customFields.count, customFields.count) + + object.customFields[key] = value + + XCTAssertNotNil(object.customFields[key]) + XCTAssertEqual(object.customFields.count, customFields.count) + } + + func testEmpty() { + let object = Object.empty() + XCTAssertEqual(object.customFields.count, 0) + } + +} + +// MARK: - Data + +extension CustomFieldsContainerTests: CustomFieldsDataProvider { } + +// MARK: - Assertions + +extension CustomFieldsContainerTests: AssertJSONDeserializing { } + +extension CustomFieldsContainerTests: AssertJSONSerializing { } + +extension CustomFieldsContainerTests: AssertJSONTwoWaySerialization { } diff --git a/QuizTrainTests/Misc/Containment/Containers/ErrorContainerTests.swift b/QuizTrainTests/Misc/Containment/Containers/ErrorContainerTests.swift new file mode 100644 index 0000000..304e2b9 --- /dev/null +++ b/QuizTrainTests/Misc/Containment/Containers/ErrorContainerTests.swift @@ -0,0 +1,84 @@ +import XCTest +@testable import QuizTrain + +// MARK: - Tests + +class ErrorContainerTests: XCTestCase { + + enum TestError: Error { + case errorCaseA + case errorCaseB + case errorCaseC + } + + enum TestErrorDebugDescription: String, Error, DebugDescription { + + case errorCaseA + case errorCaseB + case errorCaseC + + var debugDescription: String { + return self.rawValue + } + } + + func testSingleError() { + + let error = TestError.errorCaseB + let container = ErrorContainer(error) + + XCTAssertEqual(container.errors.count, 1) + XCTAssertTrue(container.errors.contains(error)) + } + + func testMultipleErrors() { + + let errors = [TestError.errorCaseC, TestError.errorCaseB] + + guard let container = ErrorContainer(errors) else { + XCTFail("ErrorContainer cannot be nil when initialized with 1+ errors: \(errors)") + return + } + + XCTAssertEqual(container.errors.count, errors.count) + for error in errors { + XCTAssertTrue(container.errors.contains(error)) + } + } + + func testNoErrors() { + + let errors = [TestError]() + let container = ErrorContainer(errors) + + XCTAssertNil(container) + } + + func testDebugDescription() { + + // Simple Errors + + let simpleErrors = [TestError.errorCaseC, TestError.errorCaseB] + guard let containerA = ErrorContainer(simpleErrors) else { + XCTFail("Container cannot be nil.") + return + } + + XCTAssertGreaterThan(containerA.debugDescription.count, 0) + + // Errors conforming to DebugDescription + + let debugDescriptionErrors = [TestErrorDebugDescription.errorCaseC, TestErrorDebugDescription.errorCaseB] + guard let containerB = ErrorContainer(debugDescriptionErrors) else { + XCTFail("Container cannot be nil.") + return + } + + XCTAssertGreaterThan(containerB.debugDescription.count, 0) + + for debugDescriptionError in debugDescriptionErrors { + XCTAssertNotNil(containerB.debugDescription.range(of: debugDescriptionError.rawValue)) + } + } + +} diff --git a/QuizTrainTests/Misc/Containment/Containers/JSONDictionaryContainerTests.swift b/QuizTrainTests/Misc/Containment/Containers/JSONDictionaryContainerTests.swift new file mode 100644 index 0000000..8080b8d --- /dev/null +++ b/QuizTrainTests/Misc/Containment/Containers/JSONDictionaryContainerTests.swift @@ -0,0 +1,141 @@ +import XCTest +@testable import QuizTrain + +// MARK: - Tests + +class JSONDictionaryContainerTests: XCTestCase { + + typealias Object = JSONDictionaryContainer + + func testInit() { + + XCTAssertEqual(Object(json: json).json.count, json.count) + + for (k, v) in json { + XCTAssertEqual(Object(json: [k: v]).json.count, 1) + } + } + + func testInitWithEmptyJSON() { + XCTAssertEqual(Object(json: [:]).json.count, 0) + } + + func testJSONDeserializing() { + assertJSONDeserializing(type: Object.self, from: json) + assertJSONDeserializing(type: Object.self, from: [json, json, json]) + } + + func testJSONSerializing() { + + let objectA = Object(json: json) + + assertJSONSerializing(objectA) + assertJSONSerializing([objectA, objectA, objectA]) + + var objectB = Object(json: json) + objectB.json["A new JSON Key"] = "Howdy!" + + assertJSONSerializing(objectB) + assertJSONSerializing([objectB, objectB, objectB]) + } + + func testJSONTwoWaySerialization() { + + assertJSONTwoWaySerialization(json) + assertJSONTwoWaySerialization([json, json, json]) + + let objectA = Object(json: json) + + assertJSONTwoWaySerialization(objectA) + assertJSONTwoWaySerialization([objectA, objectA, objectA]) + + var objectB = Object(json: json) + objectB.json["A new JSON Key"] = "Howdy!" + + assertJSONTwoWaySerialization(objectB) + assertJSONTwoWaySerialization([objectB, objectB, objectB]) + } + + func testEquatable() { + + let objectA = Object(json: json) + var objectB = Object(json: json) + let objectC = Object(json: ["Key": "Value!"]) + + XCTAssertEqual(objectA, objectA) + XCTAssertEqual(objectA, objectB) + XCTAssertNotEqual(objectA, objectC) + + objectB.json["A new JSON Key"] = "Howdy!" + XCTAssertNotEqual(objectA, objectB) + objectB.json.removeValue(forKey: "A new JSON Key") + XCTAssertEqual(objectA, objectB) + + let key = json.keys.first! + objectB.json[key] = "New Value" + XCTAssertNotEqual(objectA, objectB) + objectB.json[key] = json[key] + XCTAssertEqual(objectA, objectB) + } + + func testAddingJSON() { + + var object = Object(json: json) + let key: JSONKey = "A new JSON Key" + object.json[key] = 6000 + + XCTAssertEqual(object.json.count, json.count + 1) + XCTAssertNotNil(object.json[key]) + XCTAssertEqual(object.json[key] as! Int, 6000) + } + + func testRemovingJSON() { + + var object = Object(json: json) + let key: JSONKey = json.keys.first! + + object.json.removeValue(forKey: key) + + XCTAssertNil(object.json[key]) + XCTAssertEqual(object.json.count, json.count - 1) + } + + func testAddingAndRemovingJSON() { + + var object = Object(json: json) + let key = json.keys.first! + let value = object.json[key]! + + object.json.removeValue(forKey: key) + + XCTAssertNil(object.json[key]) + XCTAssertNotEqual(object.json.count, json.count) + + object.json[key] = value + + XCTAssertNotNil(object.json[key]) + XCTAssertEqual(object.json.count, json.count) + } + +} + +// MARK: - Data + +extension JSONDictionaryContainerTests { + + var json: JSONDictionary { + return ["Hello": "World!", + "Pie": 3.14, + "Array": [1, 2.0, "3", "4️⃣"], + "Dictionary": ["Three": "🐹🐹🐹", "Party like it's": 1999]] + } + +} + +// MARK: - Assertions + +extension JSONDictionaryContainerTests: AssertJSONDeserializing { } + +extension JSONDictionaryContainerTests: AssertJSONSerializing { } + +extension JSONDictionaryContainerTests: AssertJSONTwoWaySerialization { } diff --git a/QuizTrainTests/Misc/Extensions/Array+ContentComparisonTests.swift b/QuizTrainTests/Misc/Extensions/Array+ContentComparisonTests.swift new file mode 100644 index 0000000..ae38d20 --- /dev/null +++ b/QuizTrainTests/Misc/Extensions/Array+ContentComparisonTests.swift @@ -0,0 +1,42 @@ +import XCTest +@testable import QuizTrain + +// MARK: - Tests + +class Array_ContentComparisonTests: XCTestCase { + + func testContentsOfArrayAreEqualToArray() { + + let arrayA = [1, 2, 3] + + XCTAssertTrue(arrayA.contentsAreEqual(to: arrayA)) + + let arrayB = [1, 2, 3] + + XCTAssertTrue(arrayA.contentsAreEqual(to: arrayB)) + + let arrayC = [3, 2, 1] + + XCTAssertNotEqual(arrayA, arrayC) + XCTAssertTrue(arrayA.contentsAreEqual(to: arrayC)) + + let arrayD = [1, 2] + + XCTAssertFalse(arrayA.contentsAreEqual(to: arrayD)) + + let arrayE = [1, 2, 3, 4] + + XCTAssertFalse(arrayA.contentsAreEqual(to: arrayE)) + + let arrayF: [Int]? = nil + + XCTAssertFalse(arrayA.contentsAreEqual(to: arrayF)) + XCTAssertTrue(Array.contentsAreEqual(arrayF, arrayF)) + + let arrayG = [1, 1, 2] + let arrayH = [1, 2, 2] + + XCTAssertFalse(arrayG.contentsAreEqual(to: arrayH)) + } + +} diff --git a/QuizTrainTests/Misc/Extensions/Array+RandomTests.swift b/QuizTrainTests/Misc/Extensions/Array+RandomTests.swift new file mode 100644 index 0000000..efa757f --- /dev/null +++ b/QuizTrainTests/Misc/Extensions/Array+RandomTests.swift @@ -0,0 +1,40 @@ +import XCTest +@testable import QuizTrain + +// MARK: - Tests + +class Array_RandomTests: XCTestCase { + + func testRandomIndex() { + + let arrayA = [Int]() + XCTAssertNil(arrayA.randomIndex) + + let arrayB = [1] + XCTAssertEqual(arrayB.randomIndex, 0) + + let arrayC = [1, 2, 3] + let randomIndex = arrayC.randomIndex + XCTAssertNotNil(randomIndex) + if let randomIndex = randomIndex { + XCTAssertLessThan(randomIndex, arrayC.count) + } + } + + func testRandomElement() { + + let arrayA = [Int]() + XCTAssertNil(arrayA.randomElement) + + let arrayB = [1] + XCTAssertEqual(arrayB.randomElement, 1) + + let arrayC = [1, 2, 3] + let randomElement = arrayC.randomElement + XCTAssertNotNil(randomElement) + if let randomElement = randomElement { + XCTAssertTrue(arrayC.contains(randomElement)) + } + } + +} diff --git a/QuizTrainTests/Misc/Extensions/Equatable+OptionalArrayTests.swift b/QuizTrainTests/Misc/Extensions/Equatable+OptionalArrayTests.swift new file mode 100644 index 0000000..f42dea9 --- /dev/null +++ b/QuizTrainTests/Misc/Extensions/Equatable+OptionalArrayTests.swift @@ -0,0 +1,25 @@ +import XCTest +@testable import QuizTrain + +// MARK: - Tests + +class Equatable_OptionalArrayTests: XCTestCase { + + func testEquatableWithOptionalArrays() { + + let arrayA = [1, 2, 3] + let arrayB = [1, 2, 3] + let arrayC = [4, 5, 6] + let arrayD: [Int]? = nil + let arrayE: [Int]? = nil + let arrayF: [Int]? = [1, 2, 3] + + XCTAssertTrue(arrayA == arrayA) + XCTAssertTrue(arrayA == arrayB) + XCTAssertFalse(arrayA == arrayC) + XCTAssertFalse(arrayA == arrayD) + XCTAssertTrue(arrayD == arrayE) + XCTAssertTrue(arrayA == arrayF) + } + +} diff --git a/QuizTrainTests/Misc/Operations/AsyncOperationTests.swift b/QuizTrainTests/Misc/Operations/AsyncOperationTests.swift new file mode 100644 index 0000000..57b4a6e --- /dev/null +++ b/QuizTrainTests/Misc/Operations/AsyncOperationTests.swift @@ -0,0 +1,26 @@ +import XCTest +@testable import QuizTrain + +// MARK: - Tests + +class AsyncOperationTests: XCTestCase { + + func testIsAsynchronous() { + let operation = AsyncOperation() + XCTAssertTrue(operation.isAsynchronous) + } + + func testState() { + + let operation = AsyncOperation() + + XCTAssertTrue(operation.isReady) + + operation.state = .executing + XCTAssertTrue(operation.isExecuting) + + operation.state = .finished + XCTAssertTrue(operation.isFinished) + } + +} diff --git a/QuizTrainTests/Models/CaseFieldTests.swift b/QuizTrainTests/Models/CaseFieldTests.swift new file mode 100644 index 0000000..e864486 --- /dev/null +++ b/QuizTrainTests/Models/CaseFieldTests.swift @@ -0,0 +1,190 @@ +import XCTest +@testable import QuizTrain + +// MARK: - Tests + +class CaseFieldTests: XCTestCase, ModelTests { + + typealias Object = CaseField + + func testEquatable() { + _testEquatable() + } + + func testInit() { + _testInit() + } + + func testInitWithOptionalProperties() { + _testInitWithOptionalProperties() + } + + func testJSONDeserializing() { + _testJSONDeserializing() + } + + func testJSONDeserializingWithOptionalProperties() { + _testJSONDeserializingWithOptionalProperties() + } + + func testJSONDeserializingASingleObject() { + _testJSONDeserializingASingleObject() + } + + func testJSONDeserializingMultipleObjects() { + _testJSONDeserializingMultipleObjects() + } + + func testJSONDeserializingASingleObjectMissingRequiredProperties() { + _testJSONDeserializingASingleObjectMissingRequiredProperties() + } + + func testJSONDeserializingMultipleObjectsMissingRequiredProperties() { + _testJSONDeserializingMultipleObjectsMissingRequiredProperties() + } + + func testJSONSerializingSingleObjects() { + _testJSONSerializingSingleObjects() + } + + func testJSONSerializingMultipleObjects() { + _testJSONSerializingMultipleObjects() + } + + func testJSONTwoWaySerializationForSingleItems() { + _testJSONTwoWaySerializationForSingleItems() + } + + func testJSONTwoWaySerializationForMultipleItems() { + _testJSONTwoWaySerializationForMultipleItems() + } + + func testVariableProperties() { + _testVariableProperties() + } + + func testUpdateRequestJSON() { + _testUpdateRequestJSON() + } + +} + +// MARK: - Data + +extension CaseFieldTests { + + struct Properties { + + struct Required { + static let configs = [ConfigTests.objectWithRequiredAndOptionalPropertiesFromJSON!, ConfigTests.objectWithRequiredPropertiesFromJSON!, ConfigTests.objectWithRequiredAndOptionalPropertiesFromJSON!] // This must match the order and datasources in: JSON.required["configs"] + static let displayOrder = 10 + static let id = 11 + static let includeAll = true + static let isActive = true + static let label = "Label" + static let name = "Name" + static let systemName = "System Name" + static let templateIds = [12, 13, 14, 15, 16] + static let typeId = CustomFieldType.text + } + + struct Optional { + static let description = "Description" + } + + } + +} + +extension CaseFieldTests: JSONDataProvider { + + static var requiredJSON: JSONDictionary { + return [Object.JSONKeys.configs.rawValue: [ConfigTests.requiredAndOptionalJSON, ConfigTests.requiredJSON, ConfigTests.requiredAndOptionalJSON], // This must match the order and datasources in: Properties.Required.configs + Object.JSONKeys.displayOrder.rawValue: Properties.Required.displayOrder, + Object.JSONKeys.id.rawValue: Properties.Required.id, + Object.JSONKeys.includeAll.rawValue: Properties.Required.includeAll, + Object.JSONKeys.isActive.rawValue: Properties.Required.isActive, + Object.JSONKeys.label.rawValue: Properties.Required.label, + Object.JSONKeys.name.rawValue: Properties.Required.name, + Object.JSONKeys.systemName.rawValue: Properties.Required.systemName, + Object.JSONKeys.templateIds.rawValue: Properties.Required.templateIds, + Object.JSONKeys.typeId.rawValue: Properties.Required.typeId.rawValue] + } + + static var optionalJSON: JSONDictionary { + return [Object.JSONKeys.description.rawValue: Properties.Optional.description] + } + +} + +// MARK: - Objects + +extension CaseFieldTests: ObjectProvider { + + static var objectWithRequiredProperties: Object { + return Object(configs: Properties.Required.configs, + description: nil, + displayOrder: Properties.Required.displayOrder, + id: Properties.Required.id, + includeAll: Properties.Required.includeAll, + isActive: Properties.Required.isActive, + label: Properties.Required.label, + name: Properties.Required.name, + systemName: Properties.Required.systemName, + templateIds: Properties.Required.templateIds, + typeId: Properties.Required.typeId) + } + + static var objectWithRequiredAndOptionalProperties: Object { + return Object(configs: Properties.Required.configs, + description: Properties.Optional.description, + displayOrder: Properties.Required.displayOrder, + id: Properties.Required.id, + includeAll: Properties.Required.includeAll, + isActive: Properties.Required.isActive, + label: Properties.Required.label, + name: Properties.Required.name, + systemName: Properties.Required.systemName, + templateIds: Properties.Required.templateIds, + typeId: Properties.Required.typeId) + } + +} + +// MARK: - Assertions + +extension CaseFieldTests: AssertEquatable { } + +extension CaseFieldTests: AssertJSONDeserializing { } + +extension CaseFieldTests: AssertJSONSerializing { } + +extension CaseFieldTests: AssertJSONTwoWaySerialization { } + +extension CaseFieldTests: AssertProperties { + + func assertRequiredProperties(in object: Object) { + XCTAssertEqual(object.configs.count, Properties.Required.configs.count) + XCTAssertEqual(object.displayOrder, Properties.Required.displayOrder) + XCTAssertEqual(object.id, Properties.Required.id) + XCTAssertEqual(object.includeAll, Properties.Required.includeAll) + XCTAssertEqual(object.isActive, Properties.Required.isActive) + XCTAssertEqual(object.label, Properties.Required.label) + XCTAssertEqual(object.name, Properties.Required.name) + XCTAssertEqual(object.systemName, Properties.Required.systemName) + XCTAssertEqual(object.templateIds, Properties.Required.templateIds) + XCTAssertEqual(object.typeId, Properties.Required.typeId) + } + + func assertOptionalProperties(in object: Object, areNil: Bool) { + if areNil { + XCTAssertNil(object.description) + } else { + XCTAssertNotNil(object.description) + XCTAssertEqual(object.description, Properties.Optional.description) + } + } + + func assertVariablePropertiesCanBeChanged(in object: inout Object) { /* none */ } + +} diff --git a/QuizTrainTests/Models/CaseTests.swift b/QuizTrainTests/Models/CaseTests.swift new file mode 100644 index 0000000..4383299 --- /dev/null +++ b/QuizTrainTests/Models/CaseTests.swift @@ -0,0 +1,278 @@ +import XCTest +@testable import QuizTrain + +// MARK: - Tests + +class CaseTests: XCTestCase, ModelTests { + + typealias Object = Case + + func testEquatable() { + _testEquatable() + } + + func testInit() { + _testInit() + } + + func testInitWithOptionalProperties() { + _testInitWithOptionalProperties() + } + + func testJSONDeserializing() { + _testJSONDeserializing() + } + + func testJSONDeserializingWithOptionalProperties() { + _testJSONDeserializingWithOptionalProperties() + } + + func testJSONDeserializingASingleObject() { + _testJSONDeserializingASingleObject() + } + + func testJSONDeserializingMultipleObjects() { + _testJSONDeserializingMultipleObjects() + } + + func testJSONDeserializingASingleObjectMissingRequiredProperties() { + _testJSONDeserializingASingleObjectMissingRequiredProperties() + } + + func testJSONDeserializingMultipleObjectsMissingRequiredProperties() { + _testJSONDeserializingMultipleObjectsMissingRequiredProperties() + } + + func testJSONSerializingSingleObjects() { + _testJSONSerializingSingleObjects() + } + + func testJSONSerializingMultipleObjects() { + _testJSONSerializingMultipleObjects() + } + + func testJSONTwoWaySerializationForSingleItems() { + _testJSONTwoWaySerializationForSingleItems() + } + + func testJSONTwoWaySerializationForMultipleItems() { + _testJSONTwoWaySerializationForMultipleItems() + } + + func testVariableProperties() { + _testVariableProperties() + } + + func testUpdateRequestJSON() { + _testUpdateRequestJSON() + } + +} + +// MARK: - Data + +extension CaseTests { + + struct Properties { + + struct Required { + static let createdBy = 10 + static let createdOn = Date(secondsSince1970: 72973833) + static let id = 11 + static let priorityId = 12 + static let templateId = 13 + static let title = "Name" + static let typeId = 14 + static let updatedBy = 15 + static let updatedOn = Date(secondsSince1970: 72988400) + } + + struct Optional { + static let estimate = "2hr, 3min" + static let estimateForecast = "3hr" + static let milestoneId = 16 + static let refs = "1,2,3" + static let sectionId = 17 + static let suiteId = 18 + } + + } + +} + +extension CaseTests: CustomFieldsDataProvider { } + +extension CaseTests: JSONDataProvider { + + static var requiredJSON: JSONDictionary { + return [Object.JSONKeys.createdBy.rawValue: Properties.Required.createdBy, + Object.JSONKeys.createdOn.rawValue: Properties.Required.createdOn.secondsSince1970, + Object.JSONKeys.id.rawValue: Properties.Required.id, + Object.JSONKeys.priorityId.rawValue: Properties.Required.priorityId, + Object.JSONKeys.templateId.rawValue: Properties.Required.templateId, + Object.JSONKeys.title.rawValue: Properties.Required.title, + Object.JSONKeys.typeId.rawValue: Properties.Required.typeId, + Object.JSONKeys.updatedBy.rawValue: Properties.Required.updatedBy, + Object.JSONKeys.updatedOn.rawValue: Properties.Required.updatedOn.secondsSince1970] + } + + static var optionalJSON: JSONDictionary { + return [Object.JSONKeys.estimate.rawValue: Properties.Optional.estimate, + Object.JSONKeys.estimateForecast.rawValue: Properties.Optional.estimateForecast, + Object.JSONKeys.milestoneId.rawValue: Properties.Optional.milestoneId, + Object.JSONKeys.refs.rawValue: Properties.Optional.refs, + Object.JSONKeys.sectionId.rawValue: Properties.Optional.sectionId, + Object.JSONKeys.suiteId.rawValue: Properties.Optional.suiteId] + } + +} + +// MARK: - Objects + +extension CaseTests: ObjectProvider { + + static var objectWithRequiredProperties: Object { + return Object(createdBy: Properties.Required.createdBy, + createdOn: Properties.Required.createdOn, + estimate: nil, + estimateForecast: nil, + id: Properties.Required.id, + milestoneId: nil, + priorityId: Properties.Required.priorityId, + refs: nil, + sectionId: nil, + suiteId: nil, + templateId: Properties.Required.templateId, + title: Properties.Required.title, + typeId: Properties.Required.typeId, + updatedBy: Properties.Required.updatedBy, + updatedOn: Properties.Required.updatedOn, + customFieldsContainer: emptyCustomFieldsContainer) + } + + static var objectWithRequiredAndOptionalProperties: Object { + return Object(createdBy: Properties.Required.createdBy, + createdOn: Properties.Required.createdOn, + estimate: Properties.Optional.estimate, + estimateForecast: Properties.Optional.estimateForecast, + id: Properties.Required.id, + milestoneId: Properties.Optional.milestoneId, + priorityId: Properties.Required.priorityId, + refs: Properties.Optional.refs, + sectionId: Properties.Optional.sectionId, + suiteId: Properties.Optional.suiteId, + templateId: Properties.Required.templateId, + title: Properties.Required.title, + typeId: Properties.Required.typeId, + updatedBy: Properties.Required.updatedBy, + updatedOn: Properties.Required.updatedOn, + customFieldsContainer: customFieldsContainer) + } + +} + +// MARK: - Assertions + +extension CaseTests: AssertCustomFields { } + +extension CaseTests: AssertEquatable { } + +extension CaseTests: AssertJSONDeserializing { } + +extension CaseTests: AssertJSONSerializing { } + +extension CaseTests: AssertJSONTwoWaySerialization { } + +extension CaseTests: AssertProperties { + + func assertRequiredProperties(in object: Object) { + XCTAssertEqual(object.createdBy, Properties.Required.createdBy) + XCTAssertEqual(object.createdOn, Properties.Required.createdOn) + XCTAssertEqual(object.id, Properties.Required.id) + XCTAssertEqual(object.priorityId, Properties.Required.priorityId) + XCTAssertEqual(object.templateId, Properties.Required.templateId) + XCTAssertEqual(object.title, Properties.Required.title) + XCTAssertEqual(object.typeId, Properties.Required.typeId) + XCTAssertEqual(object.updatedBy, Properties.Required.updatedBy) + XCTAssertEqual(object.updatedOn, Properties.Required.updatedOn) + } + + func assertOptionalProperties(in object: Object, areNil: Bool) { + if areNil { + XCTAssertNil(object.estimate) + XCTAssertNil(object.estimateForecast) + XCTAssertNil(object.milestoneId) + XCTAssertNil(object.refs) + XCTAssertNil(object.sectionId) + XCTAssertNil(object.suiteId) + } else { + XCTAssertNotNil(object.estimate) + XCTAssertNotNil(object.estimateForecast) + XCTAssertNotNil(object.milestoneId) + XCTAssertNotNil(object.refs) + XCTAssertNotNil(object.sectionId) + XCTAssertNotNil(object.suiteId) + XCTAssertEqual(object.estimate, Properties.Optional.estimate) + XCTAssertEqual(object.estimateForecast, Properties.Optional.estimateForecast) + XCTAssertEqual(object.milestoneId, Properties.Optional.milestoneId) + XCTAssertEqual(object.refs, Properties.Optional.refs) + XCTAssertEqual(object.sectionId, Properties.Optional.sectionId) + XCTAssertEqual(object.suiteId, Properties.Optional.suiteId) + } + } + + func assertVariablePropertiesCanBeChanged(in object: inout Object) { + + // Properties + + object.estimate = "New Estimate" + object.milestoneId = 999 + object.priorityId = 9999 + object.refs = "9,9,9" + object.templateId = 99999 + object.title = "New Title" + object.typeId = 999999 + + XCTAssertNotEqual(object.estimate, Properties.Optional.estimate) + XCTAssertNotEqual(object.milestoneId, Properties.Optional.milestoneId) + XCTAssertNotEqual(object.priorityId, Properties.Required.priorityId) + XCTAssertNotEqual(object.refs, Properties.Optional.refs) + XCTAssertNotEqual(object.templateId, Properties.Required.templateId) + XCTAssertNotEqual(object.title, Properties.Required.title) + XCTAssertNotEqual(object.typeId, Properties.Required.typeId) + XCTAssertEqual(object.estimate, "New Estimate") + XCTAssertEqual(object.milestoneId, 999) + XCTAssertEqual(object.priorityId, 9999) + XCTAssertEqual(object.refs, "9,9,9") + XCTAssertEqual(object.templateId, 99999) + XCTAssertEqual(object.title, "New Title") + XCTAssertEqual(object.typeId, 999999) + + // Custom Fields + + let customFieldsCount = object.customFields.count + + object.customFields["custom_field_test01"] = "Custom Field Test 01" + object.customFields["custom_field_test02"] = 9000 + object.customFields["custom_field_test03"] = -8.0 + object.customFields["invalid_custom_field_test04"] = "This should not be added." + + XCTAssertNotNil(object.customFields["custom_field_test01"]) + XCTAssertNotNil(object.customFields["custom_field_test02"]) + XCTAssertNotNil(object.customFields["custom_field_test03"]) + XCTAssertNil(object.customFields["invalid_custom_field_test04"]) + XCTAssertEqual(object.customFields["custom_field_test01"] as! String, "Custom Field Test 01") + XCTAssertEqual(object.customFields["custom_field_test02"] as! Int, 9000) + XCTAssertEqual(object.customFields["custom_field_test03"] as! Double, -8.0) + + XCTAssertEqual(object.customFields.count, customFieldsCount + 3) + + object.customFields.removeValue(forKey: "custom_field_test01") + + XCTAssertNil(object.customFields["custom_field_test01"]) + XCTAssertEqual(object.customFields.count, customFieldsCount + 2) + } + +} + +extension CaseTests: AssertUpdateRequestJSON { } diff --git a/QuizTrainTests/Models/CaseTypeTests.swift b/QuizTrainTests/Models/CaseTypeTests.swift new file mode 100644 index 0000000..fe67d75 --- /dev/null +++ b/QuizTrainTests/Models/CaseTypeTests.swift @@ -0,0 +1,146 @@ +import XCTest +@testable import QuizTrain + +// MARK: - Tests + +class CaseTypeTests: XCTestCase, ModelTests { + + typealias Object = CaseType + + func testEquatable() { + _testEquatable() + } + + func testInit() { + _testInit() + } + + func testInitWithOptionalProperties() { + _testInitWithOptionalProperties() + } + + func testJSONDeserializing() { + _testJSONDeserializing() + } + + func testJSONDeserializingWithOptionalProperties() { + _testJSONDeserializingWithOptionalProperties() + } + + func testJSONDeserializingASingleObject() { + _testJSONDeserializingASingleObject() + } + + func testJSONDeserializingMultipleObjects() { + _testJSONDeserializingMultipleObjects() + } + + func testJSONDeserializingASingleObjectMissingRequiredProperties() { + _testJSONDeserializingASingleObjectMissingRequiredProperties() + } + + func testJSONDeserializingMultipleObjectsMissingRequiredProperties() { + _testJSONDeserializingMultipleObjectsMissingRequiredProperties() + } + + func testJSONSerializingSingleObjects() { + _testJSONSerializingSingleObjects() + } + + func testJSONSerializingMultipleObjects() { + _testJSONSerializingMultipleObjects() + } + + func testJSONTwoWaySerializationForSingleItems() { + _testJSONTwoWaySerializationForSingleItems() + } + + func testJSONTwoWaySerializationForMultipleItems() { + _testJSONTwoWaySerializationForMultipleItems() + } + + func testVariableProperties() { + _testVariableProperties() + } + + func testUpdateRequestJSON() { + _testUpdateRequestJSON() + } + +} + +// MARK: - Data + +extension CaseTypeTests { + + struct Properties { + + struct Required { + static let id = 10 + static let isDefault = true + static let name = "Name" + } + + struct Optional { + // none + } + + } + +} + +extension CaseTypeTests: JSONDataProvider { + + static var requiredJSON: JSONDictionary { + return [Object.JSONKeys.id.rawValue: Properties.Required.id, + Object.JSONKeys.isDefault.rawValue: Properties.Required.isDefault, + Object.JSONKeys.name.rawValue: Properties.Required.name] + } + + static var optionalJSON: JSONDictionary { + return [:] // none + } + +} + +// MARK: - Objects + +extension CaseTypeTests: ObjectProvider { + + static var objectWithRequiredProperties: Object { + return Object(id: Properties.Required.id, + isDefault: Properties.Required.isDefault, + name: Properties.Required.name) + } + + static var objectWithRequiredAndOptionalProperties: Object { + return Object(id: Properties.Required.id, + isDefault: Properties.Required.isDefault, + name: Properties.Required.name) + } + +} + +// MARK: - Assertions + +extension CaseTypeTests: AssertEquatable { } + +extension CaseTypeTests: AssertJSONDeserializing { } + +extension CaseTypeTests: AssertJSONSerializing { } + +extension CaseTypeTests: AssertJSONTwoWaySerialization { } + +extension CaseTypeTests: AssertProperties { + + func assertRequiredProperties(in object: Object) { + XCTAssertEqual(object.id, Properties.Required.id) + XCTAssertEqual(object.isDefault, Properties.Required.isDefault) + XCTAssertEqual(object.name, Properties.Required.name) + } + + func assertOptionalProperties(in object: Object, areNil: Bool) { /* none */ } + + func assertVariablePropertiesCanBeChanged(in object: inout Object) { /* none */ } + +} diff --git a/QuizTrainTests/Models/ConfigurationGroupTests.swift b/QuizTrainTests/Models/ConfigurationGroupTests.swift new file mode 100644 index 0000000..7ea7b8b --- /dev/null +++ b/QuizTrainTests/Models/ConfigurationGroupTests.swift @@ -0,0 +1,157 @@ +import XCTest +@testable import QuizTrain + +// MARK: - Tests + +class ConfigurationGroupTests: XCTestCase, ModelTests { + + typealias Object = ConfigurationGroup + + func testEquatable() { + _testEquatable() + } + + func testInit() { + _testInit() + } + + func testInitWithOptionalProperties() { + _testInitWithOptionalProperties() + } + + func testJSONDeserializing() { + _testJSONDeserializing() + } + + func testJSONDeserializingWithOptionalProperties() { + _testJSONDeserializingWithOptionalProperties() + } + + func testJSONDeserializingASingleObject() { + _testJSONDeserializingASingleObject() + } + + func testJSONDeserializingMultipleObjects() { + _testJSONDeserializingMultipleObjects() + } + + func testJSONDeserializingASingleObjectMissingRequiredProperties() { + _testJSONDeserializingASingleObjectMissingRequiredProperties() + } + + func testJSONDeserializingMultipleObjectsMissingRequiredProperties() { + _testJSONDeserializingMultipleObjectsMissingRequiredProperties() + } + + func testJSONSerializingSingleObjects() { + _testJSONSerializingSingleObjects() + } + + func testJSONSerializingMultipleObjects() { + _testJSONSerializingMultipleObjects() + } + + func testJSONTwoWaySerializationForSingleItems() { + _testJSONTwoWaySerializationForSingleItems() + } + + func testJSONTwoWaySerializationForMultipleItems() { + _testJSONTwoWaySerializationForMultipleItems() + } + + func testVariableProperties() { + _testVariableProperties() + } + + func testUpdateRequestJSON() { + _testUpdateRequestJSON() + } + +} + +// MARK: - Data + +extension ConfigurationGroupTests { + + struct Properties { + + struct Required { + static let configs = [ConfigurationTests.objectWithRequiredAndOptionalPropertiesFromJSON!, ConfigurationTests.objectWithRequiredPropertiesFromJSON!, ConfigurationTests.objectWithRequiredAndOptionalPropertiesFromJSON!] // This must match the order and datasources in: JSON.required["configs"] + static let id = 10 + static let name = "Name" + static let projectId = 11 + } + + struct Optional { + // none + } + + } + +} + +extension ConfigurationGroupTests: JSONDataProvider { + + static var requiredJSON: JSONDictionary { + return [Object.JSONKeys.configs.rawValue: [ConfigurationTests.requiredAndOptionalJSON, ConfigurationTests.requiredJSON, ConfigurationTests.requiredAndOptionalJSON], // This must match the order and datasources in: Properties.Required.configs + Object.JSONKeys.id.rawValue: Properties.Required.id, + Object.JSONKeys.name.rawValue: Properties.Required.name, + Object.JSONKeys.projectId.rawValue: Properties.Required.projectId] + } + + static var optionalJSON: JSONDictionary { + return [:] // none + } + +} + +// MARK: - Objects + +extension ConfigurationGroupTests: ObjectProvider { + + static var objectWithRequiredProperties: Object { + return Object(configs: Properties.Required.configs, + id: Properties.Required.id, + name: Properties.Required.name, + projectId: Properties.Required.projectId) + } + + static var objectWithRequiredAndOptionalProperties: Object { + return Object(configs: Properties.Required.configs, + id: Properties.Required.id, + name: Properties.Required.name, + projectId: Properties.Required.projectId) + } + +} + +// MARK: - Assertions + +extension ConfigurationGroupTests: AssertEquatable { } + +extension ConfigurationGroupTests: AssertJSONDeserializing { } + +extension ConfigurationGroupTests: AssertJSONSerializing { } + +extension ConfigurationGroupTests: AssertJSONTwoWaySerialization { } + +extension ConfigurationGroupTests: AssertProperties { + + func assertRequiredProperties(in object: Object) { + XCTAssertEqual(object.configs.count, Properties.Required.configs.count) + XCTAssertEqual(object.id, Properties.Required.id) + XCTAssertEqual(object.name, Properties.Required.name) + XCTAssertEqual(object.projectId, Properties.Required.projectId) + } + + func assertOptionalProperties(in object: Object, areNil: Bool) { /* none */ } + + func assertVariablePropertiesCanBeChanged(in object: inout Object) { + object.name = "New Name" + XCTAssertNotEqual(object.name, Properties.Required.name) + XCTAssertEqual(object.name, "New Name") + } + +} + +extension ConfigurationGroupTests: AssertUpdateRequestJSON { } diff --git a/QuizTrainTests/Models/ConfigurationTests.swift b/QuizTrainTests/Models/ConfigurationTests.swift new file mode 100644 index 0000000..19aa987 --- /dev/null +++ b/QuizTrainTests/Models/ConfigurationTests.swift @@ -0,0 +1,152 @@ +import XCTest +@testable import QuizTrain + +// MARK: - Tests + +class ConfigurationTests: XCTestCase, ModelTests { + + typealias Object = Configuration + + func testEquatable() { + _testEquatable() + } + + func testInit() { + _testInit() + } + + func testInitWithOptionalProperties() { + _testInitWithOptionalProperties() + } + + func testJSONDeserializing() { + _testJSONDeserializing() + } + + func testJSONDeserializingWithOptionalProperties() { + _testJSONDeserializingWithOptionalProperties() + } + + func testJSONDeserializingASingleObject() { + _testJSONDeserializingASingleObject() + } + + func testJSONDeserializingMultipleObjects() { + _testJSONDeserializingMultipleObjects() + } + + func testJSONDeserializingASingleObjectMissingRequiredProperties() { + _testJSONDeserializingASingleObjectMissingRequiredProperties() + } + + func testJSONDeserializingMultipleObjectsMissingRequiredProperties() { + _testJSONDeserializingMultipleObjectsMissingRequiredProperties() + } + + func testJSONSerializingSingleObjects() { + _testJSONSerializingSingleObjects() + } + + func testJSONSerializingMultipleObjects() { + _testJSONSerializingMultipleObjects() + } + + func testJSONTwoWaySerializationForSingleItems() { + _testJSONTwoWaySerializationForSingleItems() + } + + func testJSONTwoWaySerializationForMultipleItems() { + _testJSONTwoWaySerializationForMultipleItems() + } + + func testVariableProperties() { + _testVariableProperties() + } + + func testUpdateRequestJSON() { + _testUpdateRequestJSON() + } + +} + +// MARK: - Data + +extension ConfigurationTests { + + struct Properties { + + struct Required { + static let id = 10 + static let groupId = 11 + static let name = "Name" + } + + struct Optional { + // none + } + + } + +} + +extension ConfigurationTests: JSONDataProvider { + + static var requiredJSON: JSONDictionary { + return [Object.JSONKeys.id.rawValue: Properties.Required.id, + Object.JSONKeys.groupId.rawValue: Properties.Required.groupId, + Object.JSONKeys.name.rawValue: Properties.Required.name] + } + + static var optionalJSON: JSONDictionary { + return [:] // none + } + +} + +// MARK: - Objects + +extension ConfigurationTests: ObjectProvider { + + static var objectWithRequiredProperties: Object { + return Object(id: Properties.Required.id, + groupId: Properties.Required.groupId, + name: Properties.Required.name) + } + + static var objectWithRequiredAndOptionalProperties: Object { + return Object(id: Properties.Required.id, + groupId: Properties.Required.groupId, + name: Properties.Required.name) + } + +} + +// MARK: - Assertions + +extension ConfigurationTests: AssertEquatable { } + +extension ConfigurationTests: AssertJSONDeserializing { } + +extension ConfigurationTests: AssertJSONSerializing { } + +extension ConfigurationTests: AssertJSONTwoWaySerialization { } + +extension ConfigurationTests: AssertProperties { + + func assertRequiredProperties(in object: Object) { + XCTAssertEqual(object.id, Properties.Required.id) + XCTAssertEqual(object.groupId, Properties.Required.groupId) + XCTAssertEqual(object.name, Properties.Required.name) + } + + func assertOptionalProperties(in object: Object, areNil: Bool) { /* none */ } + + func assertVariablePropertiesCanBeChanged(in object: inout Object) { + object.name = "New Name" + XCTAssertNotEqual(object.name, Properties.Required.name) + XCTAssertEqual(object.name, "New Name") + } + +} + +extension ConfigurationTests: AssertUpdateRequestJSON { } diff --git a/QuizTrainTests/Models/Custom Fields/Config.ContextTests.swift b/QuizTrainTests/Models/Custom Fields/Config.ContextTests.swift new file mode 100644 index 0000000..ef97da8 --- /dev/null +++ b/QuizTrainTests/Models/Custom Fields/Config.ContextTests.swift @@ -0,0 +1,145 @@ +import XCTest +@testable import QuizTrain + +// MARK: - Tests + +class Config_ContextTests: XCTestCase, ModelTests { + + typealias Object = Config.Context + + func testEquatable() { + _testEquatable() + } + + func testInit() { + _testInit() + } + + func testInitWithOptionalProperties() { + _testInitWithOptionalProperties() + } + + func testJSONDeserializing() { + _testJSONDeserializing() + } + + func testJSONDeserializingWithOptionalProperties() { + _testJSONDeserializingWithOptionalProperties() + } + + func testJSONDeserializingASingleObject() { + _testJSONDeserializingASingleObject() + } + + func testJSONDeserializingMultipleObjects() { + _testJSONDeserializingMultipleObjects() + } + + func testJSONDeserializingASingleObjectMissingRequiredProperties() { + _testJSONDeserializingASingleObjectMissingRequiredProperties() + } + + func testJSONDeserializingMultipleObjectsMissingRequiredProperties() { + _testJSONDeserializingMultipleObjectsMissingRequiredProperties() + } + + func testJSONSerializingSingleObjects() { + _testJSONSerializingSingleObjects() + } + + func testJSONSerializingMultipleObjects() { + _testJSONSerializingMultipleObjects() + } + + func testJSONTwoWaySerializationForSingleItems() { + _testJSONTwoWaySerializationForSingleItems() + } + + func testJSONTwoWaySerializationForMultipleItems() { + _testJSONTwoWaySerializationForMultipleItems() + } + + func testVariableProperties() { + _testVariableProperties() + } + + func testUpdateRequestJSON() { + _testUpdateRequestJSON() + } + +} + +// MARK: - Data + +extension Config_ContextTests { + + struct Properties { + + struct Required { + static let isGlobal = true + } + + struct Optional { + static let projectIds = [1, 2, 3, 4, 5] + } + + } + +} + +extension Config_ContextTests: JSONDataProvider { + + static var requiredJSON: JSONDictionary { + return [Object.JSONKeys.isGlobal.rawValue: Properties.Required.isGlobal] + } + + static var optionalJSON: JSONDictionary { + return [Object.JSONKeys.projectIds.rawValue: Properties.Optional.projectIds] + } + +} + +// MARK: - Objects + +extension Config_ContextTests: ObjectProvider { + + static var objectWithRequiredProperties: Object { + return Object(isGlobal: Properties.Required.isGlobal, + projectIds: nil) + } + + static var objectWithRequiredAndOptionalProperties: Object { + return Object(isGlobal: Properties.Required.isGlobal, + projectIds: Properties.Optional.projectIds) + } + +} + +// MARK: - Assertions + +extension Config_ContextTests: AssertEquatable { } + +extension Config_ContextTests: AssertJSONDeserializing { } + +extension Config_ContextTests: AssertJSONSerializing { } + +extension Config_ContextTests: AssertJSONTwoWaySerialization { } + +extension Config_ContextTests: AssertProperties { + + func assertRequiredProperties(in object: Object) { + XCTAssertEqual(object.isGlobal, Properties.Required.isGlobal) + } + + func assertOptionalProperties(in object: Object, areNil: Bool) { + if areNil { + XCTAssertNil(object.projectIds) + } else { + XCTAssertNotNil(object.projectIds) + XCTAssertEqual(object.projectIds!, Properties.Optional.projectIds) + } + } + + func assertVariablePropertiesCanBeChanged(in object: inout Object) { /* none */ } + +} diff --git a/QuizTrainTests/Models/Custom Fields/ConfigTests.swift b/QuizTrainTests/Models/Custom Fields/ConfigTests.swift new file mode 100644 index 0000000..b158988 --- /dev/null +++ b/QuizTrainTests/Models/Custom Fields/ConfigTests.swift @@ -0,0 +1,182 @@ +import XCTest +@testable import QuizTrain + +// MARK: - Tests + +class ConfigTests: XCTestCase, ModelTests { + + typealias Object = Config + + func testEquatable() { + _testEquatable() + } + + func testInit() { + _testInit() + } + + func testInitWithOptionalProperties() { + _testInitWithOptionalProperties() + } + + func testJSONDeserializing() { + _testJSONDeserializing() + } + + func testJSONDeserializingWithOptionalProperties() { + _testJSONDeserializingWithOptionalProperties() + } + + func testJSONDeserializingASingleObject() { + _testJSONDeserializingASingleObject() + } + + func testJSONDeserializingMultipleObjects() { + _testJSONDeserializingMultipleObjects() + } + + func testJSONDeserializingASingleObjectMissingRequiredProperties() { + _testJSONDeserializingASingleObjectMissingRequiredProperties() + } + + func testJSONDeserializingMultipleObjectsMissingRequiredProperties() { + _testJSONDeserializingMultipleObjectsMissingRequiredProperties() + } + + func testJSONSerializingSingleObjects() { + _testJSONSerializingSingleObjects() + } + + func testJSONSerializingMultipleObjects() { + _testJSONSerializingMultipleObjects() + } + + func testJSONTwoWaySerializationForSingleItems() { + _testJSONTwoWaySerializationForSingleItems() + } + + func testJSONTwoWaySerializationForMultipleItems() { + _testJSONTwoWaySerializationForMultipleItems() + } + + func testVariableProperties() { + _testVariableProperties() + } + + func testUpdateRequestJSON() { + _testUpdateRequestJSON() + } + + func testProjects() { + + let contextA = Object.Context(isGlobal: true, projectIds: nil) + let objectA = Object(context: contextA, id: "id", optionsContainer: Config.OptionsContainer(json: ["test": "test"])) + assertProjects(in: objectA) + + let contextB = Object.Context(isGlobal: false, projectIds: nil) + let objectB = Object(context: contextB, id: "id", optionsContainer: Config.OptionsContainer(json: ["test": "test"])) + assertProjects(in: objectB) + + let contextC = Object.Context(isGlobal: false, projectIds: [1, 2, 3]) + let objectC = Object(context: contextC, id: "id", optionsContainer: Config.OptionsContainer(json: ["test": "test"])) + assertProjects(in: objectC) + } + +} + +// MARK: - Data + +extension ConfigTests { + + struct Properties { + + struct Required { + static let context = Config_ContextTests.objectWithRequiredAndOptionalPropertiesFromJSON! // This must match the order and datasources in: JSON.required["context"] + static let id = "id" + static let options: [String: Any] = ["optionA": true, "optionB": "Hello", "optionC": [1, 2.0, -3]] + } + + struct Optional { + // none + } + + } + +} + +extension ConfigTests: JSONDataProvider { + + static var requiredJSON: JSONDictionary { + return [Object.JSONKeys.context.rawValue: Config_ContextTests.requiredAndOptionalJSON, // This must match the order and datasources in: Properties.Required.context + Object.JSONKeys.id.rawValue: Properties.Required.id, + Object.JSONKeys.options.rawValue: Properties.Required.options] + } + + static var optionalJSON: JSONDictionary { + return [:] // none + } + +} + +// MARK: - Objects + +extension ConfigTests: ObjectProvider { + + static var objectWithRequiredProperties: Object { + return Object(context: Properties.Required.context, + id: Properties.Required.id, + optionsContainer: Config.OptionsContainer(json: Properties.Required.options)) + } + + static var objectWithRequiredAndOptionalProperties: Object { + return Object(context: Properties.Required.context, + id: Properties.Required.id, + optionsContainer: Config.OptionsContainer(json: Properties.Required.options)) + } + +} + +// MARK: - Assertions + +extension ConfigTests { + + func assertProjects(in object: Object) { + if object.context.isGlobal { + XCTAssertEqual(object.projects, UniqueSelection.all) + } else if object.context.projectIds == nil { + XCTAssertEqual(object.projects, UniqueSelection.none) + } else { + XCTAssertNotNil(object.context.projectIds) + if let projectIds = object.context.projectIds { + XCTAssertEqual(object.projects, UniqueSelection.some(Set(projectIds))) + } + } + } + +} + +extension ConfigTests: AssertEquatable { } + +extension ConfigTests: AssertJSONDeserializing { } + +extension ConfigTests: AssertJSONSerializing { } + +extension ConfigTests: AssertJSONTwoWaySerialization { } + +extension ConfigTests: AssertProperties { + + func assertRequiredProperties(in object: Object) { + XCTAssertNotNil(object.context) + XCTAssertEqual(object.context.isGlobal, Properties.Required.context.isGlobal) + XCTAssertNotNil(object.context.projectIds) + XCTAssertNotNil(Properties.Required.context.projectIds) + XCTAssertEqual(object.context.projectIds!, Properties.Required.context.projectIds!) + XCTAssertEqual(object.id, Properties.Required.id) + XCTAssertEqual(object.options.count, Properties.Required.options.count) + } + + func assertOptionalProperties(in object: Object, areNil: Bool) { /* none */ } + + func assertVariablePropertiesCanBeChanged(in object: inout Object) { /* none */ } + +} diff --git a/QuizTrainTests/Models/MilestoneTests.swift b/QuizTrainTests/Models/MilestoneTests.swift new file mode 100644 index 0000000..48d12d1 --- /dev/null +++ b/QuizTrainTests/Models/MilestoneTests.swift @@ -0,0 +1,239 @@ +import XCTest +@testable import QuizTrain + +// MARK: - Tests + +class MilestoneTests: XCTestCase, ModelTests { + + typealias Object = Milestone + + func testEquatable() { + _testEquatable() + } + + func testInit() { + _testInit() + } + + func testInitWithOptionalProperties() { + _testInitWithOptionalProperties() + } + + func testJSONDeserializing() { + _testJSONDeserializing() + } + + func testJSONDeserializingWithOptionalProperties() { + _testJSONDeserializingWithOptionalProperties() + } + + func testJSONDeserializingASingleObject() { + _testJSONDeserializingASingleObject() + } + + func testJSONDeserializingMultipleObjects() { + _testJSONDeserializingMultipleObjects() + } + + func testJSONDeserializingASingleObjectMissingRequiredProperties() { + _testJSONDeserializingASingleObjectMissingRequiredProperties() + } + + func testJSONDeserializingMultipleObjectsMissingRequiredProperties() { + _testJSONDeserializingMultipleObjectsMissingRequiredProperties() + } + + func testJSONSerializingSingleObjects() { + _testJSONSerializingSingleObjects() + } + + func testJSONSerializingMultipleObjects() { + _testJSONSerializingMultipleObjects() + } + + func testJSONTwoWaySerializationForSingleItems() { + _testJSONTwoWaySerializationForSingleItems() + } + + func testJSONTwoWaySerializationForMultipleItems() { + _testJSONTwoWaySerializationForMultipleItems() + } + + func testVariableProperties() { + _testVariableProperties() + } + + func testUpdateRequestJSON() { + _testUpdateRequestJSON() + } + +} + +// MARK: - Data + +extension MilestoneTests { + + struct Properties { + + struct Required { + static let id = 10 + static let isCompleted = true + static let isStarted = true + static let name = "Name" + static let projectId = 11 + static let url = URL(string: "https://www.testrail.com/")! + } + + struct Optional { + static let completedOn = Date(secondsSince1970: 72988302) + static let description = "Description" + static let dueOn = Date(secondsSince1970: 72988400) + static let milestones = [MilestoneTests.objectWithRequiredPropertiesFromJSON!, MilestoneTests.objectWithRequiredPropertiesFromJSON!, MilestoneTests.objectWithRequiredPropertiesFromJSON!] // This must match the order and datasources in: JSON.optionals["milestones"] + static let parentId = 12 + static let startOn = Date(secondsSince1970: 72977000) + static let startedOn = Date(secondsSince1970: 72977321) + } + + } + +} + +extension MilestoneTests: JSONDataProvider { + + static var requiredJSON: JSONDictionary { + return [Object.JSONKeys.id.rawValue: Properties.Required.id, + Object.JSONKeys.isCompleted.rawValue: Properties.Required.isCompleted, + Object.JSONKeys.isStarted.rawValue: Properties.Required.isStarted, + Object.JSONKeys.name.rawValue: Properties.Required.name, + Object.JSONKeys.projectId.rawValue: Properties.Required.projectId, + Object.JSONKeys.url.rawValue: Properties.Required.url.absoluteString] + } + + static var optionalJSON: JSONDictionary { + return [Object.JSONKeys.completedOn.rawValue: Properties.Optional.completedOn.secondsSince1970, + Object.JSONKeys.description.rawValue: Properties.Optional.description, + Object.JSONKeys.dueOn.rawValue: Properties.Optional.dueOn.secondsSince1970, + Object.JSONKeys.milestones.rawValue: [MilestoneTests.requiredJSON, MilestoneTests.requiredJSON, MilestoneTests.requiredJSON], // This must match the order and datasources in: Properties.Optional.milestones + Object.JSONKeys.parentId.rawValue: Properties.Optional.parentId, + Object.JSONKeys.startOn.rawValue: Properties.Optional.startOn.secondsSince1970, + Object.JSONKeys.startedOn.rawValue: Properties.Optional.startedOn.secondsSince1970] + } + +} + +// MARK: - Objects + +extension MilestoneTests: ObjectProvider { + + static var objectWithRequiredProperties: Object { + return Object(completedOn: nil, + description: nil, + dueOn: nil, + id: Properties.Required.id, + isCompleted: Properties.Required.isCompleted, + isStarted: Properties.Required.isStarted, + milestones: nil, + name: Properties.Required.name, + parentId: nil, + projectId: Properties.Required.projectId, + startOn: nil, + startedOn: nil, + url: Properties.Required.url) + } + + static var objectWithRequiredAndOptionalProperties: Object { + return Object(completedOn: Properties.Optional.completedOn, + description: Properties.Optional.description, + dueOn: Properties.Optional.dueOn, + id: Properties.Required.id, + isCompleted: Properties.Required.isCompleted, + isStarted: Properties.Required.isStarted, + milestones: Properties.Optional.milestones, + name: Properties.Required.name, + parentId: Properties.Optional.parentId, + projectId: Properties.Required.projectId, + startOn: Properties.Optional.startOn, + startedOn: Properties.Optional.startedOn, + url: Properties.Required.url) + } + +} + +// MARK: - Assertions + +extension MilestoneTests: AssertEquatable { } + +extension MilestoneTests: AssertJSONDeserializing { } + +extension MilestoneTests: AssertJSONSerializing { } + +extension MilestoneTests: AssertJSONTwoWaySerialization { } + +extension MilestoneTests: AssertProperties { + + func assertRequiredProperties(in object: Object) { + XCTAssertEqual(object.id, Properties.Required.id) + XCTAssertEqual(object.isCompleted, Properties.Required.isCompleted) + XCTAssertEqual(object.isStarted, Properties.Required.isStarted) + XCTAssertEqual(object.name, Properties.Required.name) + XCTAssertEqual(object.projectId, Properties.Required.projectId) + XCTAssertEqual(object.url, Properties.Required.url) + } + + func assertOptionalProperties(in object: Object, areNil: Bool) { + if areNil { + XCTAssertNil(object.completedOn) + XCTAssertNil(object.description) + XCTAssertNil(object.dueOn) + XCTAssertNil(object.milestones) + XCTAssertNil(object.parentId) + XCTAssertNil(object.startOn) + XCTAssertNil(object.startedOn) + } else { + XCTAssertNotNil(object.completedOn) + XCTAssertNotNil(object.description) + XCTAssertNotNil(object.dueOn) + XCTAssertNotNil(object.milestones) + XCTAssertNotNil(object.parentId) + XCTAssertNotNil(object.startOn) + XCTAssertNotNil(object.startedOn) + XCTAssertEqual(object.completedOn, Properties.Optional.completedOn) + XCTAssertEqual(object.description, Properties.Optional.description) + XCTAssertEqual(object.dueOn, Properties.Optional.dueOn) + XCTAssertEqual(object.milestones!, Properties.Optional.milestones) + XCTAssertEqual(object.parentId, Properties.Optional.parentId) + XCTAssertEqual(object.startOn, Properties.Optional.startOn) + XCTAssertEqual(object.startedOn, Properties.Optional.startedOn) + } + } + + func assertVariablePropertiesCanBeChanged(in object: inout Object) { + + object.description = "New Description" + object.dueOn = Date(secondsSince1970: 90000000) + object.isCompleted = false + object.isStarted = false + object.name = "New Name" + object.parentId = 9999 + object.startOn = Date(secondsSince1970: 80000000) + + XCTAssertNotEqual(object.description, Properties.Optional.description) + XCTAssertNotEqual(object.dueOn, Properties.Optional.dueOn) + XCTAssertNotEqual(object.isCompleted, Properties.Required.isCompleted) + XCTAssertNotEqual(object.isStarted, Properties.Required.isStarted) + XCTAssertNotEqual(object.name, Properties.Required.name) + XCTAssertNotEqual(object.parentId, Properties.Optional.parentId) + XCTAssertNotEqual(object.startOn, Properties.Optional.startOn) + + XCTAssertEqual(object.description, "New Description") + XCTAssertEqual(object.dueOn, Date(secondsSince1970: 90000000)) + XCTAssertEqual(object.isCompleted, false) + XCTAssertEqual(object.isStarted, false) + XCTAssertEqual(object.name, "New Name") + XCTAssertEqual(object.parentId, 9999) + XCTAssertEqual(object.startOn, Date(secondsSince1970: 80000000)) + } + +} + +extension MilestoneTests: AssertUpdateRequestJSON { } diff --git a/QuizTrainTests/Models/Plan.EntryTests.swift b/QuizTrainTests/Models/Plan.EntryTests.swift new file mode 100644 index 0000000..6ae824d --- /dev/null +++ b/QuizTrainTests/Models/Plan.EntryTests.swift @@ -0,0 +1,157 @@ +import XCTest +@testable import QuizTrain + +// MARK: - Tests + +class Plan_EntryTests: XCTestCase, ModelTests { + + typealias Object = Plan.Entry + + func testEquatable() { + _testEquatable() + } + + func testInit() { + _testInit() + } + + func testInitWithOptionalProperties() { + _testInitWithOptionalProperties() + } + + func testJSONDeserializing() { + _testJSONDeserializing() + } + + func testJSONDeserializingWithOptionalProperties() { + _testJSONDeserializingWithOptionalProperties() + } + + func testJSONDeserializingASingleObject() { + _testJSONDeserializingASingleObject() + } + + func testJSONDeserializingMultipleObjects() { + _testJSONDeserializingMultipleObjects() + } + + func testJSONDeserializingASingleObjectMissingRequiredProperties() { + _testJSONDeserializingASingleObjectMissingRequiredProperties() + } + + func testJSONDeserializingMultipleObjectsMissingRequiredProperties() { + _testJSONDeserializingMultipleObjectsMissingRequiredProperties() + } + + func testJSONSerializingSingleObjects() { + _testJSONSerializingSingleObjects() + } + + func testJSONSerializingMultipleObjects() { + _testJSONSerializingMultipleObjects() + } + + func testJSONTwoWaySerializationForSingleItems() { + _testJSONTwoWaySerializationForSingleItems() + } + + func testJSONTwoWaySerializationForMultipleItems() { + _testJSONTwoWaySerializationForMultipleItems() + } + + func testVariableProperties() { + _testVariableProperties() + } + + func testUpdateRequestJSON() { + _testUpdateRequestJSON() + } + +} + +// MARK: - Data + +extension Plan_EntryTests { + + struct Properties { + + struct Required { + static let id = "Id" + static let name = "Name" + static let runs = [RunTests.objectWithRequiredAndOptionalPropertiesFromJSON!, RunTests.objectWithRequiredPropertiesFromJSON!, RunTests.objectWithRequiredAndOptionalPropertiesFromJSON!] // This must match the order and datasources in: JSON.required["runs"] + static let suiteId = 10 + } + + struct Optional { + // none + } + + } + +} + +extension Plan_EntryTests: JSONDataProvider { + + static var requiredJSON: JSONDictionary { + return [Object.JSONKeys.id.rawValue: Properties.Required.id, + Object.JSONKeys.name.rawValue: Properties.Required.name, + Object.JSONKeys.runs.rawValue: [RunTests.requiredAndOptionalJSON, RunTests.requiredJSON, RunTests.requiredAndOptionalJSON], // This must match the order and datasources in: Properties.Required.runs + Object.JSONKeys.suiteId.rawValue: Properties.Required.suiteId] + } + + static var optionalJSON: JSONDictionary { + return [:] // none + } + +} + +// MARK: - Objects + +extension Plan_EntryTests: ObjectProvider { + + static var objectWithRequiredProperties: Object { + return Object(id: Properties.Required.id, + name: Properties.Required.name, + runs: Properties.Required.runs, + suiteId: Properties.Required.suiteId) + } + + static var objectWithRequiredAndOptionalProperties: Object { + return Object(id: Properties.Required.id, + name: Properties.Required.name, + runs: Properties.Required.runs, + suiteId: Properties.Required.suiteId) + } + +} + +// MARK: - Assertions + +extension Plan_EntryTests: AssertEquatable { } + +extension Plan_EntryTests: AssertJSONDeserializing { } + +extension Plan_EntryTests: AssertJSONSerializing { } + +extension Plan_EntryTests: AssertJSONTwoWaySerialization { } + +extension Plan_EntryTests: AssertProperties { + + func assertRequiredProperties(in object: Object) { + XCTAssertEqual(object.id, Properties.Required.id) + XCTAssertEqual(object.name, Properties.Required.name) + XCTAssertEqual(object.runs.count, Properties.Required.runs.count) + XCTAssertEqual(object.suiteId, Properties.Required.suiteId) + } + + func assertOptionalProperties(in object: Object, areNil: Bool) { /* none */ } + + func assertVariablePropertiesCanBeChanged(in object: inout Object) { + object.name = "New Name" + XCTAssertNotEqual(object.name, Properties.Required.name) + XCTAssertEqual(object.name, "New Name") + } + +} + +extension Plan_EntryTests: AssertUpdateRequestJSON { } diff --git a/QuizTrainTests/Models/PlanTests.swift b/QuizTrainTests/Models/PlanTests.swift new file mode 100644 index 0000000..e111eb8 --- /dev/null +++ b/QuizTrainTests/Models/PlanTests.swift @@ -0,0 +1,278 @@ +import XCTest +@testable import QuizTrain + +// MARK: - Tests + +class PlanTests: XCTestCase, ModelTests { + + typealias Object = Plan + + func testEquatable() { + _testEquatable() + } + + func testInit() { + _testInit() + } + + func testInitWithOptionalProperties() { + _testInitWithOptionalProperties() + } + + func testJSONDeserializing() { + _testJSONDeserializing() + } + + func testJSONDeserializingWithOptionalProperties() { + _testJSONDeserializingWithOptionalProperties() + } + + func testJSONDeserializingASingleObject() { + _testJSONDeserializingASingleObject() + } + + func testJSONDeserializingMultipleObjects() { + _testJSONDeserializingMultipleObjects() + } + + func testJSONDeserializingASingleObjectMissingRequiredProperties() { + _testJSONDeserializingASingleObjectMissingRequiredProperties() + } + + func testJSONDeserializingMultipleObjectsMissingRequiredProperties() { + _testJSONDeserializingMultipleObjectsMissingRequiredProperties() + } + + func testJSONSerializingSingleObjects() { + _testJSONSerializingSingleObjects() + } + + func testJSONSerializingMultipleObjects() { + _testJSONSerializingMultipleObjects() + } + + func testJSONTwoWaySerializationForSingleItems() { + _testJSONTwoWaySerializationForSingleItems() + } + + func testJSONTwoWaySerializationForMultipleItems() { + _testJSONTwoWaySerializationForMultipleItems() + } + + func testVariableProperties() { + _testVariableProperties() + } + + func testUpdateRequestJSON() { + _testUpdateRequestJSON() + } + +} + +// MARK: - Data + +extension PlanTests { + + struct Properties { + + struct Required { + static let blockedCount = 9 + static let createdBy = 10 + static let createdOn = Date(secondsSince1970: 72973833) + static let customStatus1Count = 11 + static let customStatus2Count = 12 + static let customStatus3Count = 13 + static let customStatus4Count = 14 + static let customStatus5Count = 15 + static let customStatus6Count = 16 + static let customStatus7Count = 17 + static let failedCount = 18 + static let id = 19 + static let isCompleted = true + static let name = "Name" + static let passedCount = 20 + static let projectId = 21 + static let retestCount = 22 + static let untestedCount = 23 + static let url = URL(string: "https://www.testrail.com/")! + } + + struct Optional { + static let assignedtoId = 24 + static let completedOn = Date(secondsSince1970: 72988302) + static let description = "Description" + static let entries = [Plan_EntryTests.objectWithRequiredAndOptionalPropertiesFromJSON!, Plan_EntryTests.objectWithRequiredPropertiesFromJSON!, Plan_EntryTests.objectWithRequiredAndOptionalPropertiesFromJSON!] // This must match the order and datasources in: JSON.optionals["entries"] + static let milestoneId = 25 + } + + } + +} + +extension PlanTests: JSONDataProvider { + + static var requiredJSON: JSONDictionary { + return [Object.JSONKeys.blockedCount.rawValue: Properties.Required.blockedCount, + Object.JSONKeys.createdBy.rawValue: Properties.Required.createdBy, + Object.JSONKeys.createdOn.rawValue: Properties.Required.createdOn.secondsSince1970, + Object.JSONKeys.customStatus1Count.rawValue: Properties.Required.customStatus1Count, + Object.JSONKeys.customStatus2Count.rawValue: Properties.Required.customStatus2Count, + Object.JSONKeys.customStatus3Count.rawValue: Properties.Required.customStatus3Count, + Object.JSONKeys.customStatus4Count.rawValue: Properties.Required.customStatus4Count, + Object.JSONKeys.customStatus5Count.rawValue: Properties.Required.customStatus5Count, + Object.JSONKeys.customStatus6Count.rawValue: Properties.Required.customStatus6Count, + Object.JSONKeys.customStatus7Count.rawValue: Properties.Required.customStatus7Count, + Object.JSONKeys.failedCount.rawValue: Properties.Required.failedCount, + Object.JSONKeys.id.rawValue: Properties.Required.id, + Object.JSONKeys.isCompleted.rawValue: Properties.Required.isCompleted, + Object.JSONKeys.name.rawValue: Properties.Required.name, + Object.JSONKeys.passedCount.rawValue: Properties.Required.passedCount, + Object.JSONKeys.projectId.rawValue: Properties.Required.projectId, + Object.JSONKeys.retestCount.rawValue: Properties.Required.retestCount, + Object.JSONKeys.untestedCount.rawValue: Properties.Required.untestedCount, + Object.JSONKeys.url.rawValue: Properties.Required.url.absoluteString] + } + + static var optionalJSON: JSONDictionary { + return [Object.JSONKeys.assignedtoId.rawValue: Properties.Optional.assignedtoId, + Object.JSONKeys.completedOn.rawValue: Properties.Optional.completedOn.secondsSince1970, + Object.JSONKeys.description.rawValue: Properties.Optional.description, + Object.JSONKeys.entries.rawValue: [Plan_EntryTests.requiredAndOptionalJSON, Plan_EntryTests.requiredJSON, Plan_EntryTests.requiredAndOptionalJSON], // This must match the order and datasources in: Properties.Optional.entries + Object.JSONKeys.milestoneId.rawValue: Properties.Optional.milestoneId] + } + +} + +// MARK: - Objects + +extension PlanTests: ObjectProvider { + + static var objectWithRequiredProperties: Object { + return Object(assignedtoId: nil, + blockedCount: Properties.Required.blockedCount, + completedOn: nil, + createdBy: Properties.Required.createdBy, + createdOn: Properties.Required.createdOn, + customStatus1Count: Properties.Required.customStatus1Count, + customStatus2Count: Properties.Required.customStatus2Count, + customStatus3Count: Properties.Required.customStatus3Count, + customStatus4Count: Properties.Required.customStatus4Count, + customStatus5Count: Properties.Required.customStatus5Count, + customStatus6Count: Properties.Required.customStatus6Count, + customStatus7Count: Properties.Required.customStatus7Count, + description: nil, + entries: nil, + failedCount: Properties.Required.failedCount, + id: Properties.Required.id, + isCompleted: Properties.Required.isCompleted, + milestoneId: nil, + name: Properties.Required.name, + passedCount: Properties.Required.passedCount, + projectId: Properties.Required.projectId, + retestCount: Properties.Required.retestCount, + untestedCount: Properties.Required.untestedCount, + url: Properties.Required.url) + } + + static var objectWithRequiredAndOptionalProperties: Object { + return Object(assignedtoId: Properties.Optional.assignedtoId, + blockedCount: Properties.Required.blockedCount, + completedOn: Properties.Optional.completedOn, + createdBy: Properties.Required.createdBy, + createdOn: Properties.Required.createdOn, + customStatus1Count: Properties.Required.customStatus1Count, + customStatus2Count: Properties.Required.customStatus2Count, + customStatus3Count: Properties.Required.customStatus3Count, + customStatus4Count: Properties.Required.customStatus4Count, + customStatus5Count: Properties.Required.customStatus5Count, + customStatus6Count: Properties.Required.customStatus6Count, + customStatus7Count: Properties.Required.customStatus7Count, + description: Properties.Optional.description, + entries: Properties.Optional.entries, + failedCount: Properties.Required.failedCount, + id: Properties.Required.id, + isCompleted: Properties.Required.isCompleted, + milestoneId: Properties.Optional.milestoneId, + name: Properties.Required.name, + passedCount: Properties.Required.passedCount, + projectId: Properties.Required.projectId, + retestCount: Properties.Required.retestCount, + untestedCount: Properties.Required.untestedCount, + url: Properties.Required.url) + } + +} + +// MARK: - Assertions + +extension PlanTests: AssertEquatable { } + +extension PlanTests: AssertJSONDeserializing { } + +extension PlanTests: AssertJSONSerializing { } + +extension PlanTests: AssertJSONTwoWaySerialization { } + +extension PlanTests: AssertProperties { + + func assertRequiredProperties(in object: Object) { + XCTAssertEqual(object.blockedCount, Properties.Required.blockedCount) + XCTAssertEqual(object.createdBy, Properties.Required.createdBy) + XCTAssertEqual(object.createdOn, Properties.Required.createdOn) + XCTAssertEqual(object.customStatus1Count, Properties.Required.customStatus1Count) + XCTAssertEqual(object.customStatus2Count, Properties.Required.customStatus2Count) + XCTAssertEqual(object.customStatus3Count, Properties.Required.customStatus3Count) + XCTAssertEqual(object.customStatus4Count, Properties.Required.customStatus4Count) + XCTAssertEqual(object.customStatus5Count, Properties.Required.customStatus5Count) + XCTAssertEqual(object.customStatus6Count, Properties.Required.customStatus6Count) + XCTAssertEqual(object.customStatus7Count, Properties.Required.customStatus7Count) + XCTAssertEqual(object.failedCount, Properties.Required.failedCount) + XCTAssertEqual(object.id, Properties.Required.id) + XCTAssertEqual(object.isCompleted, Properties.Required.isCompleted) + XCTAssertEqual(object.name, Properties.Required.name) + XCTAssertEqual(object.passedCount, Properties.Required.passedCount) + XCTAssertEqual(object.projectId, Properties.Required.projectId) + XCTAssertEqual(object.retestCount, Properties.Required.retestCount) + XCTAssertEqual(object.untestedCount, Properties.Required.untestedCount) + XCTAssertEqual(object.url, Properties.Required.url) + } + + func assertOptionalProperties(in object: Object, areNil: Bool) { + if areNil { + XCTAssertNil(object.assignedtoId) + XCTAssertNil(object.completedOn) + XCTAssertNil(object.description) + XCTAssertNil(object.entries) + XCTAssertNil(object.milestoneId) + } else { + XCTAssertNotNil(object.assignedtoId) + XCTAssertNotNil(object.completedOn) + XCTAssertNotNil(object.description) + XCTAssertNotNil(object.entries) + XCTAssertNotNil(object.milestoneId) + XCTAssertEqual(object.assignedtoId, Properties.Optional.assignedtoId) + XCTAssertEqual(object.completedOn, Properties.Optional.completedOn) + XCTAssertEqual(object.description, Properties.Optional.description) + XCTAssertEqual(object.entries!, Properties.Optional.entries) + XCTAssertEqual(object.milestoneId, Properties.Optional.milestoneId) + } + } + + func assertVariablePropertiesCanBeChanged(in object: inout Object) { + + object.description = "New Description" + object.milestoneId = 9999 + object.name = "New Name" + + XCTAssertNotEqual(object.description, Properties.Optional.description) + XCTAssertNotEqual(object.milestoneId, Properties.Optional.milestoneId) + XCTAssertNotEqual(object.name, Properties.Required.name) + + XCTAssertEqual(object.description, "New Description") + XCTAssertEqual(object.milestoneId, 9999) + XCTAssertEqual(object.name, "New Name") + } + +} + +extension PlanTests: AssertUpdateRequestJSON { } diff --git a/QuizTrainTests/Models/PriorityTests.swift b/QuizTrainTests/Models/PriorityTests.swift new file mode 100644 index 0000000..a04dee5 --- /dev/null +++ b/QuizTrainTests/Models/PriorityTests.swift @@ -0,0 +1,156 @@ +import XCTest +@testable import QuizTrain + +// MARK: - Tests + +class PriorityTests: XCTestCase, ModelTests { + + typealias Object = Priority + + func testEquatable() { + _testEquatable() + } + + func testInit() { + _testInit() + } + + func testInitWithOptionalProperties() { + _testInitWithOptionalProperties() + } + + func testJSONDeserializing() { + _testJSONDeserializing() + } + + func testJSONDeserializingWithOptionalProperties() { + _testJSONDeserializingWithOptionalProperties() + } + + func testJSONDeserializingASingleObject() { + _testJSONDeserializingASingleObject() + } + + func testJSONDeserializingMultipleObjects() { + _testJSONDeserializingMultipleObjects() + } + + func testJSONDeserializingASingleObjectMissingRequiredProperties() { + _testJSONDeserializingASingleObjectMissingRequiredProperties() + } + + func testJSONDeserializingMultipleObjectsMissingRequiredProperties() { + _testJSONDeserializingMultipleObjectsMissingRequiredProperties() + } + + func testJSONSerializingSingleObjects() { + _testJSONSerializingSingleObjects() + } + + func testJSONSerializingMultipleObjects() { + _testJSONSerializingMultipleObjects() + } + + func testJSONTwoWaySerializationForSingleItems() { + _testJSONTwoWaySerializationForSingleItems() + } + + func testJSONTwoWaySerializationForMultipleItems() { + _testJSONTwoWaySerializationForMultipleItems() + } + + func testVariableProperties() { + _testVariableProperties() + } + + func testUpdateRequestJSON() { + _testUpdateRequestJSON() + } + +} + +// MARK: - Data + +extension PriorityTests { + + struct Properties { + + struct Required { + static let id = 472 + static let isDefault = true + static let name = "Name" + static let priority = 3 + static let shortName = "Short Name" + } + + struct Optional { + // none + } + + } + +} + +extension PriorityTests: JSONDataProvider { + + static var requiredJSON: JSONDictionary { + return [Object.JSONKeys.id.rawValue: Properties.Required.id, + Object.JSONKeys.isDefault.rawValue: Properties.Required.isDefault, + Object.JSONKeys.name.rawValue: Properties.Required.name, + Object.JSONKeys.priority.rawValue: Properties.Required.priority, + Object.JSONKeys.shortName.rawValue: Properties.Required.shortName] + } + + static var optionalJSON: JSONDictionary { + return [:] // none + } + +} + +// MARK: - Objects + +extension PriorityTests: ObjectProvider { + + static var objectWithRequiredProperties: Object { + return Object(id: Properties.Required.id, + isDefault: Properties.Required.isDefault, + name: Properties.Required.name, + priority: Properties.Required.priority, + shortName: Properties.Required.shortName) + } + + static var objectWithRequiredAndOptionalProperties: Object { + return Object(id: Properties.Required.id, + isDefault: Properties.Required.isDefault, + name: Properties.Required.name, + priority: Properties.Required.priority, + shortName: Properties.Required.shortName) + } + +} + +// MARK: - Assertions + +extension PriorityTests: AssertEquatable { } + +extension PriorityTests: AssertJSONDeserializing { } + +extension PriorityTests: AssertJSONSerializing { } + +extension PriorityTests: AssertJSONTwoWaySerialization { } + +extension PriorityTests: AssertProperties { + + func assertRequiredProperties(in object: Object) { + XCTAssertEqual(object.id, Properties.Required.id) + XCTAssertEqual(object.isDefault, Properties.Required.isDefault) + XCTAssertEqual(object.name, Properties.Required.name) + XCTAssertEqual(object.priority, Properties.Required.priority) + XCTAssertEqual(object.shortName, Properties.Required.shortName) + } + + func assertOptionalProperties(in object: Object, areNil: Bool) { /* none */ } + + func assertVariablePropertiesCanBeChanged(in object: inout Object) { /* none */ } + +} diff --git a/QuizTrainTests/Models/ProjectTests.swift b/QuizTrainTests/Models/ProjectTests.swift new file mode 100644 index 0000000..6b97e2b --- /dev/null +++ b/QuizTrainTests/Models/ProjectTests.swift @@ -0,0 +1,198 @@ +import XCTest +@testable import QuizTrain + +// MARK: - Tests + +class ProjectTests: XCTestCase, ModelTests { + + typealias Object = Project + + func testEquatable() { + _testEquatable() + } + + func testInit() { + _testInit() + } + + func testInitWithOptionalProperties() { + _testInitWithOptionalProperties() + } + + func testJSONDeserializing() { + _testJSONDeserializing() + } + + func testJSONDeserializingWithOptionalProperties() { + _testJSONDeserializingWithOptionalProperties() + } + + func testJSONDeserializingASingleObject() { + _testJSONDeserializingASingleObject() + } + + func testJSONDeserializingMultipleObjects() { + _testJSONDeserializingMultipleObjects() + } + + func testJSONDeserializingASingleObjectMissingRequiredProperties() { + _testJSONDeserializingASingleObjectMissingRequiredProperties() + } + + func testJSONDeserializingMultipleObjectsMissingRequiredProperties() { + _testJSONDeserializingMultipleObjectsMissingRequiredProperties() + } + + func testJSONSerializingSingleObjects() { + _testJSONSerializingSingleObjects() + } + + func testJSONSerializingMultipleObjects() { + _testJSONSerializingMultipleObjects() + } + + func testJSONTwoWaySerializationForSingleItems() { + _testJSONTwoWaySerializationForSingleItems() + } + + func testJSONTwoWaySerializationForMultipleItems() { + _testJSONTwoWaySerializationForMultipleItems() + } + + func testVariableProperties() { + _testVariableProperties() + } + + func testUpdateRequestJSON() { + _testUpdateRequestJSON() + } + +} + +// MARK: - Data + +extension ProjectTests { + + struct Properties { + + struct Required { + static let id = 472 + static let isCompleted = true + static let name = "Name" + static let showAnnouncement = true + static let suiteMode = Project.SuiteMode.multipleSuites + static let url = URL(string: "https://www.testrail.com/")! + } + + struct Optional { + static let announcement = "Announcement" + static let completedOn = Date(secondsSince1970: 72988302) + } + + } + +} + +extension ProjectTests: JSONDataProvider { + + static var requiredJSON: JSONDictionary { + return [Object.JSONKeys.id.rawValue: Properties.Required.id, + Object.JSONKeys.isCompleted.rawValue: Properties.Required.isCompleted, + Object.JSONKeys.name.rawValue: Properties.Required.name, + Object.JSONKeys.showAnnouncement.rawValue: Properties.Required.showAnnouncement, + Object.JSONKeys.suiteMode.rawValue: Properties.Required.suiteMode.rawValue, + Object.JSONKeys.url.rawValue: Properties.Required.url.absoluteString] + } + + static var optionalJSON: JSONDictionary { + return [Object.JSONKeys.announcement.rawValue: Properties.Optional.announcement, + Object.JSONKeys.completedOn.rawValue: Properties.Optional.completedOn.secondsSince1970] + } + +} + +// MARK: - Objects + +extension ProjectTests: ObjectProvider { + + static var objectWithRequiredProperties: Object { + return Object(announcement: nil, + completedOn: nil, + id: Properties.Required.id, + isCompleted: Properties.Required.isCompleted, + name: Properties.Required.name, + showAnnouncement: Properties.Required.showAnnouncement, + suiteMode: Properties.Required.suiteMode, + url: Properties.Required.url) + } + + static var objectWithRequiredAndOptionalProperties: Object { + return Object(announcement: Properties.Optional.announcement, + completedOn: Properties.Optional.completedOn, + id: Properties.Required.id, + isCompleted: Properties.Required.isCompleted, + name: Properties.Required.name, + showAnnouncement: Properties.Required.showAnnouncement, + suiteMode: Properties.Required.suiteMode, + url: Properties.Required.url) + } + +} + +// MARK: - Assertions + +extension ProjectTests: AssertEquatable { } + +extension ProjectTests: AssertJSONDeserializing { } + +extension ProjectTests: AssertJSONSerializing { } + +extension ProjectTests: AssertJSONTwoWaySerialization { } + +extension ProjectTests: AssertProperties { + + func assertRequiredProperties(in object: Object) { + XCTAssertEqual(object.id, Properties.Required.id) + XCTAssertEqual(object.isCompleted, Properties.Required.isCompleted) + XCTAssertEqual(object.name, Properties.Required.name) + XCTAssertEqual(object.showAnnouncement, Properties.Required.showAnnouncement) + XCTAssertEqual(object.suiteMode, Properties.Required.suiteMode) + XCTAssertEqual(object.url, Properties.Required.url) + } + + func assertOptionalProperties(in object: Object, areNil: Bool) { + if areNil { + XCTAssertNil(object.announcement) + XCTAssertNil(object.completedOn) + } else { + XCTAssertNotNil(object.announcement) + XCTAssertNotNil(object.completedOn) + XCTAssertEqual(object.announcement, Properties.Optional.announcement) + XCTAssertEqual(object.completedOn, Properties.Optional.completedOn) + } + } + + func assertVariablePropertiesCanBeChanged(in object: inout Object) { + + object.announcement = "New Annoucement" + object.isCompleted = false + object.name = "New Name" + object.showAnnouncement = false + object.suiteMode = .singleSuitePlusBaselines + + XCTAssertNotEqual(object.announcement, Properties.Optional.announcement) + XCTAssertNotEqual(object.isCompleted, Properties.Required.isCompleted) + XCTAssertNotEqual(object.name, Properties.Required.name) + XCTAssertNotEqual(object.showAnnouncement, Properties.Required.showAnnouncement) + XCTAssertNotEqual(object.suiteMode, Properties.Required.suiteMode) + + XCTAssertEqual(object.announcement, "New Annoucement") + XCTAssertEqual(object.isCompleted, false) + XCTAssertEqual(object.name, "New Name") + XCTAssertEqual(object.showAnnouncement, false) + XCTAssertEqual(object.suiteMode, .singleSuitePlusBaselines) + } + +} + +extension ProjectTests: AssertUpdateRequestJSON { } diff --git a/QuizTrainTests/Models/ResultFieldTests.swift b/QuizTrainTests/Models/ResultFieldTests.swift new file mode 100644 index 0000000..5900b8b --- /dev/null +++ b/QuizTrainTests/Models/ResultFieldTests.swift @@ -0,0 +1,190 @@ +import XCTest +@testable import QuizTrain + +// MARK: - Tests + +class ResultFieldTests: XCTestCase, ModelTests { + + typealias Object = ResultField + + func testEquatable() { + _testEquatable() + } + + func testInit() { + _testInit() + } + + func testInitWithOptionalProperties() { + _testInitWithOptionalProperties() + } + + func testJSONDeserializing() { + _testJSONDeserializing() + } + + func testJSONDeserializingWithOptionalProperties() { + _testJSONDeserializingWithOptionalProperties() + } + + func testJSONDeserializingASingleObject() { + _testJSONDeserializingASingleObject() + } + + func testJSONDeserializingMultipleObjects() { + _testJSONDeserializingMultipleObjects() + } + + func testJSONDeserializingASingleObjectMissingRequiredProperties() { + _testJSONDeserializingASingleObjectMissingRequiredProperties() + } + + func testJSONDeserializingMultipleObjectsMissingRequiredProperties() { + _testJSONDeserializingMultipleObjectsMissingRequiredProperties() + } + + func testJSONSerializingSingleObjects() { + _testJSONSerializingSingleObjects() + } + + func testJSONSerializingMultipleObjects() { + _testJSONSerializingMultipleObjects() + } + + func testJSONTwoWaySerializationForSingleItems() { + _testJSONTwoWaySerializationForSingleItems() + } + + func testJSONTwoWaySerializationForMultipleItems() { + _testJSONTwoWaySerializationForMultipleItems() + } + + func testVariableProperties() { + _testVariableProperties() + } + + func testUpdateRequestJSON() { + _testUpdateRequestJSON() + } + +} + +// MARK: - Data + +extension ResultFieldTests { + + struct Properties { + + struct Required { + static let configs = [ConfigTests.objectWithRequiredAndOptionalPropertiesFromJSON!, ConfigTests.objectWithRequiredPropertiesFromJSON!, ConfigTests.objectWithRequiredAndOptionalPropertiesFromJSON!] // This must match the order and datasources in: JSON.required["configs"] + static let displayOrder = 10 + static let id = 11 + static let includeAll = true + static let isActive = true + static let label = "Label" + static let name = "Name" + static let systemName = "System Name" + static let templateIds = [12, 13, 14, 15, 16] + static let typeId = CustomFieldType.text + } + + struct Optional { + static let description = "Description" + } + + } + +} + +extension ResultFieldTests: JSONDataProvider { + + static var requiredJSON: JSONDictionary { + return [Object.JSONKeys.configs.rawValue: [ConfigTests.requiredAndOptionalJSON, ConfigTests.requiredJSON, ConfigTests.requiredAndOptionalJSON], // This must match the order and datasources in: Properties.Required.configs + Object.JSONKeys.displayOrder.rawValue: Properties.Required.displayOrder, + Object.JSONKeys.id.rawValue: Properties.Required.id, + Object.JSONKeys.includeAll.rawValue: Properties.Required.includeAll, + Object.JSONKeys.isActive.rawValue: Properties.Required.isActive, + Object.JSONKeys.label.rawValue: Properties.Required.label, + Object.JSONKeys.name.rawValue: Properties.Required.name, + Object.JSONKeys.systemName.rawValue: Properties.Required.systemName, + Object.JSONKeys.templateIds.rawValue: Properties.Required.templateIds, + Object.JSONKeys.typeId.rawValue: Properties.Required.typeId.rawValue] + } + + static var optionalJSON: JSONDictionary { + return [Object.JSONKeys.description.rawValue: Properties.Optional.description] + } + +} + +// MARK: - Objects + +extension ResultFieldTests: ObjectProvider { + + static var objectWithRequiredProperties: Object { + return Object(configs: Properties.Required.configs, + description: nil, + displayOrder: Properties.Required.displayOrder, + id: Properties.Required.id, + includeAll: Properties.Required.includeAll, + isActive: Properties.Required.isActive, + label: Properties.Required.label, + name: Properties.Required.name, + systemName: Properties.Required.systemName, + templateIds: Properties.Required.templateIds, + typeId: Properties.Required.typeId) + } + + static var objectWithRequiredAndOptionalProperties: Object { + return Object(configs: Properties.Required.configs, + description: Properties.Optional.description, + displayOrder: Properties.Required.displayOrder, + id: Properties.Required.id, + includeAll: Properties.Required.includeAll, + isActive: Properties.Required.isActive, + label: Properties.Required.label, + name: Properties.Required.name, + systemName: Properties.Required.systemName, + templateIds: Properties.Required.templateIds, + typeId: Properties.Required.typeId) + } + +} + +// MARK: - Assertions + +extension ResultFieldTests: AssertEquatable { } + +extension ResultFieldTests: AssertJSONDeserializing { } + +extension ResultFieldTests: AssertJSONSerializing { } + +extension ResultFieldTests: AssertJSONTwoWaySerialization { } + +extension ResultFieldTests: AssertProperties { + + func assertRequiredProperties(in object: Object) { + XCTAssertEqual(object.configs.count, Properties.Required.configs.count) + XCTAssertEqual(object.displayOrder, Properties.Required.displayOrder) + XCTAssertEqual(object.id, Properties.Required.id) + XCTAssertEqual(object.includeAll, Properties.Required.includeAll) + XCTAssertEqual(object.isActive, Properties.Required.isActive) + XCTAssertEqual(object.label, Properties.Required.label) + XCTAssertEqual(object.name, Properties.Required.name) + XCTAssertEqual(object.systemName, Properties.Required.systemName) + XCTAssertEqual(object.templateIds, Properties.Required.templateIds) + XCTAssertEqual(object.typeId, Properties.Required.typeId) + } + + func assertOptionalProperties(in object: Object, areNil: Bool) { + if areNil { + XCTAssertNil(object.description) + } else { + XCTAssertNotNil(object.description) + XCTAssertEqual(object.description, Properties.Optional.description) + } + } + + func assertVariablePropertiesCanBeChanged(in object: inout Object) { /* none */ } + +} diff --git a/QuizTrainTests/Models/ResultTests.swift b/QuizTrainTests/Models/ResultTests.swift new file mode 100644 index 0000000..24beb8a --- /dev/null +++ b/QuizTrainTests/Models/ResultTests.swift @@ -0,0 +1,201 @@ +import XCTest +@testable import QuizTrain + +// MARK: - Tests + +class ResultTests: XCTestCase, ModelTests { + + typealias Object = Result + + func testEquatable() { + _testEquatable() + } + + func testInit() { + _testInit() + } + + func testInitWithOptionalProperties() { + _testInitWithOptionalProperties() + } + + func testJSONDeserializing() { + _testJSONDeserializing() + } + + func testJSONDeserializingWithOptionalProperties() { + _testJSONDeserializingWithOptionalProperties() + } + + func testJSONDeserializingASingleObject() { + _testJSONDeserializingASingleObject() + } + + func testJSONDeserializingMultipleObjects() { + _testJSONDeserializingMultipleObjects() + } + + func testJSONDeserializingASingleObjectMissingRequiredProperties() { + _testJSONDeserializingASingleObjectMissingRequiredProperties() + } + + func testJSONDeserializingMultipleObjectsMissingRequiredProperties() { + _testJSONDeserializingMultipleObjectsMissingRequiredProperties() + } + + func testJSONSerializingSingleObjects() { + _testJSONSerializingSingleObjects() + } + + func testJSONSerializingMultipleObjects() { + _testJSONSerializingMultipleObjects() + } + + func testJSONTwoWaySerializationForSingleItems() { + _testJSONTwoWaySerializationForSingleItems() + } + + func testJSONTwoWaySerializationForMultipleItems() { + _testJSONTwoWaySerializationForMultipleItems() + } + + func testVariableProperties() { + _testVariableProperties() + } + + func testUpdateRequestJSON() { + _testUpdateRequestJSON() + } + +} + +// MARK: - Data + +extension ResultTests { + + struct Properties { + + struct Required { + static let createdBy = 67 + static let createdOn = Date(secondsSince1970: 72973833) + static let id = 972 + static let testId = 7 + } + + struct Optional { + static let assignedtoId = 47 + static let comment = "Comment" + static let defects = "Defects" + static let elapsed = "4hr, 31min" + static let statusId = 3 + static let version = "1.2.3" + } + + } + +} + +extension ResultTests: CustomFieldsDataProvider { } + +extension ResultTests: JSONDataProvider { + + static var requiredJSON: JSONDictionary { + return [Object.JSONKeys.createdBy.rawValue: Properties.Required.createdBy, + Object.JSONKeys.createdOn.rawValue: Properties.Required.createdOn.secondsSince1970, + Object.JSONKeys.id.rawValue: Properties.Required.id, + Object.JSONKeys.testId.rawValue: Properties.Required.testId] + } + + static var optionalJSON: JSONDictionary { + return [Object.JSONKeys.assignedtoId.rawValue: Properties.Optional.assignedtoId, + Object.JSONKeys.comment.rawValue: Properties.Optional.comment, + Object.JSONKeys.defects.rawValue: Properties.Optional.defects, + Object.JSONKeys.elapsed.rawValue: Properties.Optional.elapsed, + Object.JSONKeys.statusId.rawValue: Properties.Optional.statusId, + Object.JSONKeys.version.rawValue: Properties.Optional.version] + } + +} + +// MARK: - Objects + +extension ResultTests: ObjectProvider { + + static var objectWithRequiredProperties: Object { + return Object(assignedtoId: nil, + comment: nil, + createdBy: Properties.Required.createdBy, + createdOn: Properties.Required.createdOn, + defects: nil, + elapsed: nil, + id: Properties.Required.id, + statusId: nil, + testId: Properties.Required.testId, + version: nil, + customFieldsContainer: emptyCustomFieldsContainer) + } + + static var objectWithRequiredAndOptionalProperties: Object { + return Object(assignedtoId: Properties.Optional.assignedtoId, + comment: Properties.Optional.comment, + createdBy: Properties.Required.createdBy, + createdOn: Properties.Required.createdOn, + defects: Properties.Optional.defects, + elapsed: Properties.Optional.elapsed, + id: Properties.Required.id, + statusId: Properties.Optional.statusId, + testId: Properties.Required.testId, + version: Properties.Optional.version, + customFieldsContainer: customFieldsContainer) + } + +} + +// MARK: - Assertions + +extension ResultTests: AssertCustomFields { } + +extension ResultTests: AssertEquatable { } + +extension ResultTests: AssertJSONDeserializing { } + +extension ResultTests: AssertJSONSerializing { } + +extension ResultTests: AssertJSONTwoWaySerialization { } + +extension ResultTests: AssertProperties { + + func assertRequiredProperties(in object: Object) { + XCTAssertEqual(object.createdBy, Properties.Required.createdBy) + XCTAssertEqual(object.createdOn, Properties.Required.createdOn) + XCTAssertEqual(object.id, Properties.Required.id) + XCTAssertEqual(object.testId, Properties.Required.testId) + } + + func assertOptionalProperties(in object: Object, areNil: Bool) { + if areNil { + XCTAssertNil(object.assignedtoId) + XCTAssertNil(object.comment) + XCTAssertNil(object.defects) + XCTAssertNil(object.elapsed) + XCTAssertNil(object.statusId) + XCTAssertNil(object.version) + } else { + XCTAssertNotNil(object.assignedtoId) + XCTAssertNotNil(object.comment) + XCTAssertNotNil(object.defects) + XCTAssertNotNil(object.elapsed) + XCTAssertNotNil(object.statusId) + XCTAssertNotNil(object.version) + XCTAssertEqual(object.assignedtoId, Properties.Optional.assignedtoId) + XCTAssertEqual(object.comment, Properties.Optional.comment) + XCTAssertEqual(object.defects, Properties.Optional.defects) + XCTAssertEqual(object.elapsed, Properties.Optional.elapsed) + XCTAssertEqual(object.statusId, Properties.Optional.statusId) + XCTAssertEqual(object.version, Properties.Optional.version) + } + } + + func assertVariablePropertiesCanBeChanged(in object: inout Object) { /* none */ } + +} diff --git a/QuizTrainTests/Models/RunTests.swift b/QuizTrainTests/Models/RunTests.swift new file mode 100644 index 0000000..9bada4a --- /dev/null +++ b/QuizTrainTests/Models/RunTests.swift @@ -0,0 +1,307 @@ +import XCTest +@testable import QuizTrain + +// MARK: - Tests + +class RunTests: XCTestCase, ModelTests { + + typealias Object = Run + + func testEquatable() { + _testEquatable() + } + + func testInit() { + _testInit() + } + + func testInitWithOptionalProperties() { + _testInitWithOptionalProperties() + } + + func testJSONDeserializing() { + _testJSONDeserializing() + } + + func testJSONDeserializingWithOptionalProperties() { + _testJSONDeserializingWithOptionalProperties() + } + + func testJSONDeserializingASingleObject() { + _testJSONDeserializingASingleObject() + } + + func testJSONDeserializingMultipleObjects() { + _testJSONDeserializingMultipleObjects() + } + + func testJSONDeserializingASingleObjectMissingRequiredProperties() { + _testJSONDeserializingASingleObjectMissingRequiredProperties() + } + + func testJSONDeserializingMultipleObjectsMissingRequiredProperties() { + _testJSONDeserializingMultipleObjectsMissingRequiredProperties() + } + + func testJSONSerializingSingleObjects() { + _testJSONSerializingSingleObjects() + } + + func testJSONSerializingMultipleObjects() { + _testJSONSerializingMultipleObjects() + } + + func testJSONTwoWaySerializationForSingleItems() { + _testJSONTwoWaySerializationForSingleItems() + } + + func testJSONTwoWaySerializationForMultipleItems() { + _testJSONTwoWaySerializationForMultipleItems() + } + + func testVariableProperties() { + _testVariableProperties() + } + + func testUpdateRequestJSON() { + _testUpdateRequestJSON() + } + +} + +// MARK: - Data + +extension RunTests { + + struct Properties { + + struct Required { + static let blockedCount = 2 + static let createdBy = 4 + static let createdOn = Date(secondsSince1970: 72973833) + static let customStatus1Count = 3 + static let customStatus2Count = 45 + static let customStatus3Count = 78 + static let customStatus4Count = 73 + static let customStatus5Count = 820 + static let customStatus6Count = 1023 + static let customStatus7Count = 567 + static let failedCount = 80 + static let id = 20 + static let includeAll = true + static let isCompleted = true + static let name = "Name" + static let passedCount = 834 + static let projectId = 8 + static let retestCount = 73 + static let untestedCount = 53 + static let url = URL(string: "https://www.testrail.com/")! + } + + struct Optional { + static let assignedtoId = 355 + static let completedOn = Date(secondsSince1970: 72988302) + static let config = "Config" + static let configIds = [3, 4, 23, 328] + static let description = "Description" + static let milestoneId = 33 + static let planId = 2034 + static let suiteId = 36 + } + + } + +} + +extension RunTests: JSONDataProvider { + + static var requiredJSON: JSONDictionary { + return [Object.JSONKeys.blockedCount.rawValue: Properties.Required.blockedCount, + Object.JSONKeys.createdBy.rawValue: Properties.Required.createdBy, + Object.JSONKeys.createdOn.rawValue: Properties.Required.createdOn.secondsSince1970, + Object.JSONKeys.customStatus1Count.rawValue: Properties.Required.customStatus1Count, + Object.JSONKeys.customStatus2Count.rawValue: Properties.Required.customStatus2Count, + Object.JSONKeys.customStatus3Count.rawValue: Properties.Required.customStatus3Count, + Object.JSONKeys.customStatus4Count.rawValue: Properties.Required.customStatus4Count, + Object.JSONKeys.customStatus5Count.rawValue: Properties.Required.customStatus5Count, + Object.JSONKeys.customStatus6Count.rawValue: Properties.Required.customStatus6Count, + Object.JSONKeys.customStatus7Count.rawValue: Properties.Required.customStatus7Count, + Object.JSONKeys.failedCount.rawValue: Properties.Required.failedCount, + Object.JSONKeys.id.rawValue: Properties.Required.id, + Object.JSONKeys.includeAll.rawValue: Properties.Required.includeAll, + Object.JSONKeys.isCompleted.rawValue: Properties.Required.isCompleted, + Object.JSONKeys.name.rawValue: Properties.Required.name, + Object.JSONKeys.passedCount.rawValue: Properties.Required.passedCount, + Object.JSONKeys.projectId.rawValue: Properties.Required.projectId, + Object.JSONKeys.retestCount.rawValue: Properties.Required.retestCount, + Object.JSONKeys.untestedCount.rawValue: Properties.Required.untestedCount, + Object.JSONKeys.url.rawValue: Properties.Required.url.absoluteString] + } + + static var optionalJSON: JSONDictionary { + return [Object.JSONKeys.assignedtoId.rawValue: Properties.Optional.assignedtoId, + Object.JSONKeys.completedOn.rawValue: Properties.Optional.completedOn.secondsSince1970, + Object.JSONKeys.config.rawValue: Properties.Optional.config, + Object.JSONKeys.configIds.rawValue: Properties.Optional.configIds, + Object.JSONKeys.description.rawValue: Properties.Optional.description, + Object.JSONKeys.milestoneId.rawValue: Properties.Optional.milestoneId, + Object.JSONKeys.planId.rawValue: Properties.Optional.planId, + Object.JSONKeys.suiteId.rawValue: Properties.Optional.suiteId] + } + +} + +// MARK: - Objects + +extension RunTests: ObjectProvider { + + static var objectWithRequiredProperties: Object { + return Object(assignedtoId: nil, + blockedCount: Properties.Required.blockedCount, + completedOn: nil, + config: nil, + configIds: nil, + createdBy: Properties.Required.createdBy, + createdOn: Properties.Required.createdOn, + customStatus1Count: Properties.Required.customStatus1Count, + customStatus2Count: Properties.Required.customStatus2Count, + customStatus3Count: Properties.Required.customStatus3Count, + customStatus4Count: Properties.Required.customStatus4Count, + customStatus5Count: Properties.Required.customStatus5Count, + customStatus6Count: Properties.Required.customStatus6Count, + customStatus7Count: Properties.Required.customStatus7Count, + description: nil, + failedCount: Properties.Required.failedCount, + id: Properties.Required.id, + includeAll: Properties.Required.includeAll, + isCompleted: Properties.Required.isCompleted, + milestoneId: nil, + name: Properties.Required.name, + planId: nil, + passedCount: Properties.Required.passedCount, + projectId: Properties.Required.projectId, + retestCount: Properties.Required.retestCount, + suiteId: nil, + untestedCount: Properties.Required.untestedCount, + url: Properties.Required.url) + } + + static var objectWithRequiredAndOptionalProperties: Object { + return Object(assignedtoId: Properties.Optional.assignedtoId, + blockedCount: Properties.Required.blockedCount, + completedOn: Properties.Optional.completedOn, + config: Properties.Optional.config, + configIds: Properties.Optional.configIds, + createdBy: Properties.Required.createdBy, + createdOn: Properties.Required.createdOn, + customStatus1Count: Properties.Required.customStatus1Count, + customStatus2Count: Properties.Required.customStatus2Count, + customStatus3Count: Properties.Required.customStatus3Count, + customStatus4Count: Properties.Required.customStatus4Count, + customStatus5Count: Properties.Required.customStatus5Count, + customStatus6Count: Properties.Required.customStatus6Count, + customStatus7Count: Properties.Required.customStatus7Count, + description: Properties.Optional.description, + failedCount: Properties.Required.failedCount, + id: Properties.Required.id, + includeAll: Properties.Required.includeAll, + isCompleted: Properties.Required.isCompleted, + milestoneId: Properties.Optional.milestoneId, + name: Properties.Required.name, + planId: Properties.Optional.planId, + passedCount: Properties.Required.passedCount, + projectId: Properties.Required.projectId, + retestCount: Properties.Required.retestCount, + suiteId: Properties.Optional.suiteId, + untestedCount: Properties.Required.untestedCount, + url: Properties.Required.url) + } + +} + +// MARK: - Assertions + +extension RunTests: AssertEquatable { } + +extension RunTests: AssertJSONDeserializing { } + +extension RunTests: AssertJSONSerializing { } + +extension RunTests: AssertJSONTwoWaySerialization { } + +extension RunTests: AssertProperties { + + func assertRequiredProperties(in object: Object) { + XCTAssertEqual(object.blockedCount, Properties.Required.blockedCount) + XCTAssertEqual(object.createdBy, Properties.Required.createdBy) + XCTAssertEqual(object.createdOn, Properties.Required.createdOn) + XCTAssertEqual(object.customStatus1Count, Properties.Required.customStatus1Count) + XCTAssertEqual(object.customStatus2Count, Properties.Required.customStatus2Count) + XCTAssertEqual(object.customStatus3Count, Properties.Required.customStatus3Count) + XCTAssertEqual(object.customStatus4Count, Properties.Required.customStatus4Count) + XCTAssertEqual(object.customStatus5Count, Properties.Required.customStatus5Count) + XCTAssertEqual(object.customStatus6Count, Properties.Required.customStatus6Count) + XCTAssertEqual(object.customStatus7Count, Properties.Required.customStatus7Count) + XCTAssertEqual(object.failedCount, Properties.Required.failedCount) + XCTAssertEqual(object.id, Properties.Required.id) + XCTAssertEqual(object.includeAll, Properties.Required.includeAll) + XCTAssertEqual(object.isCompleted, Properties.Required.isCompleted) + XCTAssertEqual(object.name, Properties.Required.name) + XCTAssertEqual(object.passedCount, Properties.Required.passedCount) + XCTAssertEqual(object.projectId, Properties.Required.projectId) + XCTAssertEqual(object.retestCount, Properties.Required.retestCount) + XCTAssertEqual(object.untestedCount, Properties.Required.untestedCount) + XCTAssertEqual(object.url, Properties.Required.url) + } + + func assertOptionalProperties(in object: Object, areNil: Bool) { + if areNil { + XCTAssertNil(object.assignedtoId) + XCTAssertNil(object.completedOn) + XCTAssertNil(object.config) + XCTAssertNil(object.configIds) + XCTAssertNil(object.description) + XCTAssertNil(object.milestoneId) + XCTAssertNil(object.planId) + XCTAssertNil(object.suiteId) + } else { + XCTAssertNotNil(object.assignedtoId) + XCTAssertNotNil(object.completedOn) + XCTAssertNotNil(object.config) + XCTAssertNotNil(object.configIds) + XCTAssertNotNil(object.description) + XCTAssertNotNil(object.milestoneId) + XCTAssertNotNil(object.planId) + XCTAssertNotNil(object.suiteId) + XCTAssertEqual(object.assignedtoId, Properties.Optional.assignedtoId) + XCTAssertEqual(object.completedOn, Properties.Optional.completedOn) + XCTAssertEqual(object.config, Properties.Optional.config) + XCTAssertEqual(object.configIds!, Properties.Optional.configIds) + XCTAssertEqual(object.description, Properties.Optional.description) + XCTAssertEqual(object.milestoneId, Properties.Optional.milestoneId) + XCTAssertEqual(object.planId, Properties.Optional.planId) + XCTAssertEqual(object.suiteId, Properties.Optional.suiteId) + } + } + + func assertVariablePropertiesCanBeChanged(in object: inout Object) { + + object.description = "New Description" + object.includeAll = false + object.milestoneId = 99999 + object.name = "New Name" + + XCTAssertNotEqual(object.description, Properties.Optional.description) + XCTAssertNotEqual(object.includeAll, Properties.Required.includeAll) + XCTAssertNotEqual(object.milestoneId, Properties.Optional.milestoneId) + XCTAssertNotEqual(object.name, Properties.Required.name) + + XCTAssertEqual(object.description, "New Description") + XCTAssertEqual(object.includeAll, false) + XCTAssertEqual(object.milestoneId, 99999) + XCTAssertEqual(object.name, "New Name") + } + +} + +extension RunTests: AssertUpdateRequestJSON { } diff --git a/QuizTrainTests/Models/SectionTests.swift b/QuizTrainTests/Models/SectionTests.swift new file mode 100644 index 0000000..bf643c4 --- /dev/null +++ b/QuizTrainTests/Models/SectionTests.swift @@ -0,0 +1,186 @@ +import XCTest +@testable import QuizTrain + +// MARK: - Tests + +class SectionTests: XCTestCase, ModelTests { + + typealias Object = Section + + func testEquatable() { + _testEquatable() + } + + func testInit() { + _testInit() + } + + func testInitWithOptionalProperties() { + _testInitWithOptionalProperties() + } + + func testJSONDeserializing() { + _testJSONDeserializing() + } + + func testJSONDeserializingWithOptionalProperties() { + _testJSONDeserializingWithOptionalProperties() + } + + func testJSONDeserializingASingleObject() { + _testJSONDeserializingASingleObject() + } + + func testJSONDeserializingMultipleObjects() { + _testJSONDeserializingMultipleObjects() + } + + func testJSONDeserializingASingleObjectMissingRequiredProperties() { + _testJSONDeserializingASingleObjectMissingRequiredProperties() + } + + func testJSONDeserializingMultipleObjectsMissingRequiredProperties() { + _testJSONDeserializingMultipleObjectsMissingRequiredProperties() + } + + func testJSONSerializingSingleObjects() { + _testJSONSerializingSingleObjects() + } + + func testJSONSerializingMultipleObjects() { + _testJSONSerializingMultipleObjects() + } + + func testJSONTwoWaySerializationForSingleItems() { + _testJSONTwoWaySerializationForSingleItems() + } + + func testJSONTwoWaySerializationForMultipleItems() { + _testJSONTwoWaySerializationForMultipleItems() + } + + func testVariableProperties() { + _testVariableProperties() + } + + func testUpdateRequestJSON() { + _testUpdateRequestJSON() + } + +} + +// MARK: - Data + +extension SectionTests { + + struct Properties { + + struct Required { + static let depth = 2 + static let displayOrder = 1 + static let id = 2733 + static let name = "Name" + } + + struct Optional { + static let description = "Description" + static let parentId = 382 + static let suiteId = 33 + } + + } + +} + +extension SectionTests: JSONDataProvider { + + static var requiredJSON: JSONDictionary { + return [Object.JSONKeys.depth.rawValue: Properties.Required.depth, + Object.JSONKeys.displayOrder.rawValue: Properties.Required.displayOrder, + Object.JSONKeys.id.rawValue: Properties.Required.id, + Object.JSONKeys.name.rawValue: Properties.Required.name] + } + + static var optionalJSON: JSONDictionary { + return [Object.JSONKeys.description.rawValue: Properties.Optional.description, + Object.JSONKeys.parentId.rawValue: Properties.Optional.parentId, + Object.JSONKeys.suiteId.rawValue: Properties.Optional.suiteId] + } + +} + +// MARK: - Objects + +extension SectionTests: ObjectProvider { + + static var objectWithRequiredProperties: Object { + return Object(depth: Properties.Required.depth, + description: nil, + displayOrder: Properties.Required.displayOrder, + id: Properties.Required.id, + name: Properties.Required.name, + parentId: nil, + suiteId: nil) + } + + static var objectWithRequiredAndOptionalProperties: Object { + return Object(depth: Properties.Required.depth, + description: Properties.Optional.description, + displayOrder: Properties.Required.displayOrder, + id: Properties.Required.id, + name: Properties.Required.name, + parentId: Properties.Optional.parentId, + suiteId: Properties.Optional.suiteId) + } + +} + +// MARK: - Assertions + +extension SectionTests: AssertEquatable { } + +extension SectionTests: AssertJSONDeserializing { } + +extension SectionTests: AssertJSONSerializing { } + +extension SectionTests: AssertJSONTwoWaySerialization { } + +extension SectionTests: AssertProperties { + + func assertRequiredProperties(in object: Object) { + XCTAssertEqual(object.depth, Properties.Required.depth) + XCTAssertEqual(object.displayOrder, Properties.Required.displayOrder) + XCTAssertEqual(object.id, Properties.Required.id) + XCTAssertEqual(object.name, Properties.Required.name) + } + + func assertOptionalProperties(in object: Object, areNil: Bool) { + if areNil { + XCTAssertNil(object.description) + XCTAssertNil(object.parentId) + XCTAssertNil(object.suiteId) + } else { + XCTAssertNotNil(object.description) + XCTAssertNotNil(object.parentId) + XCTAssertNotNil(object.suiteId) + XCTAssertEqual(object.description, Properties.Optional.description) + XCTAssertEqual(object.parentId, Properties.Optional.parentId) + XCTAssertEqual(object.suiteId, Properties.Optional.suiteId) + } + } + + func assertVariablePropertiesCanBeChanged(in object: inout Object) { + + object.description = "New Description" + object.name = "New Name" + + XCTAssertNotEqual(object.description, Properties.Optional.description) + XCTAssertNotEqual(object.name, Properties.Required.name) + + XCTAssertEqual(object.description, "New Description") + XCTAssertEqual(object.name, "New Name") + } + +} + +extension SectionTests: AssertUpdateRequestJSON { } diff --git a/QuizTrainTests/Models/StatusTests.swift b/QuizTrainTests/Models/StatusTests.swift new file mode 100644 index 0000000..995233e --- /dev/null +++ b/QuizTrainTests/Models/StatusTests.swift @@ -0,0 +1,174 @@ +import XCTest +@testable import QuizTrain + +// MARK: - Tests + +class StatusTests: XCTestCase, ModelTests { + + typealias Object = Status + + func testEquatable() { + _testEquatable() + } + + func testInit() { + _testInit() + } + + func testInitWithOptionalProperties() { + _testInitWithOptionalProperties() + } + + func testJSONDeserializing() { + _testJSONDeserializing() + } + + func testJSONDeserializingWithOptionalProperties() { + _testJSONDeserializingWithOptionalProperties() + } + + func testJSONDeserializingASingleObject() { + _testJSONDeserializingASingleObject() + } + + func testJSONDeserializingMultipleObjects() { + _testJSONDeserializingMultipleObjects() + } + + func testJSONDeserializingASingleObjectMissingRequiredProperties() { + _testJSONDeserializingASingleObjectMissingRequiredProperties() + } + + func testJSONDeserializingMultipleObjectsMissingRequiredProperties() { + _testJSONDeserializingMultipleObjectsMissingRequiredProperties() + } + + func testJSONSerializingSingleObjects() { + _testJSONSerializingSingleObjects() + } + + func testJSONSerializingMultipleObjects() { + _testJSONSerializingMultipleObjects() + } + + func testJSONTwoWaySerializationForSingleItems() { + _testJSONTwoWaySerializationForSingleItems() + } + + func testJSONTwoWaySerializationForMultipleItems() { + _testJSONTwoWaySerializationForMultipleItems() + } + + func testVariableProperties() { + _testVariableProperties() + } + + func testUpdateRequestJSON() { + _testUpdateRequestJSON() + } + +} + +// MARK: - Data + +extension StatusTests { + + struct Properties { + + struct Required { + static let colorBright = 1000 + static let colorDark = 1001 + static let colorMedium = 1002 + static let id = 1 + static let isFinal = true + static let isSystem = true + static let isUntested = true + static let label = "Label" + static let name = "Name" + } + + struct Optional { /* none */ } + + } + +} + +extension StatusTests: JSONDataProvider { + + static var requiredJSON: JSONDictionary { + return [Object.JSONKeys.colorBright.rawValue: Properties.Required.colorBright, + Object.JSONKeys.colorDark.rawValue: Properties.Required.colorDark, + Object.JSONKeys.colorMedium.rawValue: Properties.Required.colorMedium, + Object.JSONKeys.id.rawValue: Properties.Required.id, + Object.JSONKeys.isFinal.rawValue: Properties.Required.isFinal, + Object.JSONKeys.isSystem.rawValue: Properties.Required.isSystem, + Object.JSONKeys.isUntested.rawValue: Properties.Required.isUntested, + Object.JSONKeys.label.rawValue: Properties.Required.label, + Object.JSONKeys.name.rawValue: Properties.Required.name] + } + + static var optionalJSON: JSONDictionary { + return [:] // none + } + +} + +// MARK: - Objects + +extension StatusTests: ObjectProvider { + + static var objectWithRequiredProperties: Object { + return Object(colorBright: Properties.Required.colorBright, + colorDark: Properties.Required.colorDark, + colorMedium: Properties.Required.colorMedium, + id: Properties.Required.id, + isFinal: Properties.Required.isFinal, + isSystem: Properties.Required.isSystem, + isUntested: Properties.Required.isUntested, + label: Properties.Required.label, + name: Properties.Required.name) + } + + static var objectWithRequiredAndOptionalProperties: Object { + return Object(colorBright: Properties.Required.colorBright, + colorDark: Properties.Required.colorDark, + colorMedium: Properties.Required.colorMedium, + id: Properties.Required.id, + isFinal: Properties.Required.isFinal, + isSystem: Properties.Required.isSystem, + isUntested: Properties.Required.isUntested, + label: Properties.Required.label, + name: Properties.Required.name) + } + +} + +// MARK: - Assertions + +extension StatusTests: AssertEquatable { } + +extension StatusTests: AssertJSONDeserializing { } + +extension StatusTests: AssertJSONSerializing { } + +extension StatusTests: AssertJSONTwoWaySerialization { } + +extension StatusTests: AssertProperties { + + func assertRequiredProperties(in object: Object) { + XCTAssertEqual(object.colorBright, Properties.Required.colorBright) + XCTAssertEqual(object.colorDark, Properties.Required.colorDark) + XCTAssertEqual(object.colorMedium, Properties.Required.colorMedium) + XCTAssertEqual(object.id, Properties.Required.id) + XCTAssertEqual(object.isFinal, Properties.Required.isFinal) + XCTAssertEqual(object.isSystem, Properties.Required.isSystem) + XCTAssertEqual(object.isUntested, Properties.Required.isUntested) + XCTAssertEqual(object.label, Properties.Required.label) + XCTAssertEqual(object.name, Properties.Required.name) + } + + func assertOptionalProperties(in object: Object, areNil: Bool) { /* none */ } + + func assertVariablePropertiesCanBeChanged(in object: inout Object) { /* none */ } + +} diff --git a/QuizTrainTests/Models/SuiteTests.swift b/QuizTrainTests/Models/SuiteTests.swift new file mode 100644 index 0000000..8af52fc --- /dev/null +++ b/QuizTrainTests/Models/SuiteTests.swift @@ -0,0 +1,194 @@ +import XCTest +@testable import QuizTrain + +// MARK: - Tests + +class SuiteTests: XCTestCase, ModelTests { + + typealias Object = Suite + + func testEquatable() { + _testEquatable() + } + + func testInit() { + _testInit() + } + + func testInitWithOptionalProperties() { + _testInitWithOptionalProperties() + } + + func testJSONDeserializing() { + _testJSONDeserializing() + } + + func testJSONDeserializingWithOptionalProperties() { + _testJSONDeserializingWithOptionalProperties() + } + + func testJSONDeserializingASingleObject() { + _testJSONDeserializingASingleObject() + } + + func testJSONDeserializingMultipleObjects() { + _testJSONDeserializingMultipleObjects() + } + + func testJSONDeserializingASingleObjectMissingRequiredProperties() { + _testJSONDeserializingASingleObjectMissingRequiredProperties() + } + + func testJSONDeserializingMultipleObjectsMissingRequiredProperties() { + _testJSONDeserializingMultipleObjectsMissingRequiredProperties() + } + + func testJSONSerializingSingleObjects() { + _testJSONSerializingSingleObjects() + } + + func testJSONSerializingMultipleObjects() { + _testJSONSerializingMultipleObjects() + } + + func testJSONTwoWaySerializationForSingleItems() { + _testJSONTwoWaySerializationForSingleItems() + } + + func testJSONTwoWaySerializationForMultipleItems() { + _testJSONTwoWaySerializationForMultipleItems() + } + + func testVariableProperties() { + _testVariableProperties() + } + + func testUpdateRequestJSON() { + _testUpdateRequestJSON() + } + +} + +// MARK: - Data + +extension SuiteTests { + + struct Properties { + + struct Required { + static let id = 27 + static let isBaseline = true + static let isCompleted = true + static let isMaster = true + static let name = "Name" + static let projectId = 3 + static let url = URL(string: "https://www.testrail.com/")! + } + + struct Optional { + static let completedOn = Date(secondsSince1970: 72973833) + static let description = "Description" + } + + } + +} + +extension SuiteTests: JSONDataProvider { + + static var requiredJSON: JSONDictionary { + return [Object.JSONKeys.id.rawValue: Properties.Required.id, + Object.JSONKeys.isBaseline.rawValue: Properties.Required.isBaseline, + Object.JSONKeys.isCompleted.rawValue: Properties.Required.isCompleted, + Object.JSONKeys.isMaster.rawValue: Properties.Required.isMaster, + Object.JSONKeys.name.rawValue: Properties.Required.name, + Object.JSONKeys.projectId.rawValue: Properties.Required.projectId, + Object.JSONKeys.url.rawValue: Properties.Required.url.absoluteString] + } + + static var optionalJSON: JSONDictionary { + return [Object.JSONKeys.completedOn.rawValue: Properties.Optional.completedOn.secondsSince1970, + Object.JSONKeys.description.rawValue: Properties.Optional.description] + } + +} + +// MARK: - Objects + +extension SuiteTests: ObjectProvider { + + static var objectWithRequiredProperties: Object { + return Object(completedOn: nil, + description: nil, + id: Properties.Required.id, + isBaseline: Properties.Required.isBaseline, + isCompleted: Properties.Required.isCompleted, + isMaster: Properties.Required.isMaster, + name: Properties.Required.name, + projectId: Properties.Required.projectId, + url: Properties.Required.url) + } + + static var objectWithRequiredAndOptionalProperties: Object { + return Object(completedOn: Properties.Optional.completedOn, + description: Properties.Optional.description, + id: Properties.Required.id, + isBaseline: Properties.Required.isBaseline, + isCompleted: Properties.Required.isCompleted, + isMaster: Properties.Required.isMaster, + name: Properties.Required.name, + projectId: Properties.Required.projectId, + url: Properties.Required.url) + } + +} + +// MARK: - Assertions + +extension SuiteTests: AssertEquatable { } + +extension SuiteTests: AssertJSONDeserializing { } + +extension SuiteTests: AssertJSONSerializing { } + +extension SuiteTests: AssertJSONTwoWaySerialization { } + +extension SuiteTests: AssertProperties { + + func assertRequiredProperties(in object: Object) { + XCTAssertEqual(object.id, Properties.Required.id) + XCTAssertEqual(object.isBaseline, Properties.Required.isBaseline) + XCTAssertEqual(object.isCompleted, Properties.Required.isCompleted) + XCTAssertEqual(object.isMaster, Properties.Required.isMaster) + XCTAssertEqual(object.name, Properties.Required.name) + XCTAssertEqual(object.projectId, Properties.Required.projectId) + XCTAssertEqual(object.url, Properties.Required.url) + } + + func assertOptionalProperties(in object: Object, areNil: Bool) { + if areNil { + XCTAssertNil(object.completedOn) + XCTAssertNil(object.description) + } else { + XCTAssertNotNil(object.completedOn) + XCTAssertNotNil(object.description) + XCTAssertEqual(object.completedOn, Properties.Optional.completedOn) + XCTAssertEqual(object.description, Properties.Optional.description) + } + } + + func assertVariablePropertiesCanBeChanged(in object: inout Object) { + + object.description = "New Description" + object.name = "New Name" + + XCTAssertNotEqual(object.description, Properties.Optional.description) + XCTAssertNotEqual(object.name, Properties.Required.name) + + XCTAssertEqual(object.description, "New Description") + XCTAssertEqual(object.name, "New Name") + } + +} + +extension SuiteTests: AssertUpdateRequestJSON { } diff --git a/QuizTrainTests/Models/TemplateTests.swift b/QuizTrainTests/Models/TemplateTests.swift new file mode 100644 index 0000000..426e895 --- /dev/null +++ b/QuizTrainTests/Models/TemplateTests.swift @@ -0,0 +1,146 @@ +import XCTest +@testable import QuizTrain + +// MARK: - Tests + +class TemplateTests: XCTestCase, ModelTests { + + typealias Object = Template + + func testEquatable() { + _testEquatable() + } + + func testInit() { + _testInit() + } + + func testInitWithOptionalProperties() { + _testInitWithOptionalProperties() + } + + func testJSONDeserializing() { + _testJSONDeserializing() + } + + func testJSONDeserializingWithOptionalProperties() { + _testJSONDeserializingWithOptionalProperties() + } + + func testJSONDeserializingASingleObject() { + _testJSONDeserializingASingleObject() + } + + func testJSONDeserializingMultipleObjects() { + _testJSONDeserializingMultipleObjects() + } + + func testJSONDeserializingASingleObjectMissingRequiredProperties() { + _testJSONDeserializingASingleObjectMissingRequiredProperties() + } + + func testJSONDeserializingMultipleObjectsMissingRequiredProperties() { + _testJSONDeserializingMultipleObjectsMissingRequiredProperties() + } + + func testJSONSerializingSingleObjects() { + _testJSONSerializingSingleObjects() + } + + func testJSONSerializingMultipleObjects() { + _testJSONSerializingMultipleObjects() + } + + func testJSONTwoWaySerializationForSingleItems() { + _testJSONTwoWaySerializationForSingleItems() + } + + func testJSONTwoWaySerializationForMultipleItems() { + _testJSONTwoWaySerializationForMultipleItems() + } + + func testVariableProperties() { + _testVariableProperties() + } + + func testUpdateRequestJSON() { + _testUpdateRequestJSON() + } + +} + +// MARK: - Data + +extension TemplateTests { + + struct Properties { + + struct Required { + static let isDefault = true + static let id = 4 + static let name = "Name" + } + + struct Optional { + // none + } + + } + +} + +extension TemplateTests: JSONDataProvider { + + static var requiredJSON: JSONDictionary { + return [Object.JSONKeys.isDefault.rawValue: Properties.Required.isDefault, + Object.JSONKeys.id.rawValue: Properties.Required.id, + Object.JSONKeys.name.rawValue: Properties.Required.name] + } + + static var optionalJSON: JSONDictionary { + return [:] // none + } + +} + +// MARK: - Objects + +extension TemplateTests: ObjectProvider { + + static var objectWithRequiredProperties: Object { + return Object(isDefault: Properties.Required.isDefault, + id: Properties.Required.id, + name: Properties.Required.name) + } + + static var objectWithRequiredAndOptionalProperties: Object { + return Object(isDefault: Properties.Required.isDefault, + id: Properties.Required.id, + name: Properties.Required.name) + } + +} + +// MARK: - Assertions + +extension TemplateTests: AssertEquatable { } + +extension TemplateTests: AssertJSONDeserializing { } + +extension TemplateTests: AssertJSONSerializing { } + +extension TemplateTests: AssertJSONTwoWaySerialization { } + +extension TemplateTests: AssertProperties { + + func assertRequiredProperties(in template: Object) { + XCTAssertEqual(template.isDefault, Properties.Required.isDefault) + XCTAssertEqual(template.id, Properties.Required.id) + XCTAssertEqual(template.name, Properties.Required.name) + } + + func assertOptionalProperties(in template: Object, areNil: Bool) { /* none */ } + + func assertVariablePropertiesCanBeChanged(in object: inout Object) { /* none */ } + +} diff --git a/QuizTrainTests/Models/TestTests.swift b/QuizTrainTests/Models/TestTests.swift new file mode 100644 index 0000000..f9f8d40 --- /dev/null +++ b/QuizTrainTests/Models/TestTests.swift @@ -0,0 +1,215 @@ +import XCTest +@testable import QuizTrain + +// MARK: - Tests + +class TestTests: XCTestCase, ModelTests { + + typealias Object = Test + + func testEquatable() { + _testEquatable() + } + + func testInit() { + _testInit() + } + + func testInitWithOptionalProperties() { + _testInitWithOptionalProperties() + } + + func testJSONDeserializing() { + _testJSONDeserializing() + } + + func testJSONDeserializingWithOptionalProperties() { + _testJSONDeserializingWithOptionalProperties() + } + + func testJSONDeserializingASingleObject() { + _testJSONDeserializingASingleObject() + } + + func testJSONDeserializingMultipleObjects() { + _testJSONDeserializingMultipleObjects() + } + + func testJSONDeserializingASingleObjectMissingRequiredProperties() { + _testJSONDeserializingASingleObjectMissingRequiredProperties() + } + + func testJSONDeserializingMultipleObjectsMissingRequiredProperties() { + _testJSONDeserializingMultipleObjectsMissingRequiredProperties() + } + + func testJSONSerializingSingleObjects() { + _testJSONSerializingSingleObjects() + } + + func testJSONSerializingMultipleObjects() { + _testJSONSerializingMultipleObjects() + } + + func testJSONTwoWaySerializationForSingleItems() { + _testJSONTwoWaySerializationForSingleItems() + } + + func testJSONTwoWaySerializationForMultipleItems() { + _testJSONTwoWaySerializationForMultipleItems() + } + + func testVariableProperties() { + _testVariableProperties() + } + + func testUpdateRequestJSON() { + _testUpdateRequestJSON() + } + +} + +// MARK: - Data + +extension TestTests { + + struct Properties { + + struct Required { + static let caseId = 3405 + static let id = 4 + static let priorityId = 23 + static let runId = 1 + static let statusId = 93 + static let templateId = 8834 + static let title = "Title" + static let typeId = 738 + } + + struct Optional { + static let assignedtoId = 345 + static let estimate = "1hr 2min" + static let estimateForecast = "2hr" + static let milestoneId = 513 + static let refs = "1,2,3" + } + + } + +} + +extension TestTests: CustomFieldsDataProvider { } + +extension TestTests: JSONDataProvider { + + static var requiredJSON: JSONDictionary { + return [Object.JSONKeys.caseId.rawValue: Properties.Required.caseId, + Object.JSONKeys.id.rawValue: Properties.Required.id, + Object.JSONKeys.priorityId.rawValue: Properties.Required.priorityId, + Object.JSONKeys.runId.rawValue: Properties.Required.runId, + Object.JSONKeys.statusId.rawValue: Properties.Required.statusId, + Object.JSONKeys.templateId.rawValue: Properties.Required.templateId, + Object.JSONKeys.title.rawValue: Properties.Required.title, + Object.JSONKeys.typeId.rawValue: Properties.Required.typeId] + } + + static var optionalJSON: JSONDictionary { + return [Object.JSONKeys.assignedtoId.rawValue: Properties.Optional.assignedtoId, + Object.JSONKeys.estimate.rawValue: Properties.Optional.estimate, + Object.JSONKeys.estimateForecast.rawValue: Properties.Optional.estimateForecast, + Object.JSONKeys.milestoneId.rawValue: Properties.Optional.milestoneId, + Object.JSONKeys.refs.rawValue: Properties.Optional.refs] + } + +} + +// MARK: - Objects + +extension TestTests: ObjectProvider { + + static var objectWithRequiredProperties: Object { + return Object(assignedtoId: nil, + caseId: Properties.Required.caseId, + estimate: nil, + estimateForecast: nil, + id: Properties.Required.id, + milestoneId: nil, + priorityId: Properties.Required.priorityId, + refs: nil, + runId: Properties.Required.runId, + statusId: Properties.Required.statusId, + templateId: Properties.Required.templateId, + title: Properties.Required.title, + typeId: Properties.Required.typeId, + customFieldsContainer: emptyCustomFieldsContainer) + } + + static var objectWithRequiredAndOptionalProperties: Object { + return Object(assignedtoId: Properties.Optional.assignedtoId, + caseId: Properties.Required.caseId, + estimate: Properties.Optional.estimate, + estimateForecast: Properties.Optional.estimateForecast, + id: Properties.Required.id, + milestoneId: Properties.Optional.milestoneId, + priorityId: Properties.Required.priorityId, + refs: Properties.Optional.refs, + runId: Properties.Required.runId, + statusId: Properties.Required.statusId, + templateId: Properties.Required.templateId, + title: Properties.Required.title, + typeId: Properties.Required.typeId, + customFieldsContainer: customFieldsContainer) + } + +} + +// MARK: - Assertions + +extension TestTests: AssertCustomFields { } + +extension TestTests: AssertEquatable { } + +extension TestTests: AssertJSONDeserializing { } + +extension TestTests: AssertJSONSerializing { } + +extension TestTests: AssertJSONTwoWaySerialization { } + +extension TestTests: AssertProperties { + + func assertRequiredProperties(in object: Object) { + XCTAssertEqual(object.caseId, Properties.Required.caseId) + XCTAssertEqual(object.id, Properties.Required.id) + XCTAssertEqual(object.priorityId, Properties.Required.priorityId) + XCTAssertEqual(object.runId, Properties.Required.runId) + XCTAssertEqual(object.statusId, Properties.Required.statusId) + XCTAssertEqual(object.templateId, Properties.Required.templateId) + XCTAssertEqual(object.title, Properties.Required.title) + XCTAssertEqual(object.typeId, Properties.Required.typeId) + } + + func assertOptionalProperties(in object: Test, areNil: Bool) { + if areNil { + XCTAssertNil(object.assignedtoId) + XCTAssertNil(object.estimate) + XCTAssertNil(object.estimateForecast) + XCTAssertNil(object.milestoneId) + XCTAssertNil(object.refs) + } else { + XCTAssertNotNil(object.assignedtoId) + XCTAssertNotNil(object.estimate) + XCTAssertNotNil(object.estimateForecast) + XCTAssertNotNil(object.milestoneId) + XCTAssertNotNil(object.refs) + XCTAssertNotNil(object.typeId) + XCTAssertEqual(object.assignedtoId, Properties.Optional.assignedtoId) + XCTAssertEqual(object.estimate, Properties.Optional.estimate) + XCTAssertEqual(object.estimateForecast, Properties.Optional.estimateForecast) + XCTAssertEqual(object.milestoneId, Properties.Optional.milestoneId) + XCTAssertEqual(object.refs, Properties.Optional.refs) + } + } + + func assertVariablePropertiesCanBeChanged(in object: inout Object) { /* none */ } + +} diff --git a/QuizTrainTests/Models/Testing Protocols/ModelTests.swift b/QuizTrainTests/Models/Testing Protocols/ModelTests.swift new file mode 100644 index 0000000..2f7a41e --- /dev/null +++ b/QuizTrainTests/Models/Testing Protocols/ModelTests.swift @@ -0,0 +1 @@ +protocol ModelTests: EquatableTests, InitTests, JSONDeserializingTests, JSONSerializingTests, JSONTwoWaySerializationTests, UpdateRequestJSONTests, VariablePropertyTests { } diff --git a/QuizTrainTests/Models/Types/CustomFieldTypeTests.swift b/QuizTrainTests/Models/Types/CustomFieldTypeTests.swift new file mode 100644 index 0000000..3f3043b --- /dev/null +++ b/QuizTrainTests/Models/Types/CustomFieldTypeTests.swift @@ -0,0 +1,48 @@ +import XCTest +@testable import QuizTrain + +class CustomFieldTypeTests: XCTestCase { + + func testRawValueInit() { + for i in -1000...0 { + XCTAssertNil(CustomFieldType(rawValue: i)) + } + for i in 1...12 { + XCTAssertNotNil(CustomFieldType(rawValue: i)) + } + for i in 13...1000 { + XCTAssertNil(CustomFieldType(rawValue: i)) + } + } + + func testCaseRawValues() { + XCTAssertEqual(CustomFieldType.string.rawValue, 1) + XCTAssertEqual(CustomFieldType.integer.rawValue, 2) + XCTAssertEqual(CustomFieldType.text.rawValue, 3) + XCTAssertEqual(CustomFieldType.url.rawValue, 4) + XCTAssertEqual(CustomFieldType.checkbox.rawValue, 5) + XCTAssertEqual(CustomFieldType.dropdown.rawValue, 6) + XCTAssertEqual(CustomFieldType.user.rawValue, 7) + XCTAssertEqual(CustomFieldType.date.rawValue, 8) + XCTAssertEqual(CustomFieldType.milestone.rawValue, 9) + XCTAssertEqual(CustomFieldType.steps.rawValue, 10) + XCTAssertEqual(CustomFieldType.stepResults.rawValue, 11) + XCTAssertEqual(CustomFieldType.multiSelect.rawValue, 12) + } + + func testDescription() { + XCTAssertEqual(CustomFieldType.string.description(), "String") + XCTAssertEqual(CustomFieldType.integer.description(), "Integer") + XCTAssertEqual(CustomFieldType.text.description(), "Text") + XCTAssertEqual(CustomFieldType.url.description(), "URL") + XCTAssertEqual(CustomFieldType.checkbox.description(), "Checkbox") + XCTAssertEqual(CustomFieldType.dropdown.description(), "Dropdown") + XCTAssertEqual(CustomFieldType.user.description(), "User") + XCTAssertEqual(CustomFieldType.date.description(), "Date") + XCTAssertEqual(CustomFieldType.milestone.description(), "Milestone") + XCTAssertEqual(CustomFieldType.steps.description(), "Steps") + XCTAssertEqual(CustomFieldType.stepResults.description(), "Step Results") + XCTAssertEqual(CustomFieldType.multiSelect.description(), "Multi-Select") + } + +} diff --git a/QuizTrainTests/Models/Types/Project.SuiteModeTests.swift b/QuizTrainTests/Models/Types/Project.SuiteModeTests.swift new file mode 100644 index 0000000..0b5f2ca --- /dev/null +++ b/QuizTrainTests/Models/Types/Project.SuiteModeTests.swift @@ -0,0 +1,30 @@ +import XCTest +@testable import QuizTrain + +class Project_SuiteModeTests: XCTestCase { + + func testRawValueInit() { + for i in -1000...0 { + XCTAssertNil(Project.SuiteMode(rawValue: i)) + } + for i in 1...3 { + XCTAssertNotNil(Project.SuiteMode(rawValue: i)) + } + for i in 4...1000 { + XCTAssertNil(Project.SuiteMode(rawValue: i)) + } + } + + func testCaseRawValues() { + XCTAssertEqual(Project.SuiteMode.singleSuite.rawValue, 1) + XCTAssertEqual(Project.SuiteMode.singleSuitePlusBaselines.rawValue, 2) + XCTAssertEqual(Project.SuiteMode.multipleSuites.rawValue, 3) + } + + func testDescription() { + XCTAssertEqual(Project.SuiteMode.singleSuite.description(), "Single Suite") + XCTAssertEqual(Project.SuiteMode.singleSuitePlusBaselines.description(), "Single Suite Plus Baselines") + XCTAssertEqual(Project.SuiteMode.multipleSuites.description(), "Multiple Suites") + } + +} diff --git a/QuizTrainTests/Models/Types/UniqueSelectionTests.swift b/QuizTrainTests/Models/Types/UniqueSelectionTests.swift new file mode 100644 index 0000000..ecacac7 --- /dev/null +++ b/QuizTrainTests/Models/Types/UniqueSelectionTests.swift @@ -0,0 +1,34 @@ +import XCTest +@testable import QuizTrain + +class UniqueSelectionTests: XCTestCase { + + func testEquatable() { + + let a: UniqueSelection = UniqueSelection.all + let b: UniqueSelection = UniqueSelection.none + let c = UniqueSelection.some([1, 2, 3]) + let d = UniqueSelection.some([1, 2, 3, 4]) + + XCTAssertEqual(a, UniqueSelection.all) + XCTAssertEqual(b, UniqueSelection.none) + XCTAssertEqual(c, UniqueSelection.some([1, 2, 3])) + + XCTAssertNotEqual(a, b) + XCTAssertNotEqual(a, c) + XCTAssertNotEqual(a, d) + + XCTAssertNotEqual(b, a) + XCTAssertNotEqual(b, c) + XCTAssertNotEqual(b, d) + + XCTAssertNotEqual(c, a) + XCTAssertNotEqual(c, b) + XCTAssertNotEqual(c, d) + + XCTAssertNotEqual(d, a) + XCTAssertNotEqual(d, b) + XCTAssertNotEqual(d, c) + } + +} diff --git a/QuizTrainTests/Models/UserTests.swift b/QuizTrainTests/Models/UserTests.swift new file mode 100644 index 0000000..0725b89 --- /dev/null +++ b/QuizTrainTests/Models/UserTests.swift @@ -0,0 +1,149 @@ +import XCTest +@testable import QuizTrain + +// MARK: - Tests + +class UserTests: XCTestCase, ModelTests { + + typealias Object = User + + func testEquatable() { + _testEquatable() + } + + func testInit() { + _testInit() + } + + func testInitWithOptionalProperties() { + _testInitWithOptionalProperties() + } + + func testJSONDeserializing() { + _testJSONDeserializing() + } + + func testJSONDeserializingWithOptionalProperties() { + _testJSONDeserializingWithOptionalProperties() + } + + func testJSONDeserializingASingleObject() { + _testJSONDeserializingASingleObject() + } + + func testJSONDeserializingMultipleObjects() { + _testJSONDeserializingMultipleObjects() + } + + func testJSONDeserializingASingleObjectMissingRequiredProperties() { + _testJSONDeserializingASingleObjectMissingRequiredProperties() + } + + func testJSONDeserializingMultipleObjectsMissingRequiredProperties() { + _testJSONDeserializingMultipleObjectsMissingRequiredProperties() + } + + func testJSONSerializingSingleObjects() { + _testJSONSerializingSingleObjects() + } + + func testJSONSerializingMultipleObjects() { + _testJSONSerializingMultipleObjects() + } + + func testJSONTwoWaySerializationForSingleItems() { + _testJSONTwoWaySerializationForSingleItems() + } + + func testJSONTwoWaySerializationForMultipleItems() { + _testJSONTwoWaySerializationForMultipleItems() + } + + func testVariableProperties() { + _testVariableProperties() + } + + func testUpdateRequestJSON() { + _testUpdateRequestJSON() + } + +} + +// MARK: - Data + +extension UserTests { + + struct Properties { + + struct Required { + static let email = "hello@email.com" + static let id = 108 + static let isActive = true + static let name = "Name" + } + + struct Optional { /* none */ } + + } + +} + +extension UserTests: JSONDataProvider { + + static var requiredJSON: JSONDictionary { + return [Object.JSONKeys.email.rawValue: Properties.Required.email, + Object.JSONKeys.id.rawValue: Properties.Required.id, + Object.JSONKeys.isActive.rawValue: Properties.Required.isActive, + Object.JSONKeys.name.rawValue: Properties.Required.name] + } + + static var optionalJSON: JSONDictionary { + return [:] // none + } + +} + +// MARK: - Objects + +extension UserTests: ObjectProvider { + + static var objectWithRequiredProperties: Object { + return Object(email: Properties.Required.email, + id: Properties.Required.id, + isActive: Properties.Required.isActive, + name: Properties.Required.name) + } + + static var objectWithRequiredAndOptionalProperties: Object { + return Object(email: Properties.Required.email, + id: Properties.Required.id, + isActive: Properties.Required.isActive, + name: Properties.Required.name) + } + +} + +// MARK: - Assertions + +extension UserTests: AssertEquatable { } + +extension UserTests: AssertJSONDeserializing { } + +extension UserTests: AssertJSONSerializing { } + +extension UserTests: AssertJSONTwoWaySerialization { } + +extension UserTests: AssertProperties { + + func assertRequiredProperties(in object: Object) { + XCTAssertEqual(object.email, Properties.Required.email) + XCTAssertEqual(object.id, Properties.Required.id) + XCTAssertEqual(object.isActive, Properties.Required.isActive) + XCTAssertEqual(object.name, Properties.Required.name) + } + + func assertOptionalProperties(in object: Object, areNil: Bool) { /* none */ } + + func assertVariablePropertiesCanBeChanged(in object: inout Object) { /* none */ } + +} diff --git a/QuizTrainTests/Network/Filters/FilterTests.swift b/QuizTrainTests/Network/Filters/FilterTests.swift new file mode 100644 index 0000000..8dfd67f --- /dev/null +++ b/QuizTrainTests/Network/Filters/FilterTests.swift @@ -0,0 +1,168 @@ +import XCTest +@testable import QuizTrain + +// MARK: - Tests + +class FilterTests: XCTestCase { + + func testInitWithBool() { + let filter = Filter(named: "Bool", matching: true) + XCTAssertEqual(filter.name, "Bool") + XCTAssertEqual(filter.value, .bool(true)) + } + + func testInitWithDate() { + let date = Date() + let filter = Filter(named: "Date", matching: date) + XCTAssertEqual(filter.name, "Date") + XCTAssertEqual(filter.value, .timestamp(date)) + } + + func testInitWithInt() { + let filter = Filter(named: "Int", matching: 100) + XCTAssertEqual(filter.name, "Int") + XCTAssertEqual(filter.value, .int(100)) + } + + func testInitWithIntList() { + let filter = Filter(named: "IntList", matching: [1, 2, 3]) + XCTAssertEqual(filter.name, "IntList") + XCTAssertEqual(filter.value, .intList([1, 2, 3])) + } + + func testEquatable() { + + let objectA = Filter(named: "Hello", matching: true) + let objectB = Filter(named: "Hello", matching: 1) + let objectC = Filter(named: "Hello", matching: [100, 200, 300]) + let objectD = Filter(named: "Hello", matching: Date()) + + XCTAssertNotEqual(objectA, objectB) + XCTAssertNotEqual(objectA, objectC) + XCTAssertNotEqual(objectA, objectD) + + XCTAssertNotEqual(objectB, objectA) + XCTAssertNotEqual(objectB, objectC) + XCTAssertNotEqual(objectB, objectD) + + XCTAssertNotEqual(objectC, objectA) + XCTAssertNotEqual(objectC, objectB) + XCTAssertNotEqual(objectC, objectD) + + XCTAssertNotEqual(objectD, objectA) + XCTAssertNotEqual(objectD, objectB) + XCTAssertNotEqual(objectD, objectC) + } + + func testEquatableBool() { + + let objectA = Filter(named: "Hello", matching: true) + var objectB = Filter(named: "Hello", matching: true) + let objectC = Filter(named: "Hello", matching: false) + + XCTAssertEqual(objectA, objectA) + XCTAssertEqual(objectA, objectB) + XCTAssertNotEqual(objectA, objectC) + + objectB.value = .bool(false) + + XCTAssertNotEqual(objectA, objectB) + XCTAssertEqual(objectB, objectC) + + objectB.name = "Goodbye" + + XCTAssertNotEqual(objectA, objectC) + } + + func testEquatableInt() { + + let objectA = Filter(named: "Hello", matching: 1) + var objectB = Filter(named: "Hello", matching: 1) + let objectC = Filter(named: "Hello", matching: 2) + + XCTAssertEqual(objectA, objectA) + XCTAssertEqual(objectA, objectB) + XCTAssertNotEqual(objectA, objectC) + + objectB.value = .int(2) + + XCTAssertNotEqual(objectA, objectB) + XCTAssertEqual(objectB, objectC) + + objectB.name = "Goodbye" + + XCTAssertNotEqual(objectA, objectC) + } + + func testEquatableIntList() { + + let objectA = Filter(named: "Hello", matching: [100, 200, 300]) + var objectB = Filter(named: "Hello", matching: [100, 200, 300]) + let objectC = Filter(named: "Hello", matching: [100, 200]) + + XCTAssertEqual(objectA, objectA) + XCTAssertEqual(objectA, objectB) + XCTAssertNotEqual(objectA, objectC) + + objectB.value = .intList([100, 200]) + + XCTAssertNotEqual(objectA, objectB) + XCTAssertEqual(objectB, objectC) + + objectB.name = "Goodbye" + + XCTAssertNotEqual(objectA, objectC) + } + + func testEquatableTimestamp() { + + let dateA = Date() + let dateB = Date(timeInterval: 100, since: dateA) + + let objectA = Filter(named: "Hello", matching: dateA) + var objectB = Filter(named: "Hello", matching: dateA) + let objectC = Filter(named: "Hello", matching: dateB) + + XCTAssertEqual(objectA, objectA) + XCTAssertEqual(objectA, objectB) + XCTAssertNotEqual(objectA, objectC) + + objectB.value = .timestamp(dateB) + + XCTAssertNotEqual(objectA, objectB) + XCTAssertEqual(objectB, objectC) + + objectB.name = "Goodbye" + + XCTAssertNotEqual(objectA, objectC) + } + + func testQueryItemProvider() { + + // Bool + + var filter = Filter(named: "Bool", matching: true) + var queryItem = URLQueryItem(name: "Bool", value: "1") + XCTAssertEqual(filter.queryItem, queryItem) + + // Int + + filter = Filter(named: "Int", matching: 1) + queryItem = URLQueryItem(name: "Int", value: "1") + XCTAssertEqual(filter.queryItem, queryItem) + + // Int List + + filter = Filter(named: "IntList", matching: [1, 2, 3]) + queryItem = URLQueryItem(name: "IntList", value: "1,2,3") + XCTAssertEqual(filter.queryItem, queryItem) + + // Timestamp + + let date = Date() + filter = Filter(named: "Timestamp", matching: date) + queryItem = URLQueryItem(name: "Timestamp", value: String(date.secondsSince1970)) + XCTAssertEqual(filter.queryItem, queryItem) + } + +} diff --git a/QuizTrainTests/Network/Models/Add/NewCaseResults.ResultTests.swift b/QuizTrainTests/Network/Models/Add/NewCaseResults.ResultTests.swift new file mode 100644 index 0000000..9dd7811 --- /dev/null +++ b/QuizTrainTests/Network/Models/Add/NewCaseResults.ResultTests.swift @@ -0,0 +1,220 @@ +import XCTest +@testable import QuizTrain + +// MARK: - Tests + +class NewCaseResults_ResultTests: XCTestCase, AddModelTests, ValidatableTests { + + typealias Object = NewCaseResults.Result + + func testAddRequestJSON() { + _testAddRequestJSON() + } + + func testEquatable() { + _testEquatable() + } + + func testInit() { + _testInit() + } + + func testInitWithOptionalProperties() { + _testInitWithOptionalProperties() + } + + func testJSONSerializingSingleObjects() { + _testJSONSerializingSingleObjects() + } + + func testJSONSerializingMultipleObjects() { + _testJSONSerializingMultipleObjects() + } + + func testVariableProperties() { + _testVariableProperties() + } + + func testIsValid() { + _testIsValid() + } + + func testIsInvalid() { + _testIsInvalid() + } + +} + +// MARK: - Data + +extension NewCaseResults_ResultTests { + + struct Properties { + + struct Required { + static let caseId = 10 + } + + struct Optional { + static let assignedtoId = 11 + static let comment = "Comment" + static let defects = "Defects" + static let elapsed = "4hr, 31min" + static let statusId = 12 + static let version = "1.2.3" + static let customFields = NewCaseResults_ResultTests.customFields + } + + } + +} + +extension NewCaseResults_ResultTests: CustomFieldsDataProvider { } + +// MARK: - Objects + +extension NewCaseResults_ResultTests: ObjectProvider { + + static var objectWithRequiredProperties: Object { + return Object(assignedtoId: nil, + caseId: Properties.Required.caseId, + comment: nil, + defects: nil, + elapsed: nil, + statusId: nil, + version: nil, + customFields: nil) + } + + static var objectWithRequiredAndOptionalProperties: Object { + return Object(assignedtoId: Properties.Optional.assignedtoId, + caseId: Properties.Required.caseId, + comment: Properties.Optional.comment, + defects: Properties.Optional.defects, + elapsed: Properties.Optional.elapsed, + statusId: Properties.Optional.statusId, + version: Properties.Optional.version, + customFields: Properties.Optional.customFields) + } + +} + +extension NewCaseResults_ResultTests: ValidatableObjectProvider { + + var validObject: Validatable { + return objectWithRequiredAndOptionalProperties + } + + var invalidObject: Validatable { + var object = objectWithRequiredAndOptionalProperties + object.assignedtoId = nil + object.comment = nil + object.statusId = nil + return object + } + +} + +// MARK: - Assertions + +extension NewCaseResults_ResultTests: AssertAddRequestJSON { } + +extension NewCaseResults_ResultTests: AssertCustomFields { } + +extension NewCaseResults_ResultTests: AssertEquatable { } + +extension NewCaseResults_ResultTests: AssertJSONSerializing { } + +extension NewCaseResults_ResultTests: AssertProperties { + + func assertRequiredProperties(in object: Object) { + XCTAssertEqual(object.caseId, Properties.Required.caseId) + } + + func assertOptionalProperties(in object: Object, areNil: Bool) { + if areNil { + XCTAssertNil(object.assignedtoId) + XCTAssertNil(object.comment) + XCTAssertNil(object.defects) + XCTAssertNil(object.elapsed) + XCTAssertNil(object.statusId) + XCTAssertNil(object.version) + XCTAssertTrue(object.customFields.isEmpty) + } else { + XCTAssertNotNil(object.assignedtoId) + XCTAssertNotNil(object.comment) + XCTAssertNotNil(object.defects) + XCTAssertNotNil(object.elapsed) + XCTAssertNotNil(object.statusId) + XCTAssertNotNil(object.version) + XCTAssertNotNil(object.customFields) + XCTAssertEqual(object.assignedtoId, Properties.Optional.assignedtoId) + XCTAssertEqual(object.comment, Properties.Optional.comment) + XCTAssertEqual(object.defects, Properties.Optional.defects) + XCTAssertEqual(object.elapsed, Properties.Optional.elapsed) + XCTAssertEqual(object.statusId, Properties.Optional.statusId) + XCTAssertEqual(object.version, Properties.Optional.version) + XCTAssertEqual(object.customFields.count, Properties.Optional.customFields.count) + } + } + + func assertVariablePropertiesCanBeChanged(in object: inout Object) { + + // Properties + + object.assignedtoId = 1000 + object.caseId = 1001 + object.comment = "New Comment" + object.defects = "New Defects" + object.elapsed = "99hr, 99min" + object.version = "4.5.6" + object.statusId = 1002 + object.customFields = NewCaseResults_ResultTests.emptyCustomFields + + XCTAssertNotEqual(object.assignedtoId, Properties.Optional.assignedtoId) + XCTAssertNotEqual(object.caseId, Properties.Required.caseId) + XCTAssertNotEqual(object.comment, Properties.Optional.comment) + XCTAssertNotEqual(object.defects, Properties.Optional.defects) + XCTAssertNotEqual(object.elapsed, Properties.Optional.elapsed) + XCTAssertNotEqual(object.version, Properties.Optional.version) + XCTAssertNotEqual(object.statusId, Properties.Optional.statusId) + XCTAssertNotEqual(object.customFields.count, Properties.Optional.customFields.count) + + XCTAssertEqual(object.assignedtoId, 1000) + XCTAssertEqual(object.caseId, 1001) + XCTAssertEqual(object.comment, "New Comment") + XCTAssertEqual(object.defects, "New Defects") + XCTAssertEqual(object.elapsed, "99hr, 99min") + XCTAssertEqual(object.version, "4.5.6") + XCTAssertEqual(object.statusId, 1002) + XCTAssertEqual(object.customFields.count, NewCaseResults_ResultTests.emptyCustomFields.count) + + // Custom Fields + + let customFieldsCount = object.customFields.count + + object.customFields["custom_field_test01"] = "Custom Field Test 01" + object.customFields["custom_field_test02"] = 9000 + object.customFields["custom_field_test03"] = -8.0 + object.customFields["invalid_custom_field_test04"] = "This should not be added." + + XCTAssertNotNil(object.customFields["custom_field_test01"]) + XCTAssertNotNil(object.customFields["custom_field_test02"]) + XCTAssertNotNil(object.customFields["custom_field_test03"]) + XCTAssertNil(object.customFields["invalid_custom_field_test04"]) + + XCTAssertEqual(object.customFields["custom_field_test01"] as! String, "Custom Field Test 01") + XCTAssertEqual(object.customFields["custom_field_test02"] as! Int, 9000) + XCTAssertEqual(object.customFields["custom_field_test03"] as! Double, -8.0) + + XCTAssertEqual(object.customFields.count, customFieldsCount + 3) + + object.customFields.removeValue(forKey: "custom_field_test01") + + XCTAssertNil(object.customFields["custom_field_test01"]) + XCTAssertEqual(object.customFields.count, customFieldsCount + 2) + } + +} + +extension NewCaseResults_ResultTests: AssertValidatable { } diff --git a/QuizTrainTests/Network/Models/Add/NewCaseResultsTests.swift b/QuizTrainTests/Network/Models/Add/NewCaseResultsTests.swift new file mode 100644 index 0000000..51c413d --- /dev/null +++ b/QuizTrainTests/Network/Models/Add/NewCaseResultsTests.swift @@ -0,0 +1,117 @@ +import XCTest +@testable import QuizTrain + +// MARK: - Tests + +class NewCaseResultsTests: XCTestCase, AddModelTests, ValidatableTests { + + typealias Object = NewCaseResults + + func testAddRequestJSON() { + _testAddRequestJSON() + } + + func testEquatable() { + _testEquatable() + } + + func testInit() { + _testInit() + } + + func testInitWithOptionalProperties() { + _testInitWithOptionalProperties() + } + + func testJSONSerializingSingleObjects() { + _testJSONSerializingSingleObjects() + } + + func testJSONSerializingMultipleObjects() { + _testJSONSerializingMultipleObjects() + } + + func testVariableProperties() { + _testVariableProperties() + } + + func testIsValid() { + _testIsValid() + } + + func testIsInvalid() { + _testIsInvalid() + } + +} + +// MARK: - Data + +extension NewCaseResultsTests { + + struct Properties { + + struct Required { + static let results = [NewCaseResults_ResultTests.objectWithRequiredAndOptionalProperties, NewCaseResults_ResultTests.objectWithRequiredAndOptionalProperties, NewCaseResults_ResultTests.objectWithRequiredAndOptionalProperties] + } + + struct Optional { /* none */ } + + } + +} + +extension NewCaseResultsTests: CustomFieldsDataProvider { } + +// MARK: - Objects + +extension NewCaseResultsTests: ObjectProvider { + + static var objectWithRequiredProperties: Object { + return Object(results: Properties.Required.results) + } + + static var objectWithRequiredAndOptionalProperties: Object { + return Object(results: Properties.Required.results) + } + +} + +extension NewCaseResultsTests: ValidatableObjectProvider { + + var validObject: Validatable { + return objectWithRequiredAndOptionalProperties + } + + var invalidObject: Validatable { + return Object(results: [NewCaseResults_ResultTests.objectWithRequiredProperties, NewCaseResults_ResultTests.objectWithRequiredProperties, NewCaseResults_ResultTests.objectWithRequiredProperties]) + } + +} + +// MARK: - Assertions + +extension NewCaseResultsTests: AssertAddRequestJSON { } + +extension NewCaseResultsTests: AssertEquatable { } + +extension NewCaseResultsTests: AssertJSONSerializing { } + +extension NewCaseResultsTests: AssertProperties { + + func assertRequiredProperties(in object: Object) { + XCTAssertEqual(object.results, Properties.Required.results) + } + + func assertOptionalProperties(in object: Object, areNil: Bool) { /* none */ } + + func assertVariablePropertiesCanBeChanged(in object: inout Object) { + object.results.append(NewCaseResults_ResultTests.objectWithRequiredProperties) + XCTAssertNotEqual(object.results, Properties.Required.results) + object.results.remove(at: object.results.count - 1) + XCTAssertEqual(object.results, Properties.Required.results) + } + +} + +extension NewCaseResultsTests: AssertValidatable { } diff --git a/QuizTrainTests/Network/Models/Add/NewCaseTests.swift b/QuizTrainTests/Network/Models/Add/NewCaseTests.swift new file mode 100644 index 0000000..a62630b --- /dev/null +++ b/QuizTrainTests/Network/Models/Add/NewCaseTests.swift @@ -0,0 +1,193 @@ +import XCTest +@testable import QuizTrain + +// MARK: - Tests + +class NewCaseTests: XCTestCase, AddModelTests { + + typealias Object = NewCase + + func testAddRequestJSON() { + _testAddRequestJSON() + } + + func testEquatable() { + _testEquatable() + } + + func testInit() { + _testInit() + } + + func testInitWithOptionalProperties() { + _testInitWithOptionalProperties() + } + + func testJSONSerializingSingleObjects() { + _testJSONSerializingSingleObjects() + } + + func testJSONSerializingMultipleObjects() { + _testJSONSerializingMultipleObjects() + } + + func testVariableProperties() { + _testVariableProperties() + } + +} + +// MARK: - Data + +extension NewCaseTests { + + struct Properties { + + struct Required { + static let title = "Title" + } + + struct Optional { + static let estimate = "2hr, 3min" + static let milestoneId = 10 + static let priorityId = 11 + static let refs = "1,2,3" + static let templateId = 12 + static let typeId = 13 + static let customFields = NewCaseTests.customFields + } + + } + +} + +extension NewCaseTests: CustomFieldsDataProvider { } + +// MARK: - Objects + +extension NewCaseTests: ObjectProvider { + + static var objectWithRequiredProperties: Object { + return Object(estimate: nil, + milestoneId: nil, + priorityId: nil, + refs: nil, + templateId: nil, + title: Properties.Required.title, + typeId: nil, + customFields: nil) + } + + static var objectWithRequiredAndOptionalProperties: Object { + return Object(estimate: Properties.Optional.estimate, + milestoneId: Properties.Optional.milestoneId, + priorityId: Properties.Optional.priorityId, + refs: Properties.Optional.refs, + templateId: Properties.Optional.templateId, + title: Properties.Required.title, + typeId: Properties.Optional.typeId, + customFields: Properties.Optional.customFields) + } + +} + +// MARK: - Assertions + +extension NewCaseTests: AssertAddRequestJSON { } + +extension NewCaseTests: AssertCustomFields { } + +extension NewCaseTests: AssertEquatable { } + +extension NewCaseTests: AssertJSONSerializing { } + +extension NewCaseTests: AssertProperties { + + func assertRequiredProperties(in object: Object) { + XCTAssertEqual(object.title, Properties.Required.title) + } + + func assertOptionalProperties(in object: Object, areNil: Bool) { + if areNil { + XCTAssertNil(object.estimate) + XCTAssertNil(object.milestoneId) + XCTAssertNil(object.priorityId) + XCTAssertNil(object.refs) + XCTAssertNil(object.templateId) + XCTAssertNil(object.typeId) + XCTAssertTrue(object.customFields.isEmpty) + } else { + XCTAssertNotNil(object.estimate) + XCTAssertNotNil(object.milestoneId) + XCTAssertNotNil(object.priorityId) + XCTAssertNotNil(object.refs) + XCTAssertNotNil(object.templateId) + XCTAssertNotNil(object.typeId) + XCTAssertNotNil(object.customFields) + XCTAssertEqual(object.estimate, Properties.Optional.estimate) + XCTAssertEqual(object.milestoneId, Properties.Optional.milestoneId) + XCTAssertEqual(object.priorityId, Properties.Optional.priorityId) + XCTAssertEqual(object.refs, Properties.Optional.refs) + XCTAssertEqual(object.templateId, Properties.Optional.templateId) + XCTAssertEqual(object.typeId, Properties.Optional.typeId) + XCTAssertEqual(object.customFields.count, Properties.Optional.customFields.count) + } + } + + func assertVariablePropertiesCanBeChanged(in object: inout Object) { + + // Properties + + object.estimate = "New Estimate" + object.milestoneId = 1000 + object.priorityId = 1001 + object.refs = "4,5,6" + object.title = "New Title" + object.templateId = 1002 + object.typeId = 1003 + object.customFields = NewCaseTests.emptyCustomFields + + XCTAssertNotEqual(object.estimate, Properties.Optional.estimate) + XCTAssertNotEqual(object.milestoneId, Properties.Optional.milestoneId) + XCTAssertNotEqual(object.priorityId, Properties.Optional.priorityId) + XCTAssertNotEqual(object.refs, Properties.Optional.refs) + XCTAssertNotEqual(object.title, Properties.Required.title) + XCTAssertNotEqual(object.templateId, Properties.Optional.templateId) + XCTAssertNotEqual(object.typeId, Properties.Optional.typeId) + XCTAssertNotEqual(object.customFields.count, Properties.Optional.customFields.count) + + XCTAssertEqual(object.estimate, "New Estimate") + XCTAssertEqual(object.milestoneId, 1000) + XCTAssertEqual(object.priorityId, 1001) + XCTAssertEqual(object.refs, "4,5,6") + XCTAssertEqual(object.title, "New Title") + XCTAssertEqual(object.templateId, 1002) + XCTAssertEqual(object.typeId, 1003) + + // Custom Fields + + let customFieldsCount = object.customFields.count + + object.customFields["custom_field_test01"] = "Custom Field Test 01" + object.customFields["custom_field_test02"] = 9000 + object.customFields["custom_field_test03"] = -8.0 + object.customFields["invalid_custom_field_test04"] = "This should not be added." + + XCTAssertNotNil(object.customFields["custom_field_test01"]) + XCTAssertNotNil(object.customFields["custom_field_test02"]) + XCTAssertNotNil(object.customFields["custom_field_test03"]) + XCTAssertNil(object.customFields["invalid_custom_field_test04"]) + + XCTAssertEqual(object.customFields["custom_field_test01"] as! String, "Custom Field Test 01") + XCTAssertEqual(object.customFields["custom_field_test02"] as! Int, 9000) + XCTAssertEqual(object.customFields["custom_field_test03"] as! Double, -8.0) + + XCTAssertEqual(object.customFields.count, customFieldsCount + 3) + + object.customFields.removeValue(forKey: "custom_field_test01") + + XCTAssertNil(object.customFields["custom_field_test01"]) + XCTAssertEqual(object.customFields.count, customFieldsCount + 2) + } + +} diff --git a/QuizTrainTests/Network/Models/Add/NewConfigurationGroupTests.swift b/QuizTrainTests/Network/Models/Add/NewConfigurationGroupTests.swift new file mode 100644 index 0000000..daab0e6 --- /dev/null +++ b/QuizTrainTests/Network/Models/Add/NewConfigurationGroupTests.swift @@ -0,0 +1,88 @@ +import XCTest +@testable import QuizTrain + +// MARK: - Tests + +class NewConfigurationGroupTests: XCTestCase, AddModelTests { + + typealias Object = NewConfigurationGroup + + func testAddRequestJSON() { + _testAddRequestJSON() + } + + func testEquatable() { + _testEquatable() + } + + func testInit() { + _testInit() + } + + func testInitWithOptionalProperties() { + _testInitWithOptionalProperties() + } + + func testJSONSerializingSingleObjects() { + _testJSONSerializingSingleObjects() + } + + func testJSONSerializingMultipleObjects() { + _testJSONSerializingMultipleObjects() + } + + func testVariableProperties() { + _testVariableProperties() + } + +} + +// MARK: - Data + +extension NewConfigurationGroupTests { + + struct Properties { + + struct Required { + static let name = "Name" + } + + struct Optional { /* none */ } + + } + +} + +// MARK: - Objects + +extension NewConfigurationGroupTests: ObjectProvider { + + static var objectWithRequiredProperties: Object { + return Object(name: Properties.Required.name) + } + + static var objectWithRequiredAndOptionalProperties: Object { + return Object(name: Properties.Required.name) + } + +} + +// MARK: - Assertions + +extension NewConfigurationGroupTests: AssertAddRequestJSON { } + +extension NewConfigurationGroupTests: AssertEquatable { } + +extension NewConfigurationGroupTests: AssertJSONSerializing { } + +extension NewConfigurationGroupTests: AssertProperties { + + func assertRequiredProperties(in object: Object) { + XCTAssertEqual(object.name, Properties.Required.name) + } + + func assertOptionalProperties(in object: Object, areNil: Bool) { /* none */ } + + func assertVariablePropertiesCanBeChanged(in object: inout Object) { /* none */ } + +} diff --git a/QuizTrainTests/Network/Models/Add/NewConfigurationTests.swift b/QuizTrainTests/Network/Models/Add/NewConfigurationTests.swift new file mode 100644 index 0000000..ae696d2 --- /dev/null +++ b/QuizTrainTests/Network/Models/Add/NewConfigurationTests.swift @@ -0,0 +1,88 @@ +import XCTest +@testable import QuizTrain + +// MARK: - Tests + +class NewConfigurationTests: XCTestCase, AddModelTests { + + typealias Object = NewConfiguration + + func testAddRequestJSON() { + _testAddRequestJSON() + } + + func testEquatable() { + _testEquatable() + } + + func testInit() { + _testInit() + } + + func testInitWithOptionalProperties() { + _testInitWithOptionalProperties() + } + + func testJSONSerializingSingleObjects() { + _testJSONSerializingSingleObjects() + } + + func testJSONSerializingMultipleObjects() { + _testJSONSerializingMultipleObjects() + } + + func testVariableProperties() { + _testVariableProperties() + } + +} + +// MARK: - Data + +extension NewConfigurationTests { + + struct Properties { + + struct Required { + static let name = "Name" + } + + struct Optional { /* none */ } + + } + +} + +// MARK: - Objects + +extension NewConfigurationTests: ObjectProvider { + + static var objectWithRequiredProperties: Object { + return Object(name: Properties.Required.name) + } + + static var objectWithRequiredAndOptionalProperties: Object { + return Object(name: Properties.Required.name) + } + +} + +// MARK: - Assertions + +extension NewConfigurationTests: AssertAddRequestJSON { } + +extension NewConfigurationTests: AssertEquatable { } + +extension NewConfigurationTests: AssertJSONSerializing { } + +extension NewConfigurationTests: AssertProperties { + + func assertRequiredProperties(in object: Object) { + XCTAssertEqual(object.name, Properties.Required.name) + } + + func assertOptionalProperties(in object: Object, areNil: Bool) { /* none */ } + + func assertVariablePropertiesCanBeChanged(in object: inout Object) { /* none */ } + +} diff --git a/QuizTrainTests/Network/Models/Add/NewMilestoneTests.swift b/QuizTrainTests/Network/Models/Add/NewMilestoneTests.swift new file mode 100644 index 0000000..c2f46b5 --- /dev/null +++ b/QuizTrainTests/Network/Models/Add/NewMilestoneTests.swift @@ -0,0 +1,136 @@ +import XCTest +@testable import QuizTrain + +// MARK: - Tests + +class NewMilestoneTests: XCTestCase, AddModelTests { + + typealias Object = NewMilestone + + func testAddRequestJSON() { + _testAddRequestJSON() + } + + func testEquatable() { + _testEquatable() + } + + func testInit() { + _testInit() + } + + func testInitWithOptionalProperties() { + _testInitWithOptionalProperties() + } + + func testJSONSerializingSingleObjects() { + _testJSONSerializingSingleObjects() + } + + func testJSONSerializingMultipleObjects() { + _testJSONSerializingMultipleObjects() + } + + func testVariableProperties() { + _testVariableProperties() + } + +} + +// MARK: - Data + +extension NewMilestoneTests { + + struct Properties { + + struct Required { + static let name = "Name" + } + + struct Optional { + static let description = "Description" + static let dueOn = Date(secondsSince1970: 2000000) + static let parentId = 10 + static let startOn = Date(secondsSince1970: 1000000) + } + + } + +} + +// MARK: - Objects + +extension NewMilestoneTests: ObjectProvider { + + static var objectWithRequiredProperties: Object { + return Object(description: nil, + dueOn: nil, + name: Properties.Required.name, + parentId: nil, + startOn: nil) + } + + static var objectWithRequiredAndOptionalProperties: Object { + return Object(description: Properties.Optional.description, + dueOn: Properties.Optional.dueOn, + name: Properties.Required.name, + parentId: Properties.Optional.parentId, + startOn: Properties.Optional.startOn) + } + +} + +// MARK: - Assertions + +extension NewMilestoneTests: AssertAddRequestJSON { } + +extension NewMilestoneTests: AssertEquatable { } + +extension NewMilestoneTests: AssertJSONSerializing { } + +extension NewMilestoneTests: AssertProperties { + + func assertRequiredProperties(in object: Object) { + XCTAssertEqual(object.name, Properties.Required.name) + } + + func assertOptionalProperties(in object: Object, areNil: Bool) { + if areNil { + XCTAssertNil(object.description) + XCTAssertNil(object.dueOn) + XCTAssertNil(object.parentId) + XCTAssertNil(object.startOn) + } else { + XCTAssertNotNil(object.description) + XCTAssertNotNil(object.dueOn) + XCTAssertNotNil(object.parentId) + XCTAssertNotNil(object.startOn) + XCTAssertEqual(object.description, Properties.Optional.description) + XCTAssertEqual(object.dueOn, Properties.Optional.dueOn) + XCTAssertEqual(object.parentId, Properties.Optional.parentId) + XCTAssertEqual(object.startOn, Properties.Optional.startOn) + } + } + + func assertVariablePropertiesCanBeChanged(in object: inout Object) { + + object.description = "New Description" + object.dueOn = Date(secondsSince1970: 2999999) + object.name = "New Name" + object.parentId = 1000 + object.startOn = Date(secondsSince1970: 3999999) + + XCTAssertNotEqual(object.description, Properties.Optional.description) + XCTAssertNotEqual(object.dueOn, Properties.Optional.dueOn) + XCTAssertNotEqual(object.name, Properties.Required.name) + XCTAssertNotEqual(object.parentId, Properties.Optional.parentId) + XCTAssertNotEqual(object.startOn, Properties.Optional.startOn) + + XCTAssertEqual(object.description, "New Description") + XCTAssertEqual(object.dueOn, Date(secondsSince1970: 2999999)) + XCTAssertEqual(object.name, "New Name") + XCTAssertEqual(object.parentId, 1000) + XCTAssertEqual(object.startOn, Date(secondsSince1970: 3999999)) + } + +} diff --git a/QuizTrainTests/Network/Models/Add/NewPlan.Entry.RunTests.swift b/QuizTrainTests/Network/Models/Add/NewPlan.Entry.RunTests.swift new file mode 100644 index 0000000..de3b681 --- /dev/null +++ b/QuizTrainTests/Network/Models/Add/NewPlan.Entry.RunTests.swift @@ -0,0 +1,161 @@ +import XCTest +@testable import QuizTrain + +// MARK: - Tests + +class NewPlan_Entry_RunTests: XCTestCase, AddModelTests { + + typealias Object = NewPlan.Entry.Run + + func testAddRequestJSON() { + _testAddRequestJSON() + } + + func testEquatable() { + _testEquatable() + } + + func testInit() { + _testInit() + } + + func testInitWithOptionalProperties() { + _testInitWithOptionalProperties() + } + + func testJSONSerializingSingleObjects() { + _testJSONSerializingSingleObjects() + } + + func testJSONSerializingMultipleObjects() { + _testJSONSerializingMultipleObjects() + } + + func testVariableProperties() { + _testVariableProperties() + } + +} + +// MARK: - Data + +extension NewPlan_Entry_RunTests { + + struct Properties { + + struct Required { /* none */ } + + struct Optional { + static let assignedtoId: Int = 1 + static let caseIds: [Int] = [2, 3, 4] + static let configIds: [Int] = [5, 6] + static let description: String = "Description" + static let includeAll: Bool = false + static let milestoneId: Int = 7 + static let name: String = "Name" + static let suiteId: Int = 8 + } + + } + +} + +// MARK: - Objects + +extension NewPlan_Entry_RunTests: ObjectProvider { + + static var objectWithRequiredProperties: Object { + return Object(assignedtoId: nil, + caseIds: nil, + configIds: nil, + description: nil, + includeAll: nil, + milestoneId: nil, + name: nil, + suiteId: nil) + } + + static var objectWithRequiredAndOptionalProperties: Object { + return Object(assignedtoId: Properties.Optional.assignedtoId, + caseIds: Properties.Optional.caseIds, + configIds: Properties.Optional.configIds, + description: Properties.Optional.description, + includeAll: Properties.Optional.includeAll, + milestoneId: Properties.Optional.milestoneId, + name: Properties.Optional.name, + suiteId: Properties.Optional.suiteId) + } + +} + +// MARK: - Assertions + +extension NewPlan_Entry_RunTests: AssertAddRequestJSON { } + +extension NewPlan_Entry_RunTests: AssertEquatable { } + +extension NewPlan_Entry_RunTests: AssertJSONSerializing { } + +extension NewPlan_Entry_RunTests: AssertProperties { + + func assertRequiredProperties(in object: Object) { /* none */ } + + func assertOptionalProperties(in object: Object, areNil: Bool) { + if areNil { + XCTAssertNil(object.assignedtoId) + XCTAssertNil(object.caseIds) + XCTAssertNil(object.configIds) + XCTAssertNil(object.description) + XCTAssertNil(object.includeAll) + XCTAssertNil(object.milestoneId) + XCTAssertNil(object.name) + XCTAssertNil(object.suiteId) + } else { + XCTAssertNotNil(object.assignedtoId) + XCTAssertNotNil(object.caseIds) + XCTAssertNotNil(object.configIds) + XCTAssertNotNil(object.description) + XCTAssertNotNil(object.includeAll) + XCTAssertNotNil(object.milestoneId) + XCTAssertNotNil(object.name) + XCTAssertNotNil(object.suiteId) + XCTAssertEqual(object.assignedtoId, Properties.Optional.assignedtoId) + if let caseIds = object.caseIds { XCTAssertEqual(caseIds, Properties.Optional.caseIds) } + if let configIds = object.configIds { XCTAssertEqual(configIds, Properties.Optional.configIds) } + XCTAssertEqual(object.description, Properties.Optional.description) + XCTAssertEqual(object.includeAll, Properties.Optional.includeAll) + XCTAssertEqual(object.milestoneId, Properties.Optional.milestoneId) + XCTAssertEqual(object.name, Properties.Optional.name) + XCTAssertEqual(object.suiteId, Properties.Optional.suiteId) + } + } + + func assertVariablePropertiesCanBeChanged(in object: inout Object) { + + object.assignedtoId = 1000 + object.caseIds = [1001, 1002, 1003] + object.configIds = [1004, 1005] + object.description = "New Description" + object.includeAll = true + object.milestoneId = 1006 + object.name = "New Name" + object.suiteId = 1007 + + XCTAssertNotEqual(object.assignedtoId, Properties.Optional.assignedtoId) + if let caseIds = object.caseIds { XCTAssertNotEqual(caseIds, Properties.Optional.caseIds) } + if let configIds = object.configIds { XCTAssertNotEqual(configIds, Properties.Optional.configIds) } + XCTAssertNotEqual(object.description, Properties.Optional.description) + XCTAssertNotEqual(object.includeAll, Properties.Optional.includeAll) + XCTAssertNotEqual(object.name, Properties.Optional.name) + XCTAssertNotEqual(object.suiteId, Properties.Optional.suiteId) + + XCTAssertEqual(object.assignedtoId, 1000) + if let caseIds = object.caseIds { XCTAssertEqual(caseIds, [1001, 1002, 1003]) } + if let configIds = object.configIds { XCTAssertEqual(configIds, [1004, 1005]) } + XCTAssertEqual(object.description, "New Description") + XCTAssertEqual(object.includeAll, true) + XCTAssertEqual(object.name, "New Name") + XCTAssertEqual(object.suiteId, 1007) + } + +} diff --git a/QuizTrainTests/Network/Models/Add/NewPlan.EntryTests.swift b/QuizTrainTests/Network/Models/Add/NewPlan.EntryTests.swift new file mode 100644 index 0000000..da16924 --- /dev/null +++ b/QuizTrainTests/Network/Models/Add/NewPlan.EntryTests.swift @@ -0,0 +1,202 @@ +import XCTest +@testable import QuizTrain + +// MARK: - Tests + +class NewPlan_EntryTests: XCTestCase, AddModelTests { + + typealias Object = NewPlan.Entry + + func testAddRequestJSON() { + _testAddRequestJSON() + } + + func testEquatable() { + _testEquatable() + } + + func testInit() { + _testInit() + } + + func testInitWithOptionalProperties() { + _testInitWithOptionalProperties() + } + + func testJSONSerializingSingleObjects() { + _testJSONSerializingSingleObjects() + } + + func testJSONSerializingMultipleObjects() { + _testJSONSerializingMultipleObjects() + } + + func testVariableProperties() { + _testVariableProperties() + } + +} + +// MARK: - Data + +extension NewPlan_EntryTests { + + struct Properties { + + struct Required { + static let suiteId = 10 + } + + struct Optional { + static let assignedtoId = 11 + static let caseIds = [12, 13, 14] + static let description = "Description" + static let includeAll = true + static let name = "Name" + static let runs = [NewPlan_Entry_RunTests.objectWithRequiredAndOptionalProperties, NewPlan_Entry_RunTests.objectWithRequiredAndOptionalProperties, NewPlan_Entry_RunTests.objectWithRequiredAndOptionalProperties] + } + + } + +} + +// MARK: - Objects + +extension NewPlan_EntryTests: ObjectProvider { + + static var objectWithRequiredProperties: Object { + return Object(assignedtoId: nil, + caseIds: nil, + description: nil, + includeAll: nil, + name: nil, + runs: nil, + suiteId: Properties.Required.suiteId) + } + + static var objectWithRequiredAndOptionalProperties: Object { + return Object(assignedtoId: Properties.Optional.assignedtoId, + caseIds: Properties.Optional.caseIds, + description: Properties.Optional.description, + includeAll: Properties.Optional.includeAll, + name: Properties.Optional.name, + runs: Properties.Optional.runs, + suiteId: Properties.Required.suiteId) + } + +} + +// MARK: - Assertions + +extension NewPlan_EntryTests: AssertAddRequestJSON { } + +extension NewPlan_EntryTests: AssertEquatable { } + +extension NewPlan_EntryTests: AssertJSONSerializing { } + +extension NewPlan_EntryTests: AssertProperties { + + func assertRequiredProperties(in object: Object) { + XCTAssertEqual(object.suiteId, Properties.Required.suiteId) + } + + func assertOptionalProperties(in object: Object, areNil: Bool) { + if areNil { + XCTAssertNil(object.assignedtoId) + XCTAssertNil(object.caseIds) + XCTAssertNil(object.configIds) + XCTAssertNil(object.description) + XCTAssertNil(object.includeAll) + XCTAssertNil(object.name) + XCTAssertNil(object.runs) + } else { + XCTAssertNotNil(object.assignedtoId) + XCTAssertNotNil(object.caseIds) + XCTAssertNotNil(object.configIds) + XCTAssertNotNil(object.description) + XCTAssertNotNil(object.includeAll) + XCTAssertNotNil(object.name) + XCTAssertNotNil(object.runs) + XCTAssertEqual(object.assignedtoId, Properties.Optional.assignedtoId) + if let caseIds = object.caseIds { XCTAssertEqual(caseIds, Properties.Optional.caseIds) } + if let configIds = object.configIds { + if let configIdsInRuns = self.configIds(in: Properties.Optional.runs) { + XCTAssertEqual(configIds, configIdsInRuns) + } + } + XCTAssertEqual(object.description, Properties.Optional.description) + XCTAssertEqual(object.includeAll, Properties.Optional.includeAll) + XCTAssertEqual(object.name, Properties.Optional.name) + if let runs = object.runs { XCTAssertEqual(runs, Properties.Optional.runs) } + } + } + + func assertVariablePropertiesCanBeChanged(in object: inout Object) { + + let initialConfigIds = object.configIds + + object.assignedtoId = 1000 + object.caseIds = [1001, 1002, 1003] + object.description = "New Description" + object.includeAll = false + object.name = "New Name" + object.runs = [NewPlan_Entry_RunTests.objectWithRequiredProperties, NewPlan_Entry_RunTests.objectWithRequiredProperties] + object.suiteId = 1004 + + XCTAssertNotEqual(object.assignedtoId, Properties.Optional.assignedtoId) + if let caseIds = object.caseIds { XCTAssertNotEqual(caseIds, Properties.Optional.caseIds) } + if let configIds = object.configIds, let initialConfigIds = initialConfigIds { XCTAssertNotEqual(configIds, initialConfigIds) } + XCTAssertNotEqual(object.description, Properties.Optional.description) + XCTAssertNotEqual(object.includeAll, Properties.Optional.includeAll) + XCTAssertNotEqual(object.name, Properties.Optional.name) + XCTAssertNotEqual(object.runs!, Properties.Optional.runs) + if let runs = object.runs { XCTAssertNotEqual(runs, Properties.Optional.runs) } + XCTAssertNotEqual(object.suiteId, Properties.Required.suiteId) + + XCTAssertEqual(object.assignedtoId, 1000) + if let caseIds = object.caseIds { XCTAssertEqual(caseIds, [1001, 1002, 1003]) } + if let configIds = object.configIds { + if let allRunConfigIds = self.configIds(in: [NewPlan_Entry_RunTests.objectWithRequiredAndOptionalProperties, NewPlan_Entry_RunTests.objectWithRequiredAndOptionalProperties]) { + XCTAssertEqual(configIds, allRunConfigIds) + } + } + XCTAssertEqual(object.description, "New Description") + XCTAssertEqual(object.includeAll, false) + XCTAssertEqual(object.name, "New Name") + if let runs = object.runs { XCTAssertEqual(runs, [NewPlan_Entry_RunTests.objectWithRequiredProperties, NewPlan_Entry_RunTests.objectWithRequiredProperties]) } + XCTAssertEqual(object.suiteId, 1004) + } + +} + +extension NewPlan_EntryTests { + + /* + Helper to return all configIds from an array of NewPlan.Entry.Run's. + */ + func configIds(in runs: [NewPlan.Entry.Run]) -> [Int]? { + + var allRunConfigIds = [Int]() + var allRunConfigIdsAreNil = true + + for run in runs { + + guard let runConfigIds = run.configIds else { + continue + } + + allRunConfigIdsAreNil = false + allRunConfigIds.append(contentsOf: runConfigIds) + } + + if allRunConfigIdsAreNil { + return nil + } + + allRunConfigIds = Array(Set(allRunConfigIds)) // Remove duplicates + allRunConfigIds.sort() // configIds is sorted by default + + return allRunConfigIds + } + +} diff --git a/QuizTrainTests/Network/Models/Add/NewPlanTests.swift b/QuizTrainTests/Network/Models/Add/NewPlanTests.swift new file mode 100644 index 0000000..43d5f2e --- /dev/null +++ b/QuizTrainTests/Network/Models/Add/NewPlanTests.swift @@ -0,0 +1,127 @@ +import XCTest +@testable import QuizTrain + +// MARK: - Tests + +class NewPlanTests: XCTestCase, AddModelTests { + + typealias Object = NewPlan + + func testAddRequestJSON() { + _testAddRequestJSON() + } + + func testEquatable() { + _testEquatable() + } + + func testInit() { + _testInit() + } + + func testInitWithOptionalProperties() { + _testInitWithOptionalProperties() + } + + func testJSONSerializingSingleObjects() { + _testJSONSerializingSingleObjects() + } + + func testJSONSerializingMultipleObjects() { + _testJSONSerializingMultipleObjects() + } + + func testVariableProperties() { + _testVariableProperties() + } + +} + +// MARK: - Data + +extension NewPlanTests { + + struct Properties { + + struct Required { + static let name = "Name" + } + + struct Optional { + static let description = "Description" + static let entries = [NewPlan_EntryTests.objectWithRequiredAndOptionalProperties, NewPlan_EntryTests.objectWithRequiredAndOptionalProperties, NewPlan_EntryTests.objectWithRequiredAndOptionalProperties] + static let milestoneId = 10 + } + + } + +} + +// MARK: - Objects + +extension NewPlanTests: ObjectProvider { + + static var objectWithRequiredProperties: Object { + return Object(description: nil, + entries: nil, + milestoneId: nil, + name: Properties.Required.name) + } + + static var objectWithRequiredAndOptionalProperties: Object { + return Object(description: Properties.Optional.description, + entries: Properties.Optional.entries, + milestoneId: Properties.Optional.milestoneId, + name: Properties.Required.name) + } + +} + +// MARK: - Assertions + +extension NewPlanTests: AssertAddRequestJSON { } + +extension NewPlanTests: AssertEquatable { } + +extension NewPlanTests: AssertJSONSerializing { } + +extension NewPlanTests: AssertProperties { + + func assertRequiredProperties(in object: Object) { + XCTAssertEqual(object.name, Properties.Required.name) + } + + func assertOptionalProperties(in object: Object, areNil: Bool) { + if areNil { + XCTAssertNil(object.description) + XCTAssertNil(object.entries) + XCTAssertNil(object.milestoneId) + } else { + XCTAssertNotNil(object.description) + XCTAssertNotNil(object.entries) + XCTAssertNotNil(object.milestoneId) + XCTAssertEqual(object.description, Properties.Optional.description) + if let entries = object.entries { XCTAssertEqual(entries, Properties.Optional.entries) } + XCTAssertEqual(object.milestoneId, Properties.Optional.milestoneId) + } + } + + func assertVariablePropertiesCanBeChanged(in object: inout Object) { + + object.description = "New Description" + object.entries = [NewPlan_EntryTests.objectWithRequiredAndOptionalProperties, NewPlan_EntryTests.objectWithRequiredAndOptionalProperties] + object.milestoneId = 1000 + object.name = "New Name" + + XCTAssertNotEqual(object.description, Properties.Optional.description) + if let entries = object.entries { XCTAssertNotEqual(entries, Properties.Optional.entries) } + XCTAssertNotEqual(object.milestoneId, Properties.Optional.milestoneId) + XCTAssertNotEqual(object.name, Properties.Required.name) + + XCTAssertEqual(object.description, "New Description") + if let entries = object.entries { XCTAssertEqual(entries, [NewPlan_EntryTests.objectWithRequiredAndOptionalProperties, NewPlan_EntryTests.objectWithRequiredAndOptionalProperties]) } + XCTAssertEqual(object.milestoneId, 1000) + XCTAssertEqual(object.name, "New Name") + } + +} diff --git a/QuizTrainTests/Network/Models/Add/NewProjectTests.swift b/QuizTrainTests/Network/Models/Add/NewProjectTests.swift new file mode 100644 index 0000000..a7fd1cf --- /dev/null +++ b/QuizTrainTests/Network/Models/Add/NewProjectTests.swift @@ -0,0 +1,123 @@ +import XCTest +@testable import QuizTrain + +// MARK: - Tests + +class NewProjectTests: XCTestCase, AddModelTests { + + typealias Object = NewProject + + func testAddRequestJSON() { + _testAddRequestJSON() + } + + func testEquatable() { + _testEquatable() + } + + func testInit() { + _testInit() + } + + func testInitWithOptionalProperties() { + _testInitWithOptionalProperties() + } + + func testJSONSerializingSingleObjects() { + _testJSONSerializingSingleObjects() + } + + func testJSONSerializingMultipleObjects() { + _testJSONSerializingMultipleObjects() + } + + func testVariableProperties() { + _testVariableProperties() + } + +} + +// MARK: - Data + +extension NewProjectTests { + + struct Properties { + + struct Required { + static let name = "Name" + static let showAnnouncement = true + static let suiteMode = Project.SuiteMode.multipleSuites + } + + struct Optional { + static let announcement = "Announcement" + } + + } + +} + +// MARK: - Objects + +extension NewProjectTests: ObjectProvider { + + static var objectWithRequiredProperties: Object { + return Object(announcement: nil, + name: Properties.Required.name, + showAnnouncement: Properties.Required.showAnnouncement, + suiteMode: Properties.Required.suiteMode) + } + + static var objectWithRequiredAndOptionalProperties: Object { + return Object(announcement: Properties.Optional.announcement, + name: Properties.Required.name, + showAnnouncement: Properties.Required.showAnnouncement, + suiteMode: Properties.Required.suiteMode) + } + +} + +// MARK: - Assertions + +extension NewProjectTests: AssertAddRequestJSON { } + +extension NewProjectTests: AssertEquatable { } + +extension NewProjectTests: AssertJSONSerializing { } + +extension NewProjectTests: AssertProperties { + + func assertRequiredProperties(in object: Object) { + XCTAssertEqual(object.name, Properties.Required.name) + XCTAssertEqual(object.showAnnouncement, Properties.Required.showAnnouncement) + XCTAssertEqual(object.suiteMode, Properties.Required.suiteMode) + } + + func assertOptionalProperties(in object: Object, areNil: Bool) { + if areNil { + XCTAssertNil(object.announcement) + } else { + XCTAssertNotNil(object.announcement) + XCTAssertEqual(object.announcement, Properties.Optional.announcement) + } + } + + func assertVariablePropertiesCanBeChanged(in object: inout Object) { + + object.announcement = "New Announcement" + object.name = "New Name" + object.showAnnouncement = false + object.suiteMode = .singleSuite + + XCTAssertNotEqual(object.announcement, Properties.Optional.announcement) + XCTAssertNotEqual(object.name, Properties.Required.name) + XCTAssertNotEqual(object.showAnnouncement, Properties.Required.showAnnouncement) + XCTAssertNotEqual(object.suiteMode, Properties.Required.suiteMode) + + XCTAssertEqual(object.announcement, "New Announcement") + XCTAssertEqual(object.name, "New Name") + XCTAssertEqual(object.showAnnouncement, false) + XCTAssertEqual(object.suiteMode, .singleSuite) + } + +} diff --git a/QuizTrainTests/Network/Models/Add/NewResultTests.swift b/QuizTrainTests/Network/Models/Add/NewResultTests.swift new file mode 100644 index 0000000..f38ee2b --- /dev/null +++ b/QuizTrainTests/Network/Models/Add/NewResultTests.swift @@ -0,0 +1,211 @@ +import XCTest +@testable import QuizTrain + +// MARK: - Tests + +class NewResultTests: XCTestCase, AddModelTests, ValidatableTests { + + typealias Object = NewResult + + func testAddRequestJSON() { + _testAddRequestJSON() + } + + func testEquatable() { + _testEquatable() + } + + func testInit() { + _testInit() + } + + func testInitWithOptionalProperties() { + _testInitWithOptionalProperties() + } + + func testJSONSerializingSingleObjects() { + _testJSONSerializingSingleObjects() + } + + func testJSONSerializingMultipleObjects() { + _testJSONSerializingMultipleObjects() + } + + func testVariableProperties() { + _testVariableProperties() + } + + func testIsValid() { + _testIsValid() + } + + func testIsInvalid() { + _testIsInvalid() + } + +} + +// MARK: - Data + +extension NewResultTests { + + struct Properties { + + struct Required { /* none */ } + + struct Optional { + static let assignedtoId = 11 + static let comment = "Comment" + static let defects = "Defects" + static let elapsed = "4hr, 31min" + static let statusId = 10 + static let version = "1.2.3" + static let customFields = NewResultTests.customFields + } + + } + +} + +extension NewResultTests: CustomFieldsDataProvider { } + +// MARK: - Objects + +extension NewResultTests: ObjectProvider { + + static var objectWithRequiredProperties: Object { + return Object(assignedtoId: nil, + comment: nil, + defects: nil, + elapsed: nil, + statusId: nil, + version: nil, + customFields: nil) + } + + static var objectWithRequiredAndOptionalProperties: Object { + return Object(assignedtoId: Properties.Optional.assignedtoId, + comment: Properties.Optional.comment, + defects: Properties.Optional.defects, + elapsed: Properties.Optional.elapsed, + statusId: Properties.Optional.statusId, + version: Properties.Optional.version, + customFields: Properties.Optional.customFields) + } + +} + +extension NewResultTests: ValidatableObjectProvider { + + var validObject: Validatable { + return objectWithRequiredAndOptionalProperties + } + + var invalidObject: Validatable { + var object = objectWithRequiredAndOptionalProperties + object.assignedtoId = nil + object.comment = nil + object.statusId = nil + return object + } + +} + +// MARK: - Assertions + +extension NewResultTests: AssertAddRequestJSON { } + +extension NewResultTests: AssertCustomFields { } + +extension NewResultTests: AssertEquatable { } + +extension NewResultTests: AssertJSONSerializing { } + +extension NewResultTests: AssertProperties { + + func assertRequiredProperties(in object: Object) { /* none */ } + + func assertOptionalProperties(in object: Object, areNil: Bool) { + if areNil { + XCTAssertNil(object.assignedtoId) + XCTAssertNil(object.comment) + XCTAssertNil(object.defects) + XCTAssertNil(object.elapsed) + XCTAssertNil(object.statusId) + XCTAssertNil(object.version) + XCTAssertTrue(object.customFields.isEmpty) + } else { + XCTAssertNotNil(object.assignedtoId) + XCTAssertNotNil(object.comment) + XCTAssertNotNil(object.defects) + XCTAssertNotNil(object.elapsed) + XCTAssertNotNil(object.statusId) + XCTAssertNotNil(object.version) + XCTAssertNotNil(object.customFields) + XCTAssertEqual(object.assignedtoId, Properties.Optional.assignedtoId) + XCTAssertEqual(object.comment, Properties.Optional.comment) + XCTAssertEqual(object.defects, Properties.Optional.defects) + XCTAssertEqual(object.elapsed, Properties.Optional.elapsed) + XCTAssertEqual(object.statusId, Properties.Optional.statusId) + XCTAssertEqual(object.version, Properties.Optional.version) + XCTAssertEqual(object.customFields.count, Properties.Optional.customFields.count) + } + } + + func assertVariablePropertiesCanBeChanged(in object: inout Object) { + + // Properties + + object.assignedtoId = 1000 + object.comment = "New Comment" + object.defects = "New Defects" + object.elapsed = "99hr, 99min" + object.version = "4.5.6" + object.statusId = 1001 + object.customFields = NewResultTests.emptyCustomFields + + XCTAssertNotEqual(object.assignedtoId, Properties.Optional.assignedtoId) + XCTAssertNotEqual(object.comment, Properties.Optional.comment) + XCTAssertNotEqual(object.defects, Properties.Optional.defects) + XCTAssertNotEqual(object.elapsed, Properties.Optional.elapsed) + XCTAssertNotEqual(object.version, Properties.Optional.version) + XCTAssertNotEqual(object.statusId, Properties.Optional.statusId) + XCTAssertNotEqual(object.customFields.count, Properties.Optional.customFields.count) + + XCTAssertEqual(object.assignedtoId, 1000) + XCTAssertEqual(object.comment, "New Comment") + XCTAssertEqual(object.defects, "New Defects") + XCTAssertEqual(object.elapsed, "99hr, 99min") + XCTAssertEqual(object.version, "4.5.6") + XCTAssertEqual(object.statusId, 1001) + XCTAssertEqual(object.customFields.count, NewResultTests.emptyCustomFields.count) + + // Custom Fields + + let customFieldsCount = object.customFields.count + + object.customFields["custom_field_test01"] = "Custom Field Test 01" + object.customFields["custom_field_test02"] = 9000 + object.customFields["custom_field_test03"] = -8.0 + object.customFields["invalid_custom_field_test04"] = "This should not be added." + + XCTAssertNotNil(object.customFields["custom_field_test01"]) + XCTAssertNotNil(object.customFields["custom_field_test02"]) + XCTAssertNotNil(object.customFields["custom_field_test03"]) + XCTAssertNil(object.customFields["invalid_custom_field_test04"]) + + XCTAssertEqual(object.customFields["custom_field_test01"] as! String, "Custom Field Test 01") + XCTAssertEqual(object.customFields["custom_field_test02"] as! Int, 9000) + XCTAssertEqual(object.customFields["custom_field_test03"] as! Double, -8.0) + + XCTAssertEqual(object.customFields.count, customFieldsCount + 3) + + object.customFields.removeValue(forKey: "custom_field_test01") + + XCTAssertNil(object.customFields["custom_field_test01"]) + XCTAssertEqual(object.customFields.count, customFieldsCount + 2) + } + +} + +extension NewResultTests: AssertValidatable { } diff --git a/QuizTrainTests/Network/Models/Add/NewRunTests.swift b/QuizTrainTests/Network/Models/Add/NewRunTests.swift new file mode 100644 index 0000000..17f407c --- /dev/null +++ b/QuizTrainTests/Network/Models/Add/NewRunTests.swift @@ -0,0 +1,154 @@ +import XCTest +@testable import QuizTrain + +// MARK: - Tests + +class NewRunTests: XCTestCase, AddModelTests { + + typealias Object = NewRun + + func testAddRequestJSON() { + _testAddRequestJSON() + } + + func testEquatable() { + _testEquatable() + } + + func testInit() { + _testInit() + } + + func testInitWithOptionalProperties() { + _testInitWithOptionalProperties() + } + + func testJSONSerializingSingleObjects() { + _testJSONSerializingSingleObjects() + } + + func testJSONSerializingMultipleObjects() { + _testJSONSerializingMultipleObjects() + } + + func testVariableProperties() { + _testVariableProperties() + } + +} + +// MARK: - Data + +extension NewRunTests { + + struct Properties { + + struct Required { + static let name = "Name" + } + + struct Optional { + static let assignedtoId = 13 + static let caseIds = [10, 11, 12] + static let description = "Description" + static let includeAll = true + static let milestoneId = 14 + static let suiteId = 15 + } + + } + +} + +// MARK: - Objects + +extension NewRunTests: ObjectProvider { + + static var objectWithRequiredProperties: Object { + return Object(assignedtoId: nil, + caseIds: nil, + description: nil, + includeAll: nil, + milestoneId: nil, + name: Properties.Required.name, + suiteId: nil) + } + + static var objectWithRequiredAndOptionalProperties: Object { + return Object(assignedtoId: Properties.Optional.assignedtoId, + caseIds: Properties.Optional.caseIds, + description: Properties.Optional.description, + includeAll: Properties.Optional.includeAll, + milestoneId: Properties.Optional.milestoneId, + name: Properties.Required.name, + suiteId: Properties.Optional.suiteId) + } + +} + +// MARK: - Assertions + +extension NewRunTests: AssertAddRequestJSON { } + +extension NewRunTests: AssertEquatable { } + +extension NewRunTests: AssertJSONSerializing { } + +extension NewRunTests: AssertProperties { + + func assertRequiredProperties(in object: Object) { + XCTAssertEqual(object.name, Properties.Required.name) + } + + func assertOptionalProperties(in object: Object, areNil: Bool) { + if areNil { + XCTAssertNil(object.assignedtoId) + XCTAssertNil(object.caseIds) + XCTAssertNil(object.description) + XCTAssertNil(object.includeAll) + XCTAssertNil(object.milestoneId) + XCTAssertNil(object.suiteId) + } else { + XCTAssertNotNil(object.assignedtoId) + XCTAssertNotNil(object.caseIds) + XCTAssertNotNil(object.description) + XCTAssertNotNil(object.includeAll) + XCTAssertNotNil(object.milestoneId) + XCTAssertNotNil(object.suiteId) + XCTAssertEqual(object.assignedtoId, Properties.Optional.assignedtoId) + if let objectCaseIds = object.caseIds { XCTAssertEqual(objectCaseIds, Properties.Optional.caseIds) } + XCTAssertEqual(object.description, Properties.Optional.description) + XCTAssertEqual(object.includeAll, Properties.Optional.includeAll) + XCTAssertEqual(object.milestoneId, Properties.Optional.milestoneId) + XCTAssertEqual(object.suiteId, Properties.Optional.suiteId) + } + } + + func assertVariablePropertiesCanBeChanged(in object: inout Object) { + + object.assignedtoId = 1000 + object.caseIds = [1001, 1002, 1003] + object.description = "New Description" + object.includeAll = false + object.milestoneId = 1004 + object.name = "New Name" + object.suiteId = 1005 + + XCTAssertNotEqual(object.assignedtoId, Properties.Optional.assignedtoId) + if let objectCaseIds = object.caseIds { XCTAssertNotEqual(objectCaseIds, Properties.Optional.caseIds) } + XCTAssertNotEqual(object.description, Properties.Optional.description) + XCTAssertNotEqual(object.includeAll, Properties.Optional.includeAll) + XCTAssertNotEqual(object.milestoneId, Properties.Optional.milestoneId) + XCTAssertNotEqual(object.name, Properties.Required.name) + XCTAssertNotEqual(object.suiteId, Properties.Optional.suiteId) + + XCTAssertEqual(object.assignedtoId, 1000) + if let objectCaseIds = object.caseIds { XCTAssertEqual(objectCaseIds, [1001, 1002, 1003]) } + XCTAssertEqual(object.description, "New Description") + XCTAssertEqual(object.includeAll, false) + XCTAssertEqual(object.milestoneId, 1004) + XCTAssertEqual(object.name, "New Name") + XCTAssertEqual(object.suiteId, 1005) + } + +} diff --git a/QuizTrainTests/Network/Models/Add/NewSectionTests.swift b/QuizTrainTests/Network/Models/Add/NewSectionTests.swift new file mode 100644 index 0000000..3dd0408 --- /dev/null +++ b/QuizTrainTests/Network/Models/Add/NewSectionTests.swift @@ -0,0 +1,127 @@ +import XCTest +@testable import QuizTrain + +// MARK: - Tests + +class NewSectionTests: XCTestCase, AddModelTests { + + typealias Object = NewSection + + func testAddRequestJSON() { + _testAddRequestJSON() + } + + func testEquatable() { + _testEquatable() + } + + func testInit() { + _testInit() + } + + func testInitWithOptionalProperties() { + _testInitWithOptionalProperties() + } + + func testJSONSerializingSingleObjects() { + _testJSONSerializingSingleObjects() + } + + func testJSONSerializingMultipleObjects() { + _testJSONSerializingMultipleObjects() + } + + func testVariableProperties() { + _testVariableProperties() + } + +} + +// MARK: - Data + +extension NewSectionTests { + + struct Properties { + + struct Required { + static let name = "Name" + } + + struct Optional { + static let description = "Description" + static let parentId = 10 + static let suiteId = 11 + } + + } + +} + +// MARK: - Objects + +extension NewSectionTests: ObjectProvider { + + static var objectWithRequiredProperties: Object { + return Object(description: nil, + name: Properties.Required.name, + parentId: nil, + suiteId: nil) + } + + static var objectWithRequiredAndOptionalProperties: Object { + return Object(description: Properties.Optional.description, + name: Properties.Required.name, + parentId: Properties.Optional.parentId, + suiteId: Properties.Optional.suiteId) + } + +} + +// MARK: - Assertions + +extension NewSectionTests: AssertAddRequestJSON { } + +extension NewSectionTests: AssertEquatable { } + +extension NewSectionTests: AssertJSONSerializing { } + +extension NewSectionTests: AssertProperties { + + func assertRequiredProperties(in object: Object) { + XCTAssertEqual(object.name, Properties.Required.name) + } + + func assertOptionalProperties(in object: Object, areNil: Bool) { + if areNil { + XCTAssertNil(object.description) + XCTAssertNil(object.parentId) + XCTAssertNil(object.suiteId) + } else { + XCTAssertNotNil(object.description) + XCTAssertNotNil(object.parentId) + XCTAssertNotNil(object.suiteId) + XCTAssertEqual(object.description, Properties.Optional.description) + XCTAssertEqual(object.parentId, Properties.Optional.parentId) + XCTAssertEqual(object.suiteId, Properties.Optional.suiteId) + } + } + + func assertVariablePropertiesCanBeChanged(in object: inout Object) { + + object.description = "New Description" + object.name = "New Name" + object.parentId = 1000 + object.suiteId = 1001 + + XCTAssertNotEqual(object.description, Properties.Optional.description) + XCTAssertNotEqual(object.name, Properties.Required.name) + XCTAssertNotEqual(object.parentId, Properties.Optional.parentId) + XCTAssertNotEqual(object.suiteId, Properties.Optional.suiteId) + + XCTAssertEqual(object.description, "New Description") + XCTAssertEqual(object.name, "New Name") + XCTAssertEqual(object.parentId, 1000) + XCTAssertEqual(object.suiteId, 1001) + } + +} diff --git a/QuizTrainTests/Network/Models/Add/NewSuiteTests.swift b/QuizTrainTests/Network/Models/Add/NewSuiteTests.swift new file mode 100644 index 0000000..8cd58b8 --- /dev/null +++ b/QuizTrainTests/Network/Models/Add/NewSuiteTests.swift @@ -0,0 +1,109 @@ +import XCTest +@testable import QuizTrain + +// MARK: - Tests + +class NewSuiteTests: XCTestCase, AddModelTests { + + typealias Object = NewSuite + + func testAddRequestJSON() { + _testAddRequestJSON() + } + + func testEquatable() { + _testEquatable() + } + + func testInit() { + _testInit() + } + + func testInitWithOptionalProperties() { + _testInitWithOptionalProperties() + } + + func testJSONSerializingSingleObjects() { + _testJSONSerializingSingleObjects() + } + + func testJSONSerializingMultipleObjects() { + _testJSONSerializingMultipleObjects() + } + + func testVariableProperties() { + _testVariableProperties() + } + +} + +// MARK: - Data + +extension NewSuiteTests { + + struct Properties { + + struct Required { + static let name = "Name" + } + + struct Optional { + static let description = "Description" + } + + } + +} + +// MARK: - Objects + +extension NewSuiteTests: ObjectProvider { + + static var objectWithRequiredProperties: Object { + return Object(description: nil, + name: Properties.Required.name) + } + + static var objectWithRequiredAndOptionalProperties: Object { + return Object(description: Properties.Optional.description, + name: Properties.Required.name) + } + +} + +// MARK: - Assertions + +extension NewSuiteTests: AssertAddRequestJSON { } + +extension NewSuiteTests: AssertEquatable { } + +extension NewSuiteTests: AssertJSONSerializing { } + +extension NewSuiteTests: AssertProperties { + + func assertRequiredProperties(in object: Object) { + XCTAssertEqual(object.name, Properties.Required.name) + } + + func assertOptionalProperties(in object: Object, areNil: Bool) { + if areNil { + XCTAssertNil(object.description) + } else { + XCTAssertNotNil(object.description) + XCTAssertEqual(object.description, Properties.Optional.description) + } + } + + func assertVariablePropertiesCanBeChanged(in object: inout Object) { + + object.description = "New Description" + object.name = "New Name" + + XCTAssertNotEqual(object.description, Properties.Optional.description) + XCTAssertNotEqual(object.name, Properties.Required.name) + + XCTAssertEqual(object.description, "New Description") + XCTAssertEqual(object.name, "New Name") + } + +} diff --git a/QuizTrainTests/Network/Models/Add/NewTestResults.ResultTests.swift b/QuizTrainTests/Network/Models/Add/NewTestResults.ResultTests.swift new file mode 100644 index 0000000..9de9723 --- /dev/null +++ b/QuizTrainTests/Network/Models/Add/NewTestResults.ResultTests.swift @@ -0,0 +1,220 @@ +import XCTest +@testable import QuizTrain + +// MARK: - Tests + +class NewTestResults_ResultTests: XCTestCase, AddModelTests, ValidatableTests { + + typealias Object = NewTestResults.Result + + func testAddRequestJSON() { + _testAddRequestJSON() + } + + func testEquatable() { + _testEquatable() + } + + func testInit() { + _testInit() + } + + func testInitWithOptionalProperties() { + _testInitWithOptionalProperties() + } + + func testJSONSerializingSingleObjects() { + _testJSONSerializingSingleObjects() + } + + func testJSONSerializingMultipleObjects() { + _testJSONSerializingMultipleObjects() + } + + func testVariableProperties() { + _testVariableProperties() + } + + func testIsValid() { + _testIsValid() + } + + func testIsInvalid() { + _testIsInvalid() + } + +} + +// MARK: - Data + +extension NewTestResults_ResultTests { + + struct Properties { + + struct Required { + static let testId = 10 + } + + struct Optional { + static let assignedtoId = 11 + static let comment = "Comment" + static let defects = "Defects" + static let elapsed = "4hr, 31min" + static let statusId = 12 + static let version = "1.2.3" + static let customFields = NewTestResults_ResultTests.customFields + } + + } + +} + +extension NewTestResults_ResultTests: CustomFieldsDataProvider { } + +// MARK: - Objects + +extension NewTestResults_ResultTests: ObjectProvider { + + static var objectWithRequiredProperties: Object { + return Object(assignedtoId: nil, + comment: nil, + defects: nil, + elapsed: nil, + statusId: nil, + testId: Properties.Required.testId, + version: nil, + customFields: nil) + } + + static var objectWithRequiredAndOptionalProperties: Object { + return Object(assignedtoId: Properties.Optional.assignedtoId, + comment: Properties.Optional.comment, + defects: Properties.Optional.defects, + elapsed: Properties.Optional.elapsed, + statusId: Properties.Optional.statusId, + testId: Properties.Required.testId, + version: Properties.Optional.version, + customFields: Properties.Optional.customFields) + } + +} + +extension NewTestResults_ResultTests: ValidatableObjectProvider { + + var validObject: Validatable { + return objectWithRequiredAndOptionalProperties + } + + var invalidObject: Validatable { + var object = objectWithRequiredAndOptionalProperties + object.assignedtoId = nil + object.comment = nil + object.statusId = nil + return object + } + +} + +// MARK: - Assertions + +extension NewTestResults_ResultTests: AssertAddRequestJSON { } + +extension NewTestResults_ResultTests: AssertCustomFields { } + +extension NewTestResults_ResultTests: AssertEquatable { } + +extension NewTestResults_ResultTests: AssertJSONSerializing { } + +extension NewTestResults_ResultTests: AssertProperties { + + func assertRequiredProperties(in object: Object) { + XCTAssertEqual(object.testId, Properties.Required.testId) + } + + func assertOptionalProperties(in object: Object, areNil: Bool) { + if areNil { + XCTAssertNil(object.assignedtoId) + XCTAssertNil(object.comment) + XCTAssertNil(object.defects) + XCTAssertNil(object.elapsed) + XCTAssertNil(object.statusId) + XCTAssertNil(object.version) + XCTAssertTrue(object.customFields.isEmpty) + } else { + XCTAssertNotNil(object.assignedtoId) + XCTAssertNotNil(object.comment) + XCTAssertNotNil(object.defects) + XCTAssertNotNil(object.elapsed) + XCTAssertNotNil(object.statusId) + XCTAssertNotNil(object.version) + XCTAssertNotNil(object.customFields) + XCTAssertEqual(object.assignedtoId, Properties.Optional.assignedtoId) + XCTAssertEqual(object.comment, Properties.Optional.comment) + XCTAssertEqual(object.defects, Properties.Optional.defects) + XCTAssertEqual(object.elapsed, Properties.Optional.elapsed) + XCTAssertEqual(object.statusId, Properties.Optional.statusId) + XCTAssertEqual(object.version, Properties.Optional.version) + XCTAssertEqual(object.customFields.count, Properties.Optional.customFields.count) + } + } + + func assertVariablePropertiesCanBeChanged(in object: inout Object) { + + // Properties + + object.assignedtoId = 1000 + object.comment = "New Comment" + object.defects = "New Defects" + object.elapsed = "99hr, 99min" + object.version = "4.5.6" + object.statusId = 1001 + object.testId = 1002 + object.customFields = NewTestResults_ResultTests.emptyCustomFields + + XCTAssertNotEqual(object.assignedtoId, Properties.Optional.assignedtoId) + XCTAssertNotEqual(object.comment, Properties.Optional.comment) + XCTAssertNotEqual(object.defects, Properties.Optional.defects) + XCTAssertNotEqual(object.elapsed, Properties.Optional.elapsed) + XCTAssertNotEqual(object.version, Properties.Optional.version) + XCTAssertNotEqual(object.statusId, Properties.Optional.statusId) + XCTAssertNotEqual(object.testId, Properties.Required.testId) + XCTAssertNotEqual(object.customFields.count, Properties.Optional.customFields.count) + + XCTAssertEqual(object.assignedtoId, 1000) + XCTAssertEqual(object.comment, "New Comment") + XCTAssertEqual(object.defects, "New Defects") + XCTAssertEqual(object.elapsed, "99hr, 99min") + XCTAssertEqual(object.version, "4.5.6") + XCTAssertEqual(object.statusId, 1001) + XCTAssertEqual(object.testId, 1002) + XCTAssertEqual(object.customFields.count, NewTestResults_ResultTests.emptyCustomFields.count) + + // Custom Fields + + let customFieldsCount = object.customFields.count + + object.customFields["custom_field_test01"] = "Custom Field Test 01" + object.customFields["custom_field_test02"] = 9000 + object.customFields["custom_field_test03"] = -8.0 + object.customFields["invalid_custom_field_test04"] = "This should not be added." + + XCTAssertNotNil(object.customFields["custom_field_test01"]) + XCTAssertNotNil(object.customFields["custom_field_test02"]) + XCTAssertNotNil(object.customFields["custom_field_test03"]) + XCTAssertNil(object.customFields["invalid_custom_field_test04"]) + + XCTAssertEqual(object.customFields["custom_field_test01"] as! String, "Custom Field Test 01") + XCTAssertEqual(object.customFields["custom_field_test02"] as! Int, 9000) + XCTAssertEqual(object.customFields["custom_field_test03"] as! Double, -8.0) + + XCTAssertEqual(object.customFields.count, customFieldsCount + 3) + + object.customFields.removeValue(forKey: "custom_field_test01") + + XCTAssertNil(object.customFields["custom_field_test01"]) + XCTAssertEqual(object.customFields.count, customFieldsCount + 2) + } + +} + +extension NewTestResults_ResultTests: AssertValidatable { } diff --git a/QuizTrainTests/Network/Models/Add/NewTestResultsTests.swift b/QuizTrainTests/Network/Models/Add/NewTestResultsTests.swift new file mode 100644 index 0000000..9123e43 --- /dev/null +++ b/QuizTrainTests/Network/Models/Add/NewTestResultsTests.swift @@ -0,0 +1,117 @@ +import XCTest +@testable import QuizTrain + +// MARK: - Tests + +class NewTestResultsTests: XCTestCase, AddModelTests, ValidatableTests { + + typealias Object = NewTestResults + + func testAddRequestJSON() { + _testAddRequestJSON() + } + + func testEquatable() { + _testEquatable() + } + + func testInit() { + _testInit() + } + + func testInitWithOptionalProperties() { + _testInitWithOptionalProperties() + } + + func testJSONSerializingSingleObjects() { + _testJSONSerializingSingleObjects() + } + + func testJSONSerializingMultipleObjects() { + _testJSONSerializingMultipleObjects() + } + + func testVariableProperties() { + _testVariableProperties() + } + + func testIsValid() { + _testIsValid() + } + + func testIsInvalid() { + _testIsInvalid() + } + +} + +// MARK: - Data + +extension NewTestResultsTests { + + struct Properties { + + struct Required { + static let results = [NewTestResults_ResultTests.objectWithRequiredAndOptionalProperties, NewTestResults_ResultTests.objectWithRequiredAndOptionalProperties, NewTestResults_ResultTests.objectWithRequiredAndOptionalProperties] + } + + struct Optional { /* none */ } + + } + +} + +extension NewTestResultsTests: CustomFieldsDataProvider { } + +// MARK: - Objects + +extension NewTestResultsTests: ObjectProvider { + + static var objectWithRequiredProperties: Object { + return Object(results: Properties.Required.results) + } + + static var objectWithRequiredAndOptionalProperties: Object { + return Object(results: Properties.Required.results) + } + +} + +extension NewTestResultsTests: ValidatableObjectProvider { + + var validObject: Validatable { + return objectWithRequiredAndOptionalProperties + } + + var invalidObject: Validatable { + return Object(results: [NewTestResults_ResultTests.objectWithRequiredProperties, NewTestResults_ResultTests.objectWithRequiredProperties, NewTestResults_ResultTests.objectWithRequiredProperties]) + } + +} + +// MARK: - Assertions + +extension NewTestResultsTests: AssertAddRequestJSON { } + +extension NewTestResultsTests: AssertEquatable { } + +extension NewTestResultsTests: AssertJSONSerializing { } + +extension NewTestResultsTests: AssertProperties { + + func assertRequiredProperties(in object: Object) { + XCTAssertEqual(object.results, Properties.Required.results) + } + + func assertOptionalProperties(in object: Object, areNil: Bool) { /* none */ } + + func assertVariablePropertiesCanBeChanged(in object: inout Object) { + object.results.append(NewTestResults_ResultTests.objectWithRequiredProperties) + XCTAssertNotEqual(object.results, Properties.Required.results) + object.results.remove(at: object.results.count - 1) + XCTAssertEqual(object.results, Properties.Required.results) + } + +} + +extension NewTestResultsTests: AssertValidatable { } diff --git a/QuizTrainTests/Network/Models/Testing Protocols/AddModelTests.swift b/QuizTrainTests/Network/Models/Testing Protocols/AddModelTests.swift new file mode 100644 index 0000000..0af3236 --- /dev/null +++ b/QuizTrainTests/Network/Models/Testing Protocols/AddModelTests.swift @@ -0,0 +1 @@ +protocol AddModelTests: AddRequestJSONTests, EquatableTests, InitTests, JSONSerializingTests, VariablePropertyTests { } diff --git a/QuizTrainTests/Network/Models/Testing Protocols/UpdateModelTests.swift b/QuizTrainTests/Network/Models/Testing Protocols/UpdateModelTests.swift new file mode 100644 index 0000000..c0bc9e7 --- /dev/null +++ b/QuizTrainTests/Network/Models/Testing Protocols/UpdateModelTests.swift @@ -0,0 +1 @@ +protocol UpdateModelTests: UpdateRequestJSONTests, EquatableTests, InitTests, JSONSerializingTests, VariablePropertyTests { } diff --git a/QuizTrainTests/Network/Models/Update/UpdatePlanEntryRunsTests.swift b/QuizTrainTests/Network/Models/Update/UpdatePlanEntryRunsTests.swift new file mode 100644 index 0000000..d3fb75c --- /dev/null +++ b/QuizTrainTests/Network/Models/Update/UpdatePlanEntryRunsTests.swift @@ -0,0 +1,127 @@ +import XCTest +@testable import QuizTrain + +// MARK: - Tests + +class UpdatePlanEntryRunsTests: XCTestCase, UpdateModelTests { + + typealias Object = UpdatePlanEntryRuns + + func testUpdateRequestJSON() { + _testUpdateRequestJSON() + } + + func testEquatable() { + _testEquatable() + } + + func testInit() { + _testInit() + } + + func testInitWithOptionalProperties() { + _testInitWithOptionalProperties() + } + + func testJSONSerializingSingleObjects() { + _testJSONSerializingSingleObjects() + } + + func testJSONSerializingMultipleObjects() { + _testJSONSerializingMultipleObjects() + } + + func testVariableProperties() { + _testVariableProperties() + } + +} + +// MARK: - Data + +extension UpdatePlanEntryRunsTests { + + struct Properties { + + struct Required { /* none */ } + + struct Optional { + static let assignedtoId = 10 + static let caseIds = [11, 12, 13] + static let description = "Description" + static let includeAll = true + } + + } + +} + +// MARK: - Objects + +extension UpdatePlanEntryRunsTests: ObjectProvider { + + static var objectWithRequiredProperties: Object { + return Object(assignedtoId: nil, + caseIds: nil, + description: nil, + includeAll: nil) + } + + static var objectWithRequiredAndOptionalProperties: Object { + return Object(assignedtoId: Properties.Optional.assignedtoId, + caseIds: Properties.Optional.caseIds, + description: Properties.Optional.description, + includeAll: Properties.Optional.includeAll) + } + +} + +// MARK: - Assertions + +extension UpdatePlanEntryRunsTests: AssertUpdateRequestJSON { } + +extension UpdatePlanEntryRunsTests: AssertEquatable { } + +extension UpdatePlanEntryRunsTests: AssertJSONSerializing { } + +extension UpdatePlanEntryRunsTests: AssertProperties { + + func assertRequiredProperties(in object: Object) { /* none */ } + + func assertOptionalProperties(in object: Object, areNil: Bool) { + if areNil { + XCTAssertNil(object.assignedtoId) + XCTAssertNil(object.caseIds) + XCTAssertNil(object.description) + XCTAssertNil(object.includeAll) + } else { + XCTAssertNotNil(object.assignedtoId) + XCTAssertNotNil(object.caseIds) + XCTAssertNotNil(object.description) + XCTAssertNotNil(object.includeAll) + XCTAssertEqual(object.assignedtoId, Properties.Optional.assignedtoId) + XCTAssertEqual(object.caseIds!, Properties.Optional.caseIds) + XCTAssertEqual(object.description, Properties.Optional.description) + XCTAssertEqual(object.includeAll, Properties.Optional.includeAll) + } + } + + func assertVariablePropertiesCanBeChanged(in object: inout Object) { + + object.assignedtoId = 1000 + object.caseIds = [1001, 1002, 1003] + object.description = "New Description" + object.includeAll = false + + XCTAssertNotEqual(object.assignedtoId, Properties.Optional.assignedtoId) + XCTAssertNotEqual(object.caseIds!, Properties.Optional.caseIds) + XCTAssertNotEqual(object.description, Properties.Optional.description) + XCTAssertNotEqual(object.includeAll, Properties.Optional.includeAll) + + XCTAssertEqual(object.assignedtoId, 1000) + XCTAssertEqual(object.caseIds!, [1001, 1002, 1003]) + XCTAssertEqual(object.description, "New Description") + XCTAssertEqual(object.includeAll, false) + } + +} diff --git a/QuizTrainTests/Network/ObjectAPITests.swift b/QuizTrainTests/Network/ObjectAPITests.swift new file mode 100644 index 0000000..f5f1504 --- /dev/null +++ b/QuizTrainTests/Network/ObjectAPITests.swift @@ -0,0 +1,5274 @@ +import XCTest +@testable import QuizTrain + +/* + ObjectAPI systems tests. These tests run against a real-world TestRail + instance. Sections: + + - Initialization, deinit, stored properties. + - Shared Setup/Teardown logic. This created/destroys a TestProject to be used + during testing. + - Data Provider logic. This provides data for objects used during tests. + - Tests separated into related sections (Arrange). + - Assertions separated into related sections used by tests. These perform API + requests and assert their results (Act, Assert). In some cases tests may + perform extra assertions based on the context of a specific test. + + Tests will create and delete several projects and their contents during + testing. They will not delete anything else unless you or someone modifies them + to do so. Generally it is safe to run tests against production so long as you + are OK with rate limits potentially being triggered while tests run and with + tests reading some production data in your instance. Even so it is advised that + you backup your production instance fully before running tests and verify that + the backup is valid. + + If tests crash rouge test projects may be abandoned in your instance. Their + names will be prefixed with "QuizTrainTests". It is safe to delete them so long + as you have no production projects starting with the same name. + + For tests to run you must populate TestCredentials.json with all properties + required by TestCredentials.swift. + + The user must have permissions to create/read/update/delete projects and all + objects inside of them. Furthermore any required custom fields you have created + must either have default values set, or be temporarily unmarked as required, + for tests to run. Alternatively you can add your required custom field data to + appropriate objects in the "Data Provider" section. + */ +class ObjectAPITests: XCTestCase { + + let timeout = 60.0 + let objectCount = 2 // Quantity of each object in TestProject to create. This should be 2 or higher for tests to work well. Values higher than 3 might slow down setUp and tests considerably due to rate limiting. + + static var testCredentials: TestCredentials! + var testCredentials: TestCredentials! { get { return ObjectAPITests.testCredentials } set { ObjectAPITests.testCredentials = newValue } } + + static var objectAPI: ObjectAPI! + var objectAPI: ObjectAPI! { get { return ObjectAPITests.objectAPI } set { ObjectAPITests.objectAPI = newValue } } + + static var testProject: TestProject! + var testProject: TestProject! { get { return ObjectAPITests.testProject } set { ObjectAPITests.testProject = newValue } } + + override func setUp() { + super.setUp() + continueAfterFailure = false + setUpTestCredentials() + setUpObjectAPI() + setUpTestProject() + continueAfterFailure = true + } + + override static func tearDown() { + super.tearDown() + tearDownTestProject() + } + +} + +// MARK: - TestProject + +extension ObjectAPITests { + + struct TestProject { + var cases: [Case] + var caseFields: [CaseField] + var configurationGroups: [ConfigurationGroup] + var configurations: [Configuration] + var milestones: [Milestone] + var plans: [Plan] + var project: Project + var resultFields: [ResultField] + var runs: [Run] + var sections: [Section] + var suites: [Suite] + var templates: [Template] + var tests: [Test] + var user: User + } + +} + +// MARK: - Setup/Teardown + +extension ObjectAPITests { + + func setUpTestCredentials() { + + guard testCredentials == nil else { + return + } + + do { + let bundle = Bundle(for: type(of: self)) + testCredentials = try TestCredentials.load(from: bundle) + } catch { + XCTFail("FAILED: \(#file):\(#line):\(#function): \(error)") + } + } + + func setUpObjectAPI() { + + guard objectAPI == nil else { + return + } + + guard testCredentials != nil else { + XCTFail("FAILED: \(#file):\(#line):\(#function)") + return + } + + objectAPI = ObjectAPI(username: testCredentials.username, secret: testCredentials.secret, hostname: testCredentials.hostname, port: testCredentials.port, scheme: testCredentials.scheme) + } + + // swiftlint:disable:next cyclomatic_complexity + func setUpTestProject() { + + guard testProject == nil else { + return + } + + continueAfterFailure = false + + // Project + + let newProject = NewProject(announcement: "Project Announcement", name: "QuizTrainTests - Test Project", showAnnouncement: true, suiteMode: .multipleSuites) + guard let project = assertAddProject(newProject) else { + XCTFail("FAILED: \(#file):\(#line):\(#function)") + return + } + + defer { + // If setup fails delete the test project from TestRail. This will + // also delete any other objects created below in the project. + if testProject == nil { + continueAfterFailure = true + assertDeleteProject(project) + XCTFail("FAILED: \(#file):\(#line):\(#function)") + } + } + + // Suites + + var suites = [Suite]() + + for i in 0.. NewCase { + + precondition(testProject != nil, "The Test Project must be setup before invoking \(#function)") + + var newCase = NewCase(estimate: nil, + milestoneId: nil, + priorityId: nil, + refs: nil, + templateId: nil, + title: "Test Add: Case Title", + typeId: nil, + customFields: nil) + + switch properties { + case .requiredProperties: + break + case .requiredAndOptionalProperties: + newCase.estimate = "3m" + newCase.milestoneId = nil // Setting the milestoneId does not appear to work. + newCase.priorityId = 3 + newCase.refs = "RF-1, RF-2" + newCase.templateId = testProject.templates[0].id + newCase.typeId = 1 + // data.customFields can be set by caller if necessary. + } + + return newCase + } + + func newConfiguration() -> NewConfiguration { + precondition(testProject != nil, "The Test Project must be setup before invoking \(#function)") + return NewConfiguration(name: "Test Add: Configuration Name") + } + + func newConfigurationGroup() -> NewConfigurationGroup { + precondition(testProject != nil, "The Test Project must be setup before invoking \(#function)") + return NewConfigurationGroup(name: "Test Add: Configuration Group Name") + } + + func newMilestone(with properties: Properties) -> NewMilestone { + + precondition(testProject != nil, "The Test Project must be setup before invoking \(#function)") + + var newMilestone = NewMilestone(description: nil, + dueOn: nil, + name: "Test Add: Milestone Name", + parentId: nil, + startOn: nil) + + switch properties { + case .requiredProperties: + break + case .requiredAndOptionalProperties: + let now = Date() + newMilestone.description = "Test Add: Milestone Description" + newMilestone.dueOn = Date(timeInterval: 86400, since: now) + newMilestone.parentId = nil // Caller can set if necessary. + newMilestone.startOn = now + } + + return newMilestone + } + + func newPlan(with properties: Properties) -> NewPlan { + + precondition(testProject != nil, "The Test Project must be setup before invoking \(#function)") + + var newPlan = NewPlan(description: nil, + entries: nil, + milestoneId: nil, + name: "Test Add: Plan Name") + + switch properties { + case .requiredProperties: + break + case .requiredAndOptionalProperties: + newPlan.description = "Test Add: Plan Description" + newPlan.entries = [] + for _ in 0..<3 { + let newPlanEntry = self.newPlanEntry(with: .requiredAndOptionalProperties) + newPlan.entries?.append(newPlanEntry) + } + newPlan.milestoneId = testProject.milestones[0].id + } + + return newPlan + } + + func newPlanEntry(with properties: Properties) -> NewPlan.Entry { + + precondition(testProject != nil, "The Test Project must be setup before invoking \(#function)") + + let suite = testProject.suites[0] + + var newPlanEntry = NewPlan.Entry(assignedtoId: nil, + caseIds: nil, + description: nil, + includeAll: nil, + name: nil, + runs: nil, + suiteId: suite.id) + + switch properties { + case .requiredProperties: + break + case .requiredAndOptionalProperties: + + let suiteCaseIds = testProject.cases.filter { $0.suiteId == suite.id } + + newPlanEntry.assignedtoId = testProject.user.id + newPlanEntry.caseIds = suiteCaseIds.flatMap { $0.id } + newPlanEntry.description = "Test Add: Plan Entry Description" + newPlanEntry.includeAll = false + newPlanEntry.name = "Test Add: Plan Entry Name" + newPlanEntry.runs = [] + + var groupedConfigIds = [Int: [Int]]() + for i in 0.. NewPlan.Entry.Run { + + precondition(testProject != nil, "The Test Project must be setup before invoking \(#function)") + + return NewPlan.Entry.Run(assignedtoId: nil, + caseIds: nil, + configIds: nil, + description: nil, + includeAll: nil, + milestoneId: nil, + name: nil, + suiteId: nil) + } + + func newProject(with properties: Properties) -> NewProject { + + precondition(testProject != nil, "The Test Project must be setup before invoking \(#function)") + + var newProject = NewProject(announcement: nil, + name: "QuizTrainTests: Test Add: Project Name", + showAnnouncement: false, + suiteMode: .multipleSuites) + + switch properties { + case .requiredProperties: + break + case .requiredAndOptionalProperties: + newProject.announcement = "QuizTrainTests: Test Add: Project Annoucement" + newProject.showAnnouncement = true + } + + return newProject + } + + func newResult(with properties: Properties) -> NewResult { + + precondition(testProject != nil, "The Test Project must be setup before invoking \(#function)") + + var newResult = NewResult(assignedtoId: nil, + comment: nil, + defects: nil, + elapsed: nil, + statusId: 1, + version: nil, + customFields: nil) + + switch properties { + case .requiredProperties: + break + case .requiredAndOptionalProperties: + newResult.assignedtoId = testProject.user.id + newResult.comment = "Test Add: Comment" + newResult.defects = "Test Add: Defects" + newResult.elapsed = "1m 30s" + newResult.version = "INVALID" + // data.customFields can be set by caller if necessary. + } + + return newResult + } + + func newCaseResults(with properties: Properties) -> NewCaseResults { + + precondition(testProject != nil, "The Test Project must be setup before invoking \(#function)") + + var newCaseResults = NewCaseResults(results: []) + + for _ in 0..<3 { + newCaseResults.results.append(newCaseResultsResult(with: properties)) + } + + return newCaseResults + } + + func newCaseResultsResult(with properties: Properties) -> NewCaseResults.Result { + + precondition(testProject != nil, "The Test Project must be setup before invoking \(#function)") + + var newCaseResultsResult = NewCaseResults.Result(assignedtoId: nil, + caseId: testProject.cases[0].id, + comment: nil, + defects: nil, + elapsed: nil, + statusId: nil, + version: nil, + customFields: nil) + + switch properties { + case .requiredProperties: + break + case .requiredAndOptionalProperties: + newCaseResultsResult.assignedtoId = testProject.user.id + newCaseResultsResult.comment = "Test Add: Comment" + newCaseResultsResult.defects = "Test Add: Defects" + newCaseResultsResult.elapsed = "1m 30s" + newCaseResultsResult.statusId = 1 + newCaseResultsResult.version = "INVALID" + // customFields can be set by caller if necessary. + } + + return newCaseResultsResult + } + + func newTestResults(with properties: Properties) -> NewTestResults { + + precondition(testProject != nil, "The Test Project must be setup before invoking \(#function)") + + var newTestResults = NewTestResults(results: []) + + for _ in 0..<3 { + newTestResults.results.append(newTestResultsResult(with: properties)) + } + + return newTestResults + } + + func newTestResultsResult(with properties: Properties) -> NewTestResults.Result { + + precondition(testProject != nil, "The Test Project must be setup before invoking \(#function)") + + var newTestResultsResult = NewTestResults.Result(assignedtoId: nil, + comment: nil, + defects: nil, + elapsed: nil, + statusId: nil, + testId: testProject.tests[0].id, + version: nil, + customFields: nil) + + switch properties { + case .requiredProperties: + break + case .requiredAndOptionalProperties: + newTestResultsResult.assignedtoId = testProject.user.id + newTestResultsResult.comment = "Test Add: Comment" + newTestResultsResult.defects = "Test Add: Defects" + newTestResultsResult.elapsed = "1m 30s" + newTestResultsResult.statusId = 1 + newTestResultsResult.version = "INVALID" + // customFields can be set by caller if necessary. + } + + return newTestResultsResult + } + + func newRun(with properties: Properties) -> NewRun { + + precondition(testProject != nil, "The Test Project must be setup before invoking \(#function)") + + var newRun = NewRun(assignedtoId: nil, + caseIds: testProject.cases.flatMap { $0.id }, + description: nil, + includeAll: false, + milestoneId: nil, + name: "Test Add: Run Name", + suiteId: nil) + + if testProject.project.suiteMode != .singleSuite { + newRun.suiteId = testProject.suites[0].id + } + + switch properties { + case .requiredProperties: + break + case .requiredAndOptionalProperties: + newRun.assignedtoId = testProject.user.id + newRun.description = "Test Add: Run Description" + newRun.milestoneId = testProject.milestones[0].id + } + + return newRun + } + + func newSection(with properties: Properties) -> NewSection { + + precondition(testProject != nil, "The Test Project must be setup before invoking \(#function)") + + var newSection = NewSection(description: nil, + name: "Test Add: Section Name", + parentId: nil, + suiteId: nil) + + if testProject.project.suiteMode != .singleSuite { + newSection.suiteId = testProject.suites[0].id + } + + switch properties { + case .requiredProperties: + break + case .requiredAndOptionalProperties: + newSection.description = "Test Add: Section Description" + newSection.parentId = nil // Caller can set if necessary. + } + + return newSection + } + + func newSuite(with properties: Properties) -> NewSuite { + + precondition(testProject != nil, "The Test Project must be setup before invoking \(#function)") + + var newSuite = NewSuite(description: nil, + name: "Test Add: Suite Name") + + switch properties { + case .requiredProperties: + break + case .requiredAndOptionalProperties: + newSuite.description = "Test Add: Suite Description" + } + + return newSuite + } + + func updatePlanEntryRuns() -> UpdatePlanEntryRuns { + return UpdatePlanEntryRuns(assignedtoId: testProject.user.id, + caseIds: [testProject.cases[objectCount - 1].id], + description: "Test Update: Plan.Entry Run Description", + includeAll: false) + } + +} + +// MARK: - Object Tests + +extension ObjectAPITests { + + // MARK: Case + + func testAddCase() { + + var newCase1 = newCase(with: .requiredProperties) + newCase1.title += ": \(#function)" + assertAddCase(newCase1, to: testProject.sections[0]) + + var newCase2 = newCase(with: .requiredProperties) + newCase2.title += ": \(#function)" + assertAddCase(newCase(with: .requiredAndOptionalProperties), to: testProject.sections[0]) + } + + func testDeleteCase() { + + continueAfterFailure = false + var newCase = self.newCase(with: .requiredAndOptionalProperties) + newCase.title += ": \(#function)" + guard let `case` = assertAddCase(newCase, to: testProject.sections[0]) else { return } + continueAfterFailure = true + + assertDeleteCase(`case`) + } + + func testGetCase() { + + continueAfterFailure = false + var newCase = self.newCase(with: .requiredAndOptionalProperties) + newCase.title += ": \(#function)" + guard let `case` = assertAddCase(newCase, to: testProject.sections[0]) else { return } + continueAfterFailure = true + + assertGetCase(`case`.id) + } + + func testGetCases() { + + continueAfterFailure = false + var newCase1 = newCase(with: .requiredProperties) + var newCase2 = newCase(with: .requiredAndOptionalProperties) + newCase1.title += ": \(#function)" + newCase2.title += ": \(#function)" + guard let case1 = assertAddCase(newCase1, to: testProject.sections[0]) else { return } + guard let case2 = assertAddCase(newCase2, to: testProject.sections[0]) else { return } + let addedCases = [case1, case2] + continueAfterFailure = true + + // Unfiltered + + if let cases = assertGetCases(in: testProject.project, in: testProject.suites[0], in: testProject.sections[0], filteredBy: nil) { + XCTAssertGreaterThanOrEqual(cases.count, addedCases.count) + for addedCase in addedCases { + XCTAssertEqual(cases.filter({ $0.id == addedCase.id }).count, 1, "Added Case \(addedCase.id) was not returned when getting all cases: \(cases)") + } + } + + // Filtered + + let priorityIds = [1, 2] + let filters = [Filter(named: "priority_id", matching: priorityIds)] + + if let cases = assertGetCases(in: testProject.project, in: testProject.suites[0], in: testProject.sections[0], filteredBy: filters) { + for `case` in cases { + XCTAssertEqual(priorityIds.filter({ $0 == `case`.priorityId }).count, 1, "Case \(`case`.id) did not match filter for priorityIds: \(priorityIds)") + } + } + } + + func testUpdateCase() { + + continueAfterFailure = false + var newCase = self.newCase(with: .requiredAndOptionalProperties) + newCase.title += ": \(#function)" + guard var `case` = assertAddCase(newCase, to: testProject.sections[0]) else { return } + continueAfterFailure = true + + `case`.estimate = "10m" + `case`.milestoneId = nil // Marked as inactive for the project so unable to update. + `case`.priorityId = 2 + `case`.refs = "RF-1001, RF-1002" + `case`.templateId = testProject.templates[1].id + `case`.title = "Test Update: Case Title: \(#function)" + `case`.typeId = 2 + + assertUpdateCase(`case`) + } + + // MARK: CaseField + + func testGetCaseFields() { + assertGetCaseFields() + } + + // MARK: CaseType + + func testGetCaseTypes() { + assertGetCaseTypes() + } + + // MARK: Configuration + + func testAddConfiguration() { + var newConfiguration = self.newConfiguration() + newConfiguration.name += ": \(#function)" + assertAddConfiguration(newConfiguration, to: testProject.configurationGroups[0]) + } + + func testDeleteConfiguration() { + + continueAfterFailure = false + var newConfiguration = self.newConfiguration() + newConfiguration.name += ": \(#function)" + guard let configuration = assertAddConfiguration(newConfiguration, to: testProject.configurationGroups[0]) else { return } + continueAfterFailure = true + + assertDeleteConfiguration(configuration) + } + + func testUpdateConfiguration() { + + continueAfterFailure = false + var newConfiguration = self.newConfiguration() + newConfiguration.name += ": \(#function)" + guard var configuration = assertAddConfiguration(newConfiguration, to: testProject.configurationGroups[0]) else { return } + continueAfterFailure = true + + configuration.name = "Test Update: Configuration Name: \(#function)" + + assertUpdateConfiguration(configuration) + } + + // MARK: ConfigurationGroup + + func testAddConfigurationGroup() { + var newConfigurationGroup = self.newConfigurationGroup() + newConfigurationGroup.name += ": \(#function)" + assertAddConfigurationGroup(newConfigurationGroup, to: testProject.project) + } + + func testDeleteConfigurationGroup() { + + continueAfterFailure = false + var newConfigurationGroup = self.newConfigurationGroup() + newConfigurationGroup.name += ": \(#function)" + guard let configurationGroup = assertAddConfigurationGroup(newConfigurationGroup, to: testProject.project) else { return } + continueAfterFailure = true + + assertDeleteConfigurationGroup(configurationGroup) + } + + func testGetConfigurationGroups() { + if let configurationGroups = assertGetConfigurationGroups() { + for configurationGroup in testProject.configurationGroups { + XCTAssertEqual(configurationGroups.filter({ $0.id == configurationGroup.id }).count, 1, "ConfigurationGroup \(configurationGroup.id) was not returned when getting all configuration group's in all projects: \(configurationGroups)") + } + } + } + + func testGetConfigurationGroupsInProject() { + if let configurationGroups = assertGetConfigurationGroups(in: testProject.project) { + for configurationGroup in testProject.configurationGroups { + XCTAssertEqual(configurationGroups.filter({ $0.id == configurationGroup.id }).count, 1, "ConfigurationGroup \(configurationGroup.id) was not returned when getting all configuration group's in project \(testProject.project.id): \(configurationGroups)") + } + } + } + + func testUpdateConfigurationGroup() { + + continueAfterFailure = false + var newConfigurationGroup = self.newConfigurationGroup() + newConfigurationGroup.name += ": \(#function)" + guard var configurationGroup = assertAddConfigurationGroup(newConfigurationGroup, to: testProject.project) else { return } + continueAfterFailure = true + + configurationGroup.name = "Test Update: ConfigurationGroup Name" + + assertUpdateConfigurationGroup(configurationGroup) + } + + // MARK: Milestone + + func testAddMilestone() { + var newMilestone1 = newMilestone(with: .requiredProperties) + var newMilestone2 = newMilestone(with: .requiredAndOptionalProperties) + newMilestone1.name += ": \(#function)" + newMilestone2.name += ": \(#function)" + assertAddMilestone(newMilestone1, to: testProject.project) + assertAddMilestone(newMilestone2, to: testProject.project) + } + + func testDeleteMilestone() { + + continueAfterFailure = false + var newMilestone = self.newMilestone(with: .requiredAndOptionalProperties) + newMilestone.name += ": \(#function)" + guard let milestone = assertAddMilestone(newMilestone, to: testProject.project) else { return } + continueAfterFailure = true + + assertDeleteMilestone(milestone) + } + + func testGetMilestone() { + assertGetMilestone(testProject.milestones[0].id) + } + + func testGetMilestones() { + + // Unfiltered + + if let milestones = assertGetMilestones(in: testProject.project, filteredBy: nil) { + XCTAssertGreaterThanOrEqual(milestones.count, testProject.milestones.count) + for milestone in testProject.milestones { + XCTAssertEqual(milestones.filter({ $0.id == milestone.id }).count, 1, "Milestone \(milestone.id) was not returned when getting all milestones: \(milestones)") + } + } + + // Filtered + + let filters = [Filter(named: "is_completed", matching: false)] + + if let milestones = assertGetMilestones(in: testProject.project, filteredBy: filters) { + for milestone in milestones { + XCTAssertEqual(milestone.isCompleted, false) + } + } + } + + func testUpdateMilestone() { + + continueAfterFailure = false + var newMilestone1 = newMilestone(with: .requiredProperties) + var newMilestone2 = newMilestone(with: .requiredProperties) + var newMilestone3 = newMilestone(with: .requiredProperties) + newMilestone1.name += ": \(#function)" + newMilestone2.name += ": \(#function)" + newMilestone3.name += ": \(#function)" + guard var milestone1 = assertAddMilestone(newMilestone1, to: testProject.project) else { return } + guard var milestone2 = assertAddMilestone(newMilestone2, to: testProject.project) else { return } + guard var milestone3 = assertAddMilestone(newMilestone3, to: testProject.project) else { return } + continueAfterFailure = true + + /* + - A Milestone that isStarted cannot have a startOn date. + - A Milestone with a startOn data cannot have isStarted set to true. + + If those rules are not followed TestRail will return a 400 error: + + "Milestone start date given but not marked as scheduled/upcoming." + */ + + // Not Started + + let date1 = Date() + milestone1.description = "Test Update: Milestone Description 1" + milestone1.dueOn = Date(timeInterval: 1000, since: date1) + milestone1.isCompleted = false + milestone1.isStarted = false // Must be false since startOn is not nil. + milestone1.name = "Test Update: Milestone Name 1: \(#function)" + milestone1.parentId = testProject.milestones[0].id + milestone1.startOn = date1 + + assertUpdateMilestone(milestone1) + + // Started + + let date2 = Date() + milestone2.description = "Test Update: Milestone Description 2" + milestone2.dueOn = Date(timeInterval: 1000, since: date2) + milestone2.isCompleted = false + milestone2.isStarted = true + milestone2.name = "Test Update: Milestone Name 2: \(#function)" + milestone2.parentId = testProject.milestones[0].id + milestone2.startOn = nil // Must be nil since isStarted is true. + + assertUpdateMilestone(milestone2) + + // Completed + + let date3 = Date() + milestone3.description = "Test Update: Milestone Description 3" + milestone3.dueOn = Date(timeInterval: 1000, since: date3) + milestone3.isCompleted = true + milestone3.isStarted = true + milestone3.name = "Test Update: Milestone Name 3: \(#function)" + milestone3.parentId = testProject.milestones[0].id + milestone3.startOn = nil + + assertUpdateMilestone(milestone3) + } + + // MARK: Plan + + func testAddPlan() { + + var newPlan1 = self.newPlan(with: .requiredProperties) + newPlan1.name += ": \(#function)" + assertAddPlan(newPlan1, to: testProject.project) + + var newPlan2 = self.newPlan(with: .requiredAndOptionalProperties) + newPlan2.name += ": \(#function)" + assertAddPlan(newPlan2, to: testProject.project) + } + + func testClosePlan() { + + continueAfterFailure = false + var newPlan = self.newPlan(with: .requiredAndOptionalProperties) + newPlan.name += ": \(#function)" + guard let plan = assertAddPlan(newPlan, to: testProject.project) else { return } + continueAfterFailure = true + + assertClosePlan(plan) + } + + func testDeletePlan() { + + continueAfterFailure = false + var newPlan = self.newPlan(with: .requiredAndOptionalProperties) + newPlan.name += ": \(#function)" + guard let plan = assertAddPlan(newPlan, to: testProject.project) else { return } + continueAfterFailure = true + + assertDeletePlan(plan) + } + + func testGetPlan() { + assertGetPlan(testProject.plans[0].id) + } + + func testGetPlans() { + + // Unfiltered + + if let plans = assertGetPlans(in: testProject.project, filteredBy: nil) { + for plan in testProject.plans { + XCTAssertEqual(plans.filter({ $0.id == plan.id }).count, 1, "Plan \(plan.id) was not returned when getting all plans: \(plans)") + } + } + + // Filtered + + // The limit/offset filters can be combined to paginate a request. + let limit = 1 + let filters = [Filter(named: "limit", matching: limit), + Filter(named: "offset", matching: 1)] + + if let plans = assertGetPlans(in: testProject.project, filteredBy: filters) { + XCTAssertLessThanOrEqual(plans.count, limit) + } + } + + func testUpdatePlan() { + + continueAfterFailure = false + var newPlan = NewPlan(description: "Test Add: Plan Description", entries: nil, milestoneId: nil, name: "Test Add: Plan Name") + newPlan.name += ": \(#function)" + guard var plan = assertAddPlan(newPlan, to: testProject.project) else { return } + continueAfterFailure = true + + plan.description = "Test Update: Plan Description" + plan.milestoneId = testProject.milestones[0].id + plan.name = "Test Update: Plan Name: \(#function)" + + assertUpdatePlan(plan) + } + + // MARK: Plan.Entry + + func testAddPlanEntry() { + var newPlanEntry1 = newPlanEntry(with: .requiredProperties) + var newPlanEntry2 = newPlanEntry(with: .requiredAndOptionalProperties) + newPlanEntry1.name? += ": \(#function)" + newPlanEntry2.name? += ": \(#function)" + assertAddPlanEntry(newPlanEntry1, to: testProject.plans[0]) + assertAddPlanEntry(newPlanEntry2, to: testProject.plans[1]) + } + + func testDeletePlanEntry() { + + continueAfterFailure = false + let plan = testProject.plans[0] + var newPlanEntry = self.newPlanEntry(with: .requiredAndOptionalProperties) + newPlanEntry.name? += ": \(#function)" + guard let planEntry = assertAddPlanEntry(newPlanEntry, to: plan) else { return } + continueAfterFailure = true + + assertDeletePlanEntry(planEntry, from: plan) + } + + func testUpdatePlanEntry() { + + continueAfterFailure = false + let plan = testProject.plans[0] + var newPlanEntry1 = newPlanEntry(with: .requiredAndOptionalProperties) + var newPlanEntry2 = newPlanEntry(with: .requiredAndOptionalProperties) + newPlanEntry1.name? += ": \(#function)" + newPlanEntry2.name? += ": \(#function)" + guard let planEntry1 = assertAddPlanEntry(newPlanEntry1, to: plan) else { return } + guard let planEntry2 = assertAddPlanEntry(newPlanEntry2, to: plan) else { return } + continueAfterFailure = true + + // Plan.Entry Only + + assertUpdatePlanEntry(planEntry1, in: plan) + + // Plan.Entry and Runs + + // NOTE: This does not appear to change anything on TestRail even though + // the API call succeeds. + + var updatedRuns = updatePlanEntryRuns() + updatedRuns.description? += ": \(#function)" + + assertUpdatePlanEntry(planEntry2, in: plan, with: updatedRuns) + } + + // MARK: Priority + + func testGetPriorities() { + assertGetPriorities() + } + + // MARK: Project + + func testAddProject() { + + var newProject1 = newProject(with: .requiredProperties) + newProject1.name += ": \(#function)" + if let project = assertAddProject(newProject1) { + assertDeleteProject(project) + } + + var newProject2 = newProject(with: .requiredAndOptionalProperties) + newProject2.name += ": \(#function)" + if let project = assertAddProject(newProject2) { + assertDeleteProject(project) + } + } + + func testDeleteProject() { + + continueAfterFailure = false + var newProject = self.newProject(with: .requiredAndOptionalProperties) + newProject.name += ": \(#function)" + guard let project = assertAddProject(newProject) else { return } + continueAfterFailure = true + + assertDeleteProject(project) + } + + func testGetProject() { + assertGetProject(testProject.project.id) + } + + func testGetProjects() { + assertGetProjects() + } + + func testUpdateProject() { + + continueAfterFailure = false + var newProject = self.newProject(with: .requiredAndOptionalProperties) + newProject.name += ": \(#function)" + guard var project = assertAddProject(newProject) else { return } + continueAfterFailure = true + + defer { + continueAfterFailure = true + assertDeleteProject(project) // Cleanup when complete. + } + + project.announcement = "QuizTrainTests: Test Update: Project Annoucement" + project.isCompleted = true + project.name = "QuizTrainTests: Test Update: Project Name" + project.showAnnouncement = true + // Updating project.suiteMode does not appear to work, so that is ommitted from this test. + + assertUpdateProject(project) + } + + // MARK: Result + + func testAddResult() { + let newResult1 = newResult(with: .requiredProperties) + let newResult2 = newResult(with: .requiredAndOptionalProperties) + assertAddResult(newResult1, to: testProject.tests[0]) + assertAddResult(newResult2, to: testProject.tests[0]) + } + + func testAddResultForCase() { + let newResult1 = newResult(with: .requiredProperties) + let newResult2 = newResult(with: .requiredAndOptionalProperties) + assertAddResultForCase(newResult1, to: testProject.runs[0], to: testProject.cases[0]) + assertAddResultForCase(newResult2, to: testProject.runs[0], to: testProject.cases[0]) + } + + func testAddResults() { + + // AssignedtoId + + var newTestResults = self.newTestResults(with: .requiredProperties) + + newTestResults.results = newTestResults.results.map { + var result = $0 + result.assignedtoId = testProject.user.id + return result + } + + XCTAssertTrue(newTestResults.isValid) + assertAddResults(newTestResults, to: testProject.runs[0]) + + // Comment + + newTestResults = self.newTestResults(with: .requiredProperties) + + newTestResults.results = newTestResults.results.map { + var result = $0 + result.comment = "Test Add: Test Comment" + return result + } + + XCTAssertTrue(newTestResults.isValid) + assertAddResults(newTestResults, to: testProject.runs[0]) + + // StatusId + + newTestResults = self.newTestResults(with: .requiredProperties) + + newTestResults.results = newTestResults.results.map { + var result = $0 + result.statusId = 1 + return result + } + + XCTAssertTrue(newTestResults.isValid) + assertAddResults(newTestResults, to: testProject.runs[0]) + + // All of the above. + + newTestResults = self.newTestResults(with: .requiredAndOptionalProperties) + + XCTAssertTrue(newTestResults.isValid) + assertAddResults(newTestResults, to: testProject.runs[0]) + } + + func testAddResultsForCases() { + + // AssignedtoId + + var newCaseResults = self.newCaseResults(with: .requiredProperties) + + newCaseResults.results = newCaseResults.results.map { + var result = $0 + result.assignedtoId = testProject.user.id + return result + } + + XCTAssertTrue(newCaseResults.isValid) + assertAddResultsForCases(newCaseResults, to: testProject.runs[0]) + + // Comment + + newCaseResults = self.newCaseResults(with: .requiredProperties) + + newCaseResults.results = newCaseResults.results.map { + var result = $0 + result.comment = "Test Add: Test Comment" + return result + } + + XCTAssertTrue(newCaseResults.isValid) + assertAddResultsForCases(newCaseResults, to: testProject.runs[0]) + + // StatusId + + newCaseResults = self.newCaseResults(with: .requiredProperties) + + newCaseResults.results = newCaseResults.results.map { + var result = $0 + result.statusId = 1 + return result + } + + XCTAssertTrue(newCaseResults.isValid) + assertAddResultsForCases(newCaseResults, to: testProject.runs[0]) + + // All of the above. + + newCaseResults = self.newCaseResults(with: .requiredAndOptionalProperties) + + XCTAssertTrue(newCaseResults.isValid) + assertAddResultsForCases(newCaseResults, to: testProject.runs[0]) + } + + func testGetResultsForTest() { + assertGetResultsForTest(testProject.tests[0]) + } + + func testGetResultsForCase() { + + // Unfiltered + + assertGetResultsForCase(testProject.cases[0], in: testProject.runs[0], filteredBy: nil) + + // Filtered + + let statusId = 1 + let filters = [Filter(named: "status_id", matching: statusId)] + + if let results = assertGetResultsForCase(testProject.cases[0], in: testProject.runs[0], filteredBy: filters) { + for result in results { + XCTAssertEqual(result.statusId, statusId, "Result \(result.id) did not match filter for status_id: \(statusId)") + } + } + } + + func testGetResultsForRun() { + + // Unfiltered + + assertGetResultsForRun(testProject.runs[0], filteredBy: nil) + + // Filtered + + let statusId = 1 + let filters = [Filter(named: "status_id", matching: statusId)] + + if let results = assertGetResultsForRun(testProject.runs[0], filteredBy: filters) { + for result in results { + XCTAssertEqual(result.statusId, statusId, "Result \(result.id) did not match filter for status_id: \(statusId)") + } + } + } + + // MARK: ResultField + + func testGetResultFields() { + assertGetResultFields() + } + + // MARK: Run + + func testAddRun() { + var newRun1 = newRun(with: .requiredProperties) + var newRun2 = newRun(with: .requiredAndOptionalProperties) + newRun1.name += ": \(#function)" + newRun2.name += ": \(#function)" + assertAddRun(newRun1, to: testProject.project) + assertAddRun(newRun2, to: testProject.project) + } + + func testCloseRun() { + + continueAfterFailure = false + var newRun = self.newRun(with: .requiredAndOptionalProperties) + newRun.name += ": \(#function)" + guard let run = assertAddRun(newRun, to: testProject.project) else { return } + continueAfterFailure = true + + assertCloseRun(run) + } + + func testDeleteRun() { + + continueAfterFailure = false + var newRun = self.newRun(with: .requiredAndOptionalProperties) + newRun.name += ": \(#function)" + guard let run = assertAddRun(newRun, to: testProject.project) else { return } + continueAfterFailure = true + + assertDeleteRun(run) + } + + func testGetRun() { + assertGetRun(testProject.runs[0].id) + } + + func testGetRuns() { + + // Unfiltered + + assertGetRuns(in: testProject.project, filteredBy: nil) + + // Filtered + + let isCompleted = false + let filters = [Filter(named: "is_completed", matching: isCompleted)] + + if let runs = assertGetRuns(in: testProject.project, filteredBy: filters) { + for run in runs { + XCTAssertEqual(run.isCompleted, isCompleted) + } + } + } + + func testUpdateRun() { + + continueAfterFailure = false + var newRun = self.newRun(with: .requiredAndOptionalProperties) + newRun.name += ": \(#function)" + guard var run = assertAddRun(newRun, to: testProject.project) else { return } + continueAfterFailure = true + + run.description = "Test Update: Run Description" + run.includeAll = true + run.milestoneId = testProject.milestones[1].id + run.name = "Test Update: Run Name: \(#function)" + + assertUpdateRun(run) + } + + // MARK: Section + + func testAddSection() { + var newSection1 = self.newSection(with: .requiredProperties) + var newSection2 = self.newSection(with: .requiredAndOptionalProperties) + newSection1.name += ": \(#function)" + newSection2.name += ": \(#function)" + assertAddSection(newSection1, to: testProject.project) + assertAddSection(newSection2, to: testProject.project) + } + + func testDeleteSection() { + + continueAfterFailure = false + var newSection = self.newSection(with: .requiredAndOptionalProperties) + newSection.name += ": \(#function)" + guard let section = assertAddSection(newSection, to: testProject.project) else { return } + continueAfterFailure = true + + assertDeleteSection(section) + } + + func testGetSection() { + assertGetSection(testProject.sections[0].id) + } + + func testGetSections() { + if let sections = assertGetSections(in: testProject.project, in: testProject.suites[0]) { + for section in sections { + XCTAssertEqual(section.suiteId, testProject.suites[0].id) + } + } + } + + func testUpdateSection() { + + continueAfterFailure = false + var newSection = self.newSection(with: .requiredAndOptionalProperties) + newSection.name += ": \(#function)" + guard var section = assertAddSection(newSection, to: testProject.project) else { return } + continueAfterFailure = true + + section.description = "Section Description - Updated" + section.name = "Section Name - Updated: \(#function)" + + assertUpdateSection(section) + } + + // MARK: Status + + func testGetStatuses() { + assertGetStatuses() + } + + // MARK: Suite + + func testAddSuite() { + var newSuite1 = newSuite(with: .requiredProperties) + var newSuite2 = newSuite(with: .requiredAndOptionalProperties) + newSuite1.name += ": \(#function)" + newSuite2.name += ": \(#function)" + assertAddSuite(newSuite1, to: testProject.project) + assertAddSuite(newSuite2, to: testProject.project) + } + + func testDeleteSuite() { + + continueAfterFailure = false + var newSuite = self.newSuite(with: .requiredAndOptionalProperties) + newSuite.name += ": \(#function)" + guard let suite = assertAddSuite(newSuite, to: testProject.project) else { return } + continueAfterFailure = true + + assertDeleteSuite(suite) + } + + func testGetSuite() { + assertGetSuite(testProject.suites[0].id) + } + + func testGetSuites() { + if let suites = assertGetSuites(in: testProject.project) { + for suite in testProject.suites { + XCTAssertEqual(suites.filter({ $0.id == suite.id }).count, 1, "Suite \(suite.id) was not returned when getting all suite's: \(suites)") + } + } + } + + func testUpdateSuite() { + + continueAfterFailure = false + var newSuite = self.newSuite(with: .requiredAndOptionalProperties) + newSuite.name += ": \(#function)" + guard var suite = assertAddSuite(newSuite, to: testProject.project) else { return } + continueAfterFailure = true + + suite.description = "Test Update: Suite Description" + suite.name = "Test Update: Suite Name" + + assertUpdateSuite(suite) + } + + // MARK: Template + + func testGetTemplates() { + assertGetTemplates() + } + + func testGetTemplatesInProject() { + assertGetTemplates(in: testProject.project) + } + + // MARK: Test + + func testGetTest() { + assertGetTest(testProject.tests[0].id) + } + + func testGetTests() { + + // Unfiltered + + assertGetTests(in: testProject.runs[0], filteredBy: nil) + + // Filtered + + let statusId = 1 + let filters = [Filter(named: "status_id", matching: statusId)] + + if let tests = assertGetTests(in: testProject.runs[0], filteredBy: filters) { + for test in tests { + XCTAssertEqual(test.statusId, statusId) + } + } + } + + // MARK: User + + func testGetUser() { + + continueAfterFailure = false + guard let user = assertGetUsers()?.first else { return } + continueAfterFailure = true + + assertGetUser(user.id) + } + + func testGetUserByEmail() { + + continueAfterFailure = false + guard let user = assertGetUsers()?.first else { return } + continueAfterFailure = true + + assertGetUserByEmail(user.email) + } + + func testGetUsers() { + if let users = assertGetUsers() { + XCTAssertEqual(users.filter({ $0.email == objectAPI.api.username }).count, 1, "User \(objectAPI.api.username) was not returned when getting all users: \(users)") + } + } + +} + +// MARK: - Object Matching Tests + +extension ObjectAPITests { + + // MARK: Case + + func testGetCaseTypeMatchingId() { + + continueAfterFailure = false + guard let caseTypes = assertGetCaseTypes() else { return } + XCTAssertGreaterThan(caseTypes.count, 0, "This test cannot continue because there are no CaseType's.") + continueAfterFailure = true + + guard let randomCaseType = caseTypes.randomElement else { return } + + assertGetCaseTypeMatchingId(randomCaseType.id) + } + + // MARK: ConfigurationGroup + + func testGetConfigurationGroupMatchingId() { + + continueAfterFailure = false + XCTAssertGreaterThan(testProject.configurationGroups.count, 0, "This test cannot continue because there are no ConfigurationGroup's.") + continueAfterFailure = true + + guard let randomConfigurationGroup = testProject.configurationGroups.randomElement else { return } + + assertGetConfigurationGroupMatchingId(randomConfigurationGroup.id) + } + + // MARK: Priority + + func testGetPriorityMatchingId() { + + continueAfterFailure = false + guard let priorities = assertGetPriorities() else { return } + XCTAssertGreaterThan(priorities.count, 0, "This test cannot continue because there are no Priorities.") + continueAfterFailure = true + + guard let randomPriority = priorities.randomElement else { return } + + assertGetPriorityMatchingId(randomPriority.id) + } + + // MARK: Status + + func testGetStatusMatchingId() { + + continueAfterFailure = false + guard let statuses = assertGetStatuses() else { return } + XCTAssertGreaterThan(statuses.count, 0, "This test cannot continue because there are no Statuses.") + continueAfterFailure = true + + guard let randomStatus = statuses.randomElement else { return } + + assertGetStatusMatchingId(randomStatus.id) + } + + // MARK: Template + + func testGetTemplateMatchingId() { + + continueAfterFailure = false + XCTAssertGreaterThan(testProject.templates.count, 0, "This test cannot continue because there are no Templates.") + continueAfterFailure = true + + guard let randomTemplate = testProject.templates.randomElement else { return } + + assertGetTemplateMatchingId(randomTemplate.id) + } + + func testGetTemplatesMatchingIds() { + + continueAfterFailure = false + XCTAssertGreaterThan(testProject.templates.count, 0, "This test cannot continue because there are no Templates.") + continueAfterFailure = true + + // Pick up to 3 template ids randomly. + + var allTemplates = testProject.templates + var randomTemplates = [Template]() + + if allTemplates.count > 2 { + for _ in 0..<3 { + let randomIndex = Int(arc4random_uniform(UInt32(allTemplates.count))) + randomTemplates.append(allTemplates[randomIndex]) + allTemplates.remove(at: randomIndex) + } + } else { + randomTemplates.append(contentsOf: allTemplates) + } + + let randomTemplateIds = randomTemplates.flatMap({ $0.id }) + + assertGetTemplatesMatchingIds(randomTemplateIds) + } + +} + +// MARK: - Object Forward Relationship Tests + +extension ObjectAPITests { + + // MARK: Case + + func testGetCaseToCreatedByRelationship() { + + continueAfterFailure = false + XCTAssertGreaterThan(testProject.cases.count, 0, "This test cannot continue because there are no Cases.") + continueAfterFailure = true + + for `case` in testProject.cases { + assertGetCaseToCreatedByRelationship(`case`) + assertGetCaseToCreatedByRelationship(`case`, usingObjectToRelationshipMethod: true) + } + } + + func testGetCaseToMilestoneRelationship() { + + continueAfterFailure = false + XCTAssertGreaterThan(testProject.cases.count, 0, "This test cannot continue because there are no Cases.") + continueAfterFailure = true + + for `case` in testProject.cases { + assertGetCaseToMilestoneRelationship(`case`) + assertGetCaseToMilestoneRelationship(`case`, usingObjectToRelationshipMethod: true) + } + } + + func testGetCaseToPriorityRelationship() { + + continueAfterFailure = false + XCTAssertGreaterThan(testProject.cases.count, 0, "This test cannot continue because there are no Cases.") + continueAfterFailure = true + + for `case` in testProject.cases { + assertGetCaseToPriorityRelationship(`case`) + assertGetCaseToPriorityRelationship(`case`, usingObjectToRelationshipMethod: true) + } + } + + func testGetCaseToSectionRelationship() { + + continueAfterFailure = false + XCTAssertGreaterThan(testProject.cases.count, 0, "This test cannot continue because there are no Cases.") + continueAfterFailure = true + + for `case` in testProject.cases { + assertGetCaseToSectionRelationship(`case`) + assertGetCaseToSectionRelationship(`case`, usingObjectToRelationshipMethod: true) + } + } + + func testGetCaseToSuiteRelationship() { + + continueAfterFailure = false + XCTAssertGreaterThan(testProject.cases.count, 0, "This test cannot continue because there are no Cases.") + continueAfterFailure = true + + for `case` in testProject.cases { + assertGetCaseToSuiteRelationship(`case`) + assertGetCaseToSuiteRelationship(`case`, usingObjectToRelationshipMethod: true) + } + } + + func testGetCaseToTemplateRelationship() { + + continueAfterFailure = false + XCTAssertGreaterThan(testProject.cases.count, 0, "This test cannot continue because there are no Cases.") + continueAfterFailure = true + + for `case` in testProject.cases { + assertGetCaseToTemplateRelationship(`case`) + assertGetCaseToTemplateRelationship(`case`, usingObjectToRelationshipMethod: true) + } + } + + func testGetCaseToTypeRelationship() { + + continueAfterFailure = false + XCTAssertGreaterThan(testProject.cases.count, 0, "This test cannot continue because there are no Cases.") + continueAfterFailure = true + + for `case` in testProject.cases { + assertGetCaseToTypeRelationship(`case`) + assertGetCaseToTypeRelationship(`case`, usingObjectToRelationshipMethod: true) + } + } + + func testGetCaseToUpdatedByRelationship() { + + continueAfterFailure = false + XCTAssertGreaterThan(testProject.cases.count, 0, "This test cannot continue because there are no Cases.") + continueAfterFailure = true + + for `case` in testProject.cases { + assertGetCaseToUpdatedByRelationship(`case`) + assertGetCaseToUpdatedByRelationship(`case`, usingObjectToRelationshipMethod: true) + } + } + + // MARK: CaseField + + func testGetCaseFieldToTemplatesRelationship() { + + continueAfterFailure = false + XCTAssertGreaterThan(testProject.caseFields.count, 0, "This test cannot continue because there are no CaseFields.") + continueAfterFailure = true + + for caseField in testProject.caseFields { + assertGetCaseFieldToTemplatesRelationship(caseField) + assertGetCaseFieldToTemplatesRelationship(caseField, usingObjectToRelationshipMethod: true) + } + } + + // MARK: CaseField.Config + + func testGetCaseFieldConfigToAccessibleProjectsRelationship() { + + continueAfterFailure = false + XCTAssertGreaterThan(testProject.caseFields.count, 0, "This test cannot continue because there are no CaseFields.") + continueAfterFailure = true + + for caseField in testProject.caseFields { + for config in caseField.configs { + assertGetConfigToAccessibleProjectsRelationship(config) + assertGetConfigToAccessibleProjectsRelationship(config, usingObjectToRelationshipMethod: true) + } + } + } + + func testGetCaseFieldConfigToProjectsRelationship() { + + continueAfterFailure = false + XCTAssertGreaterThan(testProject.caseFields.count, 0, "This test cannot continue because there are no CaseFields.") + continueAfterFailure = true + + for caseField in testProject.caseFields { + for config in caseField.configs { + assertGetConfigToProjectsRelationship(config) + assertGetConfigToProjectsRelationship(config, usingObjectToRelationshipMethod: true) + } + } + } + + // MARK: Configuration + + func testGetConfigurationToConfigurationGroupRelationship() { + + continueAfterFailure = false + XCTAssertGreaterThan(testProject.configurations.count, 0, "This test cannot continue because there are no Configurations.") + continueAfterFailure = true + + for configuration in testProject.configurations { + assertGetConfigurationToConfigurationGroupRelationship(configuration) + assertGetConfigurationToConfigurationGroupRelationship(configuration, usingObjectToRelationshipMethod: true) + } + } + + // MARK: ConfigurationGroup + + func testGetConfigurationGroupToProjectRelationship() { + + continueAfterFailure = false + XCTAssertGreaterThan(testProject.configurationGroups.count, 0, "This test cannot continue because there are no ConfigurationGroups.") + continueAfterFailure = true + + for configurationGroup in testProject.configurationGroups { + assertGetConfigurationGroupToProjectRelationship(configurationGroup) + assertGetConfigurationGroupToProjectRelationship(configurationGroup, usingObjectToRelationshipMethod: true) + } + } + + // MARK: Milestone + + func testGetMilestoneToParentRelationship() { + + continueAfterFailure = false + XCTAssertGreaterThan(testProject.milestones.count, 0, "This test cannot continue because there are no Milestones.") + continueAfterFailure = true + + for milestone in testProject.milestones { + assertGetMilestoneToParentRelationship(milestone) + assertGetMilestoneToParentRelationship(milestone, usingObjectToRelationshipMethod: true) + } + } + + func testGetMilestoneToProjectRelationship() { + + continueAfterFailure = false + XCTAssertGreaterThan(testProject.milestones.count, 0, "This test cannot continue because there are no Milestones.") + continueAfterFailure = true + + for milestone in testProject.milestones { + assertGetMilestoneToProjectRelationship(milestone) + assertGetMilestoneToProjectRelationship(milestone, usingObjectToRelationshipMethod: true) + } + } + + // MARK: Plan + + func testGetPlanToAssignedtoRelationship() { + + continueAfterFailure = false + XCTAssertGreaterThan(testProject.plans.count, 0, "This test cannot continue because there are no Plans.") + continueAfterFailure = true + + for plan in testProject.plans { + assertGetPlanToAssignedtoRelationship(plan) + assertGetPlanToAssignedtoRelationship(plan, usingObjectToRelationshipMethod: true) + } + } + + func testGetPlanToCreatedByRelationship() { + + continueAfterFailure = false + XCTAssertGreaterThan(testProject.plans.count, 0, "This test cannot continue because there are no Plans.") + continueAfterFailure = true + + for plan in testProject.plans { + assertGetPlanToCreatedByRelationship(plan) + assertGetPlanToCreatedByRelationship(plan, usingObjectToRelationshipMethod: true) + } + } + + func testGetPlanToMilestoneRelationship() { + + continueAfterFailure = false + XCTAssertGreaterThan(testProject.plans.count, 0, "This test cannot continue because there are no Plans.") + continueAfterFailure = true + + for plan in testProject.plans { + assertGetPlanToMilestoneRelationship(plan) + assertGetPlanToMilestoneRelationship(plan, usingObjectToRelationshipMethod: true) + } + } + + func testGetPlanToProjectRelationship() { + + continueAfterFailure = false + XCTAssertGreaterThan(testProject.plans.count, 0, "This test cannot continue because there are no Plans.") + continueAfterFailure = true + + for plan in testProject.plans { + assertGetPlanToProjectRelationship(plan) + assertGetPlanToProjectRelationship(plan, usingObjectToRelationshipMethod: true) + } + } + + // MARK: Plan.Entry + + func testGetPlanEntryToSuiteRelationship() { + + continueAfterFailure = false + XCTAssertGreaterThan(testProject.plans.count, 0, "This test cannot continue because there are no Plans.") + continueAfterFailure = true + + for plan in testProject.plans { + if let planEntries = plan.entries { + for planEntry in planEntries { + assertGetPlanEntryToSuiteRelationship(planEntry) + assertGetPlanEntryToSuiteRelationship(planEntry, usingObjectToRelationshipMethod: true) + } + } + } + } + + // MARK: Result + + func testGetResultToAssignedtoRelationship() { + + continueAfterFailure = false + let newTestResults = self.newTestResults(with: .requiredAndOptionalProperties) + guard let results = assertAddResults(newTestResults, to: testProject.runs[0]) else { return } + continueAfterFailure = true + + for result in results { + assertGetResultToAssignedtoRelationship(result) + assertGetResultToAssignedtoRelationship(result, usingObjectToRelationshipMethod: true) + } + } + + func testGetResultToCreatedByRelationship() { + + continueAfterFailure = false + let newTestResults = self.newTestResults(with: .requiredAndOptionalProperties) + guard let results = assertAddResults(newTestResults, to: testProject.runs[0]) else { return } + continueAfterFailure = true + + for result in results { + assertGetResultToCreatedByRelationship(result) + assertGetResultToCreatedByRelationship(result, usingObjectToRelationshipMethod: true) + } + } + + func testGetResultToStatusRelationship() { + + continueAfterFailure = false + let newTestResults = self.newTestResults(with: .requiredAndOptionalProperties) + guard let results = assertAddResults(newTestResults, to: testProject.runs[0]) else { return } + continueAfterFailure = true + + for result in results { + assertGetResultToStatusRelationship(result) + assertGetResultToStatusRelationship(result, usingObjectToRelationshipMethod: true) + } + } + + func testGetResultToTestRelationship() { + + continueAfterFailure = false + let newTestResults = self.newTestResults(with: .requiredAndOptionalProperties) + guard let results = assertAddResults(newTestResults, to: testProject.runs[0]) else { return } + continueAfterFailure = true + + for result in results { + assertGetResultToTestRelationship(result) + assertGetResultToTestRelationship(result, usingObjectToRelationshipMethod: true) + } + } + + // MARK: ResultField + + func testGetResultFieldToTemplatesRelationship() { + + continueAfterFailure = false + XCTAssertGreaterThan(testProject.resultFields.count, 0, "This test cannot continue because there are no ResultFields.") + continueAfterFailure = true + + for resultField in testProject.resultFields { + assertGetResultFieldToTemplatesRelationship(resultField) + assertGetResultFieldToTemplatesRelationship(resultField, usingObjectToRelationshipMethod: true) + } + } + + // MARK: ResultField.Config + + func testGetResultFieldConfigToAccessibleProjectsRelationship() { + + continueAfterFailure = false + XCTAssertGreaterThan(testProject.resultFields.count, 0, "This test cannot continue because there are no CaseFields.") + continueAfterFailure = true + + for resultField in testProject.resultFields { + for config in resultField.configs { + assertGetConfigToAccessibleProjectsRelationship(config) + assertGetConfigToAccessibleProjectsRelationship(config, usingObjectToRelationshipMethod: true) + } + } + } + + func testGetResultFieldConfigToProjectsRelationship() { + + continueAfterFailure = false + XCTAssertGreaterThan(testProject.resultFields.count, 0, "This test cannot continue because there are no ResultFields.") + continueAfterFailure = true + + for resultField in testProject.resultFields { + for config in resultField.configs { + assertGetConfigToProjectsRelationship(config) + assertGetConfigToProjectsRelationship(config, usingObjectToRelationshipMethod: true) + } + } + } + + // MARK: Run + + func testGetRunToAssignedtoRelationship() { + + continueAfterFailure = false + XCTAssertGreaterThan(testProject.runs.count, 0, "This test cannot continue because there are no Runs.") + continueAfterFailure = true + + for run in testProject.runs { + assertGetRunToAssignedtoRelationship(run) + assertGetRunToAssignedtoRelationship(run, usingObjectToRelationshipMethod: true) + } + } + + func testGetRunToConfigurationsRelationship() { + + continueAfterFailure = false + XCTAssertGreaterThan(testProject.runs.count, 0, "This test cannot continue because there are no Runs.") + continueAfterFailure = true + + for run in testProject.runs { + assertGetRunToConfigurationsRelationship(run) + assertGetRunToConfigurationsRelationship(run, usingObjectToRelationshipMethod: true) + } + } + + func testGetRunToCreatedByRelationship() { + + continueAfterFailure = false + XCTAssertGreaterThan(testProject.runs.count, 0, "This test cannot continue because there are no Runs.") + continueAfterFailure = true + + for run in testProject.runs { + assertGetRunToCreatedByRelationship(run) + assertGetRunToCreatedByRelationship(run, usingObjectToRelationshipMethod: true) + } + } + + func testGetRunToMilestoneRelationship() { + + continueAfterFailure = false + XCTAssertGreaterThan(testProject.runs.count, 0, "This test cannot continue because there are no Runs.") + continueAfterFailure = true + + for run in testProject.runs { + assertGetRunToMilestoneRelationship(run) + assertGetRunToMilestoneRelationship(run, usingObjectToRelationshipMethod: true) + } + } + + func testGetRunToPlanRelationship() { + + continueAfterFailure = false + XCTAssertGreaterThan(testProject.runs.count, 0, "This test cannot continue because there are no Runs.") + continueAfterFailure = true + + for run in testProject.runs { + assertGetRunToPlanRelationship(run) + assertGetRunToPlanRelationship(run, usingObjectToRelationshipMethod: true) + } + } + + func testGetRunToProjectRelationship() { + + continueAfterFailure = false + XCTAssertGreaterThan(testProject.runs.count, 0, "This test cannot continue because there are no Runs.") + continueAfterFailure = true + + for run in testProject.runs { + assertGetRunToProjectRelationship(run) + assertGetRunToProjectRelationship(run, usingObjectToRelationshipMethod: true) + } + } + + func testGetRunToSuiteRelationship() { + + continueAfterFailure = false + XCTAssertGreaterThan(testProject.runs.count, 0, "This test cannot continue because there are no Runs.") + continueAfterFailure = true + + for run in testProject.runs { + assertGetRunToSuiteRelationship(run) + assertGetRunToSuiteRelationship(run, usingObjectToRelationshipMethod: true) + } + } + + // MARK: Section + + func testGetSectionToParentRelationship() { + + continueAfterFailure = false + XCTAssertGreaterThan(testProject.sections.count, 0, "This test cannot continue because there are no Sections.") + continueAfterFailure = true + + for section in testProject.sections { + assertGetSectionToParentRelationship(section) + assertGetSectionToParentRelationship(section, usingObjectToRelationshipMethod: true) + } + } + + func testGetSectionToSuiteRelationship() { + + continueAfterFailure = false + XCTAssertGreaterThan(testProject.sections.count, 0, "This test cannot continue because there are no Sections.") + continueAfterFailure = true + + for section in testProject.sections { + assertGetSectionToSuiteRelationship(section) + assertGetSectionToSuiteRelationship(section, usingObjectToRelationshipMethod: true) + } + } + + // MARK: Suite + + func testGetSuiteToProjectRelationship() { + + continueAfterFailure = false + XCTAssertGreaterThan(testProject.suites.count, 0, "This test cannot continue because there are no Suites.") + continueAfterFailure = true + + for suite in testProject.suites { + assertGetSuiteToProjectRelationship(suite) + assertGetSuiteToProjectRelationship(suite, usingObjectToRelationshipMethod: true) + } + } + + // MARK: Test + + func testGetTestToAssignedtoRelationship() { + + continueAfterFailure = false + XCTAssertGreaterThan(testProject.tests.count, 0, "This test cannot continue because there are no Tests.") + continueAfterFailure = true + + for test in testProject.tests { + assertGetTestToAssignedtoRelationship(test) + assertGetTestToAssignedtoRelationship(test, usingObjectToRelationshipMethod: true) + } + } + + func testGetTestToCaseRelationship() { + + continueAfterFailure = false + XCTAssertGreaterThan(testProject.tests.count, 0, "This test cannot continue because there are no Tests.") + continueAfterFailure = true + + for test in testProject.tests { + assertGetTestToCaseRelationship(test) + assertGetTestToCaseRelationship(test, usingObjectToRelationshipMethod: true) + } + } + + func testGetTestToMilestoneRelationship() { + + continueAfterFailure = false + XCTAssertGreaterThan(testProject.tests.count, 0, "This test cannot continue because there are no Tests.") + continueAfterFailure = true + + for test in testProject.tests { + assertGetTestToMilestoneRelationship(test) + assertGetTestToMilestoneRelationship(test, usingObjectToRelationshipMethod: true) + } + } + + func testGetTestToPriorityRelationship() { + + continueAfterFailure = false + XCTAssertGreaterThan(testProject.tests.count, 0, "This test cannot continue because there are no Tests.") + continueAfterFailure = true + + for test in testProject.tests { + assertGetTestToPriorityRelationship(test) + assertGetTestToPriorityRelationship(test, usingObjectToRelationshipMethod: true) + } + } + + func testGetTestToRunRelationship() { + + continueAfterFailure = false + XCTAssertGreaterThan(testProject.tests.count, 0, "This test cannot continue because there are no Tests.") + continueAfterFailure = true + + for test in testProject.tests { + assertGetTestToRunRelationship(test) + assertGetTestToRunRelationship(test, usingObjectToRelationshipMethod: true) + } + } + + func testGetTestToStatusRelationship() { + + continueAfterFailure = false + XCTAssertGreaterThan(testProject.tests.count, 0, "This test cannot continue because there are no Tests.") + continueAfterFailure = true + + for test in testProject.tests { + assertGetTestToStatusRelationship(test) + assertGetTestToStatusRelationship(test, usingObjectToRelationshipMethod: true) + } + } + + func testGetTestToTemplateRelationship() { + + continueAfterFailure = false + XCTAssertGreaterThan(testProject.tests.count, 0, "This test cannot continue because there are no Tests.") + continueAfterFailure = true + + for test in testProject.tests { + assertGetTestToTemplateRelationship(test) + assertGetTestToTemplateRelationship(test, usingObjectToRelationshipMethod: true) + } + } + + func testGetTestToTypeRelationship() { + + continueAfterFailure = false + XCTAssertGreaterThan(testProject.tests.count, 0, "This test cannot continue because there are no Tests.") + continueAfterFailure = true + + for test in testProject.tests { + assertGetTestToTypeRelationship(test) + assertGetTestToTypeRelationship(test, usingObjectToRelationshipMethod: true) + } + } + +} + +// MARK: - Assertions: Helpers + +extension ObjectAPITests { + + // MARK: Outcome + + func assertOutcomeSucceeded(_ outcome: Outcome) -> ObjectType? { + let object: ObjectType? + switch outcome { + case .failed(let error): + XCTFail(error.debugDescription) + object = nil + case .succeeded(let _object): + object = _object + } + return object + } + + func assertOutcomeSucceeded(_ outcome: Outcome) -> ObjectType? { + let object: ObjectType? + switch outcome { + case .failed(let error): + XCTFail(error.debugDescription) + object = nil + case .succeeded(let _object): + object = _object + } + return object + } + + // MARK: CustomFields + + /* + TestRail may add any omitted custom fields when creating a new object. Use + this to methods to assert only provided custom fields during an add + request. + */ + func assertCustomFieldKeyValuePairs(in lhs: CustomFields, existIn rhs: CustomFields) { + for (key, _) in lhs.customFields { + XCTAssertNotNil(rhs.customFields[key]) + } + } + +} + +// MARK: - Assertions: Objects + +extension ObjectAPITests { + + // MARK: Case + + @discardableResult func assertAddCase(_ newCase: NewCase, to section: Section) -> Case? { + + let expectation = XCTestExpectation(description: "Add Case") + + var `case`: Case? = nil + objectAPI.addCase(newCase, to: section) { (outcome) in + `case` = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(`case`) + + if let `case` = `case` { + + XCTAssertEqual(`case`.sectionId, section.id) + XCTAssertEqual(`case`.title, newCase.title) + + // TestRail may assign default values so only assert if nil was not passed in data. + if let value = newCase.estimate { XCTAssertEqual(value, `case`.estimate) } + if let value = newCase.milestoneId { XCTAssertEqual(value, `case`.milestoneId) } + if let value = newCase.priorityId { XCTAssertEqual(value, `case`.priorityId) } + if let value = newCase.refs { XCTAssertEqual(value, `case`.refs) } + if let value = newCase.templateId { XCTAssertEqual(value, `case`.templateId) } + if let value = newCase.typeId { XCTAssertEqual(value, `case`.typeId) } + + assertCustomFieldKeyValuePairs(in: newCase, existIn: `case`) + } + + return `case` + } + + func assertDeleteCase(_ case: Case) { + + let expectation = XCTestExpectation(description: "Delete Case") + + objectAPI.deleteCase(`case`) { (outcome) in + self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + } + + @discardableResult func assertGetCase(_ caseId: Int) -> Case? { + + let expectation = XCTestExpectation(description: "Get Case") + + var `case`: Case? = nil + objectAPI.getCase(caseId) { (outcome) in + `case` = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(`case`) + + if let `case` = `case` { + XCTAssertEqual(`case`.id, caseId) + } + + return `case` + } + + @discardableResult func assertGetCases(in project: Project, in suite: Suite? = nil, in section: Section? = nil, filteredBy filters: [Filter]? = nil) -> [Case]? { + + let expectation = XCTestExpectation(description: "Get Cases") + + var cases: [Case]? = nil + objectAPI.getCases(in: project, in: suite, in: section, filteredBy: filters) { (outcome) in + cases = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(cases) + + if let cases = cases { + for `case` in cases { + XCTAssertEqual(`case`.suiteId, suite?.id) + XCTAssertEqual(`case`.sectionId, section?.id) + } + } + + return cases + } + + @discardableResult func assertUpdateCase(_ case: Case) -> Case? { + + let expectation = XCTestExpectation(description: "Update Case") + + var updatedCase: Case? = nil + objectAPI.updateCase(`case`) { (outcome) in + updatedCase = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(updatedCase) + + if let updatedCase = updatedCase { + // Identity + XCTAssertEqual(updatedCase.id, `case`.id) + // Updates + XCTAssertEqual(updatedCase.estimate, `case`.estimate) + XCTAssertEqual(updatedCase.milestoneId, `case`.milestoneId) + XCTAssertEqual(updatedCase.priorityId, `case`.priorityId) + XCTAssertEqual(updatedCase.refs, `case`.refs) + XCTAssertEqual(updatedCase.templateId, `case`.templateId) + XCTAssertEqual(updatedCase.title, `case`.title) + XCTAssertEqual(updatedCase.typeId, `case`.typeId) + XCTAssertEqual(updatedCase.customFieldsContainer, `case`.customFieldsContainer) + } + + return updatedCase + } + + // MARK: CaseField + + @discardableResult func assertGetCaseFields() -> [CaseField]? { + + let expectation = XCTestExpectation(description: "Get Case Fields") + + var caseFields: [CaseField]? = nil + objectAPI.getCaseFields { (outcome) in + caseFields = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(caseFields) + + return caseFields + } + + // MARK: CaseType + + @discardableResult func assertGetCaseTypes() -> [CaseType]? { + + let expectation = XCTestExpectation(description: "Get Case Types") + + var caseTypes: [CaseType]? = nil + objectAPI.getCaseTypes { (outcome) in + caseTypes = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(caseTypes) + + return caseTypes + } + + // MARK: Configuration + + @discardableResult func assertAddConfiguration(_ newConfiguration: NewConfiguration, to configurationGroup: ConfigurationGroup) -> Configuration? { + + let expectation = XCTestExpectation(description: "Add Configuration") + + var configuration: Configuration? = nil + objectAPI.addConfiguration(newConfiguration, to: configurationGroup) { (outcome) in + configuration = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(configuration) + + if let configuration = configuration { + XCTAssertEqual(configuration.name, newConfiguration.name) + XCTAssertEqual(configuration.groupId, configurationGroup.id) + } + + return configuration + } + + func assertDeleteConfiguration(_ configuration: Configuration) { + + let expectation = XCTestExpectation(description: "Delete Configuration") + + objectAPI.deleteConfiguration(configuration) { (outcome) in + self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + } + + @discardableResult func assertUpdateConfiguration(_ configuration: Configuration) -> Configuration? { + + let expectation = XCTestExpectation(description: "Update Configuration") + + var updatedConfiguration: Configuration? = nil + objectAPI.updateConfiguration(configuration) { (outcome) in + updatedConfiguration = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(updatedConfiguration) + + if let updatedConfiguration = updatedConfiguration { + // Identity + XCTAssertEqual(updatedConfiguration.id, configuration.id) + // Updates + XCTAssertEqual(updatedConfiguration.name, configuration.name) + } + + return updatedConfiguration + } + + // MARK: ConfigurationGroup + + @discardableResult func assertAddConfigurationGroup(_ newConfigurationGroup: NewConfigurationGroup, to project: Project) -> ConfigurationGroup? { + + let expectation = XCTestExpectation(description: "Add Configuration Group") + + var configurationGroup: ConfigurationGroup? = nil + objectAPI.addConfigurationGroup(newConfigurationGroup, to: project) { (outcome) in + configurationGroup = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(configurationGroup) + + if let configurationGroup = configurationGroup { + XCTAssertEqual(configurationGroup.name, newConfigurationGroup.name) + XCTAssertEqual(configurationGroup.projectId, project.id) + } + + return configurationGroup + } + + func assertDeleteConfigurationGroup(_ configurationGroup: ConfigurationGroup) { + + let expectation = XCTestExpectation(description: "Delete Configuration Group") + + objectAPI.deleteConfigurationGroup(configurationGroup) { (outcome) in + self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + } + + @discardableResult func assertGetConfigurationGroups() -> [ConfigurationGroup]? { + + let expectation = XCTestExpectation(description: "Get Configuration Groups") + + var configurationGroups: [ConfigurationGroup]? = nil + objectAPI.getConfigurationGroups { (outcome) in + configurationGroups = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(configurationGroups) + + return configurationGroups + } + + @discardableResult func assertGetConfigurationGroups(in project: Project) -> [ConfigurationGroup]? { + + let expectation = XCTestExpectation(description: "Get Configuration Groups In Project") + + var configurationGroups: [ConfigurationGroup]? = nil + objectAPI.getConfigurationGroups(in: project) { (outcome) in + configurationGroups = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(configurationGroups) + + if let configurationGroups = configurationGroups { + for configurationGroup in configurationGroups { + XCTAssertEqual(configurationGroup.projectId, project.id) + } + } + + return configurationGroups + } + + @discardableResult func assertUpdateConfigurationGroup(_ configurationGroup: ConfigurationGroup) -> ConfigurationGroup? { + + let expectation = XCTestExpectation(description: "Update Configuration Group") + + var updatedConfigurationGroup: ConfigurationGroup? = nil + objectAPI.updateConfigurationGroup(configurationGroup) { (outcome) in + updatedConfigurationGroup = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(updatedConfigurationGroup) + + if let updatedConfigurationGroup = updatedConfigurationGroup { + // Identity + XCTAssertEqual(updatedConfigurationGroup.id, configurationGroup.id) + // Updates + XCTAssertEqual(updatedConfigurationGroup.name, configurationGroup.name) + } + + return updatedConfigurationGroup + } + + // MARK: Milestone + + @discardableResult func assertAddMilestone(_ newMilestone: NewMilestone, to project: Project) -> Milestone? { + + let expectation = XCTestExpectation(description: "Add Milestone") + + var milestone: Milestone? = nil + objectAPI.addMilestone(newMilestone, to: project) { (outcome) in + milestone = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(milestone) + + if let milestone = milestone { + + XCTAssertEqual(milestone.name, newMilestone.name) + XCTAssertEqual(milestone.projectId, project.id) + + // TestRail may assign default values so only assert if nil was not passed in data. + if let value = newMilestone.description { XCTAssertEqual(value, milestone.description) } + if let value = newMilestone.dueOn?.secondsSince1970 { XCTAssertEqual(value, milestone.dueOn?.secondsSince1970) } + if let value = newMilestone.parentId { XCTAssertEqual(value, milestone.parentId) } + if let value = newMilestone.startOn?.secondsSince1970 { XCTAssertEqual(value, milestone.startOn?.secondsSince1970) } + } + + return milestone + } + + func assertDeleteMilestone(_ milestone: Milestone) { + + let expectation = XCTestExpectation(description: "Delete Milestone") + + objectAPI.deleteMilestone(milestone) { (outcome) in + self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + } + + @discardableResult func assertGetMilestone(_ milestoneId: Int) -> Milestone? { + + let expectation = XCTestExpectation(description: "Get Milestone") + + var milestone: Milestone? = nil + objectAPI.getMilestone(milestoneId) { (outcome) in + milestone = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(milestone) + + if let milestone = milestone { + XCTAssertEqual(milestone.id, milestoneId) + } + + return milestone + } + + @discardableResult func assertGetMilestones(in project: Project, filteredBy filters: [Filter]? = nil) -> [Milestone]? { + + let expectation = XCTestExpectation(description: "Get Milestones") + + var milestones: [Milestone]? = nil + objectAPI.getMilestones(in: project, filteredBy: filters) { (outcome) in + milestones = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(milestones) + + if let milestones = milestones { + for milestone in milestones { + XCTAssertEqual(milestone.projectId, project.id) + } + } + + return milestones + } + + @discardableResult func assertUpdateMilestone(_ milestone: Milestone) -> Milestone? { + + let expectation = XCTestExpectation(description: "Update Milestone") + + var updatedMilestone: Milestone? = nil + objectAPI.updateMilestone(milestone) { (outcome) in + updatedMilestone = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(updatedMilestone) + + if let updatedMilestone = updatedMilestone { + // Identity + XCTAssertEqual(updatedMilestone.id, milestone.id) + // Updates + XCTAssertEqual(updatedMilestone.description, milestone.description) + XCTAssertEqual(updatedMilestone.dueOn?.secondsSince1970, milestone.dueOn?.secondsSince1970) + XCTAssertEqual(updatedMilestone.isCompleted, milestone.isCompleted) + XCTAssertEqual(updatedMilestone.isStarted, milestone.isStarted) + XCTAssertEqual(updatedMilestone.name, milestone.name) + XCTAssertEqual(updatedMilestone.parentId, milestone.parentId) + XCTAssertEqual(updatedMilestone.startOn?.secondsSince1970, milestone.startOn?.secondsSince1970) + } + + return updatedMilestone + } + + // MARK: Plan + + @discardableResult func assertAddPlan(_ newPlan: NewPlan, to project: Project) -> Plan? { + + let expectation = XCTestExpectation(description: "Add Plan") + + var plan: Plan? = nil + objectAPI.addPlan(newPlan, to: project) { (outcome) in + plan = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(plan) + + if let plan = plan { + + XCTAssertEqual(plan.name, newPlan.name) + XCTAssertEqual(plan.projectId, project.id) + + // TestRail may assign default values so only assert if nil was not passed in data. + if let value = newPlan.description { XCTAssertEqual(value, plan.description) } + if let value = newPlan.milestoneId { XCTAssertEqual(value, plan.milestoneId) } + if let value = newPlan.entries { XCTAssertEqual(value.count, plan.entries?.count) } + } + + return plan + } + + @discardableResult func assertClosePlan(_ plan: Plan) -> Plan? { + + let expectation = XCTestExpectation(description: "Close Plan") + + var closedPlan: Plan? = nil + objectAPI.closePlan(plan) { (outcome) in + closedPlan = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(closedPlan) + + if let closedPlan = closedPlan { + XCTAssertNotNil(closedPlan.completedOn) + XCTAssertEqual(closedPlan.isCompleted, true) + } + + return closedPlan + } + + func assertDeletePlan(_ plan: Plan) { + + let expectation = XCTestExpectation(description: "Delete Plan") + + objectAPI.deletePlan(plan) { (outcome) in + self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + } + + @discardableResult func assertGetPlan(_ planId: Int) -> Plan? { + + let expectation = XCTestExpectation(description: "Get Plan") + + var plan: Plan? = nil + objectAPI.getPlan(planId) { (outcome) in + plan = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(plan) + + if let plan = plan { + XCTAssertEqual(plan.id, planId) + } + + return plan + } + + @discardableResult func assertGetPlans(in project: Project, filteredBy filters: [Filter]? = nil) -> [Plan]? { + + let expectation = XCTestExpectation(description: "Get Plans") + + var plans: [Plan]? = nil + objectAPI.getPlans(in: project, filteredBy: filters) { (outcome) in + plans = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(plans) + + if let plans = plans { + for plan in plans { + XCTAssertEqual(plan.projectId, project.id) + } + } + + return plans + } + + @discardableResult func assertUpdatePlan(_ plan: Plan) -> Plan? { + + let expectation = XCTestExpectation(description: "Update Plan") + + var updatedPlan: Plan? = nil + objectAPI.updatePlan(plan) { (outcome) in + updatedPlan = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(updatedPlan) + + if let updatedPlan = updatedPlan { + // Identity + XCTAssertEqual(updatedPlan.id, plan.id) + // Updates + XCTAssertEqual(updatedPlan.description, plan.description) + XCTAssertEqual(updatedPlan.milestoneId, plan.milestoneId) + XCTAssertEqual(updatedPlan.name, plan.name) + } + + return updatedPlan + } + + // MARK: Plan.Entry + + @discardableResult func assertAddPlanEntry(_ newPlanEntry: NewPlan.Entry, to plan: Plan) -> Plan.Entry? { + + let expectation = XCTestExpectation(description: "Add Plan Entry") + + var planEntry: Plan.Entry? = nil + objectAPI.addPlanEntry(newPlanEntry, to: plan) { (outcome) in + planEntry = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(planEntry) + + if let planEntry = planEntry { + + XCTAssertEqual(planEntry.suiteId, newPlanEntry.suiteId) + + // TestRail may assign default values so only assert if nil was not passed in data. + if let value = newPlanEntry.name { XCTAssertEqual(value, planEntry.name) } + + if let newPlanEntryRuns = newPlanEntry.runs, newPlanEntryRuns.count > 0 { + XCTAssertEqual(planEntry.runs.count, newPlanEntryRuns.count) + } else { + // A default Run will be returned if newPlanEntry.runs was nil + // or empty. This Run will include all tests for the Suite + // matching the Suite for newPlanEntry.suiteId. + XCTAssertEqual(planEntry.runs.count, 1) + } + } + + return planEntry + } + + func assertDeletePlanEntry(_ planEntry: Plan.Entry, from plan: Plan) { + + let expectation = XCTestExpectation(description: "Delete Plan Entry") + + objectAPI.deletePlanEntry(planEntry, from: plan) { (outcome) in + self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + } + + @discardableResult func assertUpdatePlanEntry(_ planEntry: Plan.Entry, in plan: Plan, with planEntryRuns: UpdatePlanEntryRuns? = nil) -> Plan.Entry? { + + let expectation = XCTestExpectation(description: "Update Plan Entry") + + var updatedPlanEntry: Plan.Entry? = nil + objectAPI.updatePlanEntry(planEntry, in: plan, with: planEntryRuns) { (outcome) in + updatedPlanEntry = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(updatedPlanEntry) + + if let updatedPlanEntry = updatedPlanEntry { + + // Identity + XCTAssertEqual(updatedPlanEntry.id, planEntry.id) + + // Updates + XCTAssertEqual(updatedPlanEntry.name, planEntry.name) + } + + return updatedPlanEntry + } + + // MARK: Priority + + @discardableResult func assertGetPriorities() -> [Priority]? { + + let expectation = XCTestExpectation(description: "Get Priorities") + + var priorities: [Priority]? = nil + objectAPI.getPriorities { (outcome) in + priorities = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(priorities) + + return priorities + } + + // MARK: Project + + @discardableResult func assertAddProject(_ newProject: NewProject) -> Project? { + + let expectation = XCTestExpectation(description: "Add Project") + + var project: Project? = nil + objectAPI.addProject(newProject) { (outcome) in + project = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(project) + + if let project = project { + + XCTAssertEqual(project.name, newProject.name) + XCTAssertEqual(project.showAnnouncement, newProject.showAnnouncement) + XCTAssertEqual(newProject.suiteMode, newProject.suiteMode) + + // TestRail may assign default values so only assert if nil was not passed in data. + if let value = newProject.announcement { XCTAssertEqual(value, project.announcement) } + } + + return project + } + + func assertDeleteProject(_ project: Project) { + + let expectation = XCTestExpectation(description: "Test Delete Project") + + objectAPI.deleteProject(project) { (outcome) in + self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + } + + @discardableResult func assertGetProject(_ projectId: Int) -> Project? { + + let expectation = XCTestExpectation(description: "Get Project") + + var project: Project? = nil + objectAPI.getProject(projectId) { (outcome) in + project = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(project) + + if let project = project { + XCTAssertEqual(project.id, projectId) + } + + return project + } + + @discardableResult func assertGetProjects() -> [Project]? { + + let expectation = XCTestExpectation(description: "Get Projects") + + var projects: [Project]? = nil + objectAPI.getProjects { (outcome) in + projects = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(projects) + + return projects + } + + @discardableResult func assertUpdateProject(_ project: Project) -> Project? { + + let expectation = XCTestExpectation(description: "Update Projects") + + var updatedProject: Project? = nil + objectAPI.updateProject(project) { (outcome) in + updatedProject = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(updatedProject) + + if let updatedProject = updatedProject { + // Identity + XCTAssertEqual(updatedProject.id, project.id) + // Updates + XCTAssertEqual(updatedProject.announcement, project.announcement) + XCTAssertEqual(updatedProject.isCompleted, project.isCompleted) + XCTAssertEqual(updatedProject.name, project.name) + XCTAssertEqual(updatedProject.showAnnouncement, project.showAnnouncement) + XCTAssertEqual(updatedProject.suiteMode, project.suiteMode) + } + + return updatedProject + } + + // MARK: Result + + @discardableResult func assertAddResult(_ newResult: NewResult, to test: Test) -> Result? { + + let expectation = XCTestExpectation(description: "Add Result") + + var result: Result? = nil + objectAPI.addResult(newResult, to: test) { (outcome) in + result = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(result) + + if let result = result { + + XCTAssertEqual(result.statusId, newResult.statusId) + XCTAssertEqual(result.testId, test.id) + + // TestRail may assign default values so only assert if nil was not passed in data. + if let value = newResult.assignedtoId { XCTAssertEqual(value, result.assignedtoId) } + if let value = newResult.comment { XCTAssertEqual(value, result.comment) } + if let value = newResult.defects { XCTAssertEqual(value, result.defects) } + if let value = newResult.elapsed { XCTAssertEqual(value, result.elapsed) } + if let value = newResult.version { XCTAssertEqual(value, result.version) } + + assertCustomFieldKeyValuePairs(in: newResult, existIn: result) + } + + return result + } + + @discardableResult func assertAddResultForCase(_ newResult: NewResult, to run: Run, to case: Case) -> Result? { + + let expectation = XCTestExpectation(description: "Add Result For Case") + + var result: Result? = nil + objectAPI.addResultForCase(newResult, to: run, to: `case`) { (outcome) in + result = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(result) + + if let result = result { + + XCTAssertEqual(result.statusId, newResult.statusId) + + // TestRail may assign default values so only assert if nil was not passed in data. + if let value = newResult.assignedtoId { XCTAssertEqual(value, result.assignedtoId) } + if let value = newResult.comment { XCTAssertEqual(value, result.comment) } + if let value = newResult.defects { XCTAssertEqual(value, result.defects) } + if let value = newResult.elapsed { XCTAssertEqual(value, result.elapsed) } + if let value = newResult.version { XCTAssertEqual(value, result.version) } + + assertCustomFieldKeyValuePairs(in: newResult, existIn: result) + } + + return result + } + + @discardableResult func assertAddResults(_ newTestResults: NewTestResults, to run: Run) -> [Result]? { + + let expectation = XCTestExpectation(description: "Add Results") + + var results: [Result]? = nil + objectAPI.addResults(newTestResults, to: run) { (outcome) in + results = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(results) + + if let results = results { + XCTAssertEqual(results.count, newTestResults.results.count) + } + + return results + } + + @discardableResult func assertAddResultsForCases(_ newCaseResults: NewCaseResults, to run: Run) -> [Result]? { + + let expectation = XCTestExpectation(description: "Add Results For Cases") + + var results: [Result]? = nil + objectAPI.addResultsForCases(newCaseResults, to: run) { (outcome) in + results = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(results) + + if let results = results { + XCTAssertEqual(results.count, newCaseResults.results.count) + } + + return results + } + + @discardableResult func assertGetResultsForTest(_ test: Test, filteredBy filters: [Filter]? = nil) -> [Result]? { + + let expectation = XCTestExpectation(description: "Get Results For Test") + + var results: [Result]? = nil + objectAPI.getResultsForTest(test, filteredBy: filters) { (outcome) in + results = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(results) + + return results + } + + @discardableResult func assertGetResultsForCase(_ case: Case, in run: Run, filteredBy filters: [Filter]? = nil) -> [Result]? { + + let expectation = XCTestExpectation(description: "Get Results For Case") + + var results: [Result]? = nil + objectAPI.getResultsForCase(`case`, in: run, filteredBy: filters) { (outcome) in + results = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(results) + + return results + } + + @discardableResult func assertGetResultsForRun(_ run: Run, filteredBy filters: [Filter]? = nil) -> [Result]? { + + let expectation = XCTestExpectation(description: "Get Results For Run") + + var results: [Result]? = nil + objectAPI.getResultsForRun(run, filteredBy: filters) { (outcome) in + results = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(results) + + return results + } + + // MARK: ResultField + + @discardableResult func assertGetResultFields() -> [ResultField]? { + + let expectation = XCTestExpectation(description: "Get ResultFields") + + var resultFields: [ResultField]? = nil + objectAPI.getResultFields { (outcome) in + resultFields = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(resultFields) + + return resultFields + } + + // MARK: Run + + @discardableResult func assertAddRun(_ newRun: NewRun, to project: Project) -> Run? { + + let expectation = XCTestExpectation(description: "Add Run") + + var run: Run? = nil + objectAPI.addRun(newRun, to: project) { (outcome) in + run = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(run) + + if let run = run { + + XCTAssertEqual(run.includeAll, newRun.includeAll) + XCTAssertEqual(run.name, newRun.name) + XCTAssertEqual(run.projectId, project.id) + + // TestRail may assign default values so only assert if nil was not passed in data. + if let value = newRun.assignedtoId { XCTAssertEqual(value, run.assignedtoId) } + if let value = newRun.description { XCTAssertEqual(value, run.description) } + if let value = newRun.milestoneId { XCTAssertEqual(value, run.milestoneId) } + if let value = newRun.suiteId { XCTAssertEqual(value, run.suiteId) } + } + + return run + } + + @discardableResult func assertCloseRun(_ run: Run) -> Run? { + + let expectation = XCTestExpectation(description: "Close Run") + + var closedRun: Run? = nil + objectAPI.closeRun(run) { (outcome) in + closedRun = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(closedRun) + + if let closedRun = closedRun { + XCTAssertNotNil(closedRun.completedOn) + XCTAssertTrue(closedRun.isCompleted) + XCTAssertEqual(closedRun.id, run.id) + } + + return closedRun + } + + func assertDeleteRun(_ run: Run) { + + let expectation = XCTestExpectation(description: "Delete Run") + + objectAPI.deleteRun(run) { (outcome) in + self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + } + + @discardableResult func assertGetRun(_ runId: Int) -> Run? { + + let expectation = XCTestExpectation(description: "Get Run") + + var run: Run? = nil + objectAPI.getRun(runId) { (outcome) in + run = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(run) + + if let run = run { + XCTAssertEqual(run.id, runId) + } + + return run + } + + @discardableResult func assertGetRuns(in project: Project, filteredBy filters: [Filter]? = nil) -> [Run]? { + + let expectation = XCTestExpectation(description: "Get Runs") + + var runs: [Run]? = nil + objectAPI.getRuns(in: project, filteredBy: filters) { (outcome) in + runs = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(runs) + + if let runs = runs { + for run in runs { + XCTAssertEqual(run.projectId, project.id) + } + } + + return runs + } + + @discardableResult func assertUpdateRun(_ run: Run) -> Run? { + + let expectation = XCTestExpectation(description: "Update Run") + + var updatedRun: Run? = nil + objectAPI.updateRun(run) { (outcome) in + updatedRun = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(updatedRun) + + if let updatedRun = updatedRun { + // Identity + XCTAssertEqual(updatedRun.id, run.id) + // Updates + XCTAssertEqual(updatedRun.description, run.description) + XCTAssertEqual(updatedRun.includeAll, run.includeAll) + XCTAssertEqual(updatedRun.milestoneId, run.milestoneId) + XCTAssertEqual(updatedRun.name, run.name) + } + + return updatedRun + } + + // MARK: Section + + @discardableResult func assertAddSection(_ newSection: NewSection, to project: Project) -> Section? { + + let expectation = XCTestExpectation(description: "Add Section") + + var section: Section? = nil + objectAPI.addSection(newSection, to: project) { (outcome) in + section = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(section) + + if let section = section { + + XCTAssertEqual(section.name, newSection.name) + + // TestRail may assign default values so only assert if nil was not passed in data. + if let value = newSection.description { XCTAssertEqual(value, section.description) } + if let value = newSection.parentId { XCTAssertEqual(value, section.parentId) } + + if project.suiteMode == .singleSuite { + XCTAssertNil(section.suiteId) // Optional/ignored if project is running in single suite mode, otherwise required. + } else { + XCTAssertNotNil(section.suiteId) + if let value = newSection.suiteId { XCTAssertEqual(value, section.suiteId) } + } + } + + return section + } + + func assertDeleteSection(_ section: Section) { + + let expectation = XCTestExpectation(description: "Delete Section") + + objectAPI.deleteSection(section) { (outcome) in + self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + } + + @discardableResult func assertGetSection(_ sectionId: Int) -> Section? { + + let expectation = XCTestExpectation(description: "Get Section") + + var section: Section? = nil + objectAPI.getSection(sectionId) { (outcome) in + section = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(section) + + if let section = section { + XCTAssertEqual(section.id, sectionId) + } + + return section + } + + @discardableResult func assertGetSections(in project: Project, in suite: Suite? = nil) -> [Section]? { + + let expectation = XCTestExpectation(description: "Get Sections") + + var sections: [Section]? = nil + objectAPI.getSections(in: project, in: suite) { (outcome) in + sections = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(sections) + + if let sections = sections, let suite = suite { + for section in sections { + XCTAssertEqual(section.suiteId, suite.id) + } + } + + return sections + } + + @discardableResult func assertUpdateSection(_ section: Section) -> Section? { + + let expectation = XCTestExpectation(description: "Update Section") + + var updatedSection: Section? = nil + objectAPI.updateSection(section) { (outcome) in + updatedSection = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(updatedSection) + + if let updatedSection = updatedSection { + // Identity + XCTAssertEqual(updatedSection.id, section.id) + // Updates + XCTAssertEqual(updatedSection.description, section.description) + XCTAssertEqual(updatedSection.name, section.name) + } + + return updatedSection + } + + // MARK: Status + + @discardableResult func assertGetStatuses() -> [Status]? { + + let expectation = XCTestExpectation(description: "Get Statuses") + + var statuses: [Status]? = nil + objectAPI.getStatuses { (outcome) in + statuses = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(statuses) + + return statuses + } + + // MARK: Suite + + @discardableResult func assertAddSuite(_ newSuite: NewSuite, to project: Project) -> Suite? { + + let expectation = XCTestExpectation(description: "Add Suite") + + var suite: Suite? = nil + objectAPI.addSuite(newSuite, to: project) { (outcome) in + suite = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(suite) + + if let suite = suite { + + XCTAssertEqual(suite.name, newSuite.name) + XCTAssertEqual(suite.projectId, project.id) + + // TestRail may assign default values so only assert if nil was not passed in data. + if let value = newSuite.description { XCTAssertEqual(value, suite.description) } + } + + return suite + } + + func assertDeleteSuite(_ suite: Suite) { + + let expectation = XCTestExpectation(description: "Delete Suite") + + objectAPI.deleteSuite(suite) { (outcome) in + self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + } + + @discardableResult func assertGetSuite(_ suiteId: Int) -> Suite? { + + let expectation = XCTestExpectation(description: "Get Suite") + + var suite: Suite? = nil + objectAPI.getSuite(suiteId) { (outcome) in + suite = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(suite) + + if let suite = suite { + XCTAssertEqual(suite.id, suiteId) + } + + return suite + } + + @discardableResult func assertGetSuites(in project: Project) -> [Suite]? { + + let expectation = XCTestExpectation(description: "Get Suites") + + var suites: [Suite]? = nil + objectAPI.getSuites(in: project) { (outcome) in + suites = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(suites) + + if let suites = suites { + for suite in suites { + XCTAssertEqual(suite.projectId, project.id) + } + } + + return suites + } + + @discardableResult func assertUpdateSuite(_ suite: Suite) -> Suite? { + + let expectation = XCTestExpectation(description: "Update Suite") + + var updatedSuite: Suite? = nil + objectAPI.updateSuite(suite) { (outcome) in + updatedSuite = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(updatedSuite) + + if let updatedSuite = updatedSuite { + // Identity + XCTAssertEqual(updatedSuite.id, suite.id) + // Updates + XCTAssertEqual(updatedSuite.description, suite.description) + XCTAssertEqual(updatedSuite.name, suite.name) + } + + return updatedSuite + } + + // MARK: Template + + @discardableResult func assertGetTemplates() -> [Template]? { + + let expectation = XCTestExpectation(description: "Get Templates") + + var templates: [Template]? = nil + objectAPI.getTemplates { (outcome) in + templates = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(templates) + + return templates + } + + @discardableResult func assertGetTemplates(in project: Project) -> [Template]? { + + let expectation = XCTestExpectation(description: "Get Templates In Project") + + var templates: [Template]? = nil + objectAPI.getTemplates(in: project) { (outcome) in + templates = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(templates) + + return templates + } + + // MARK: Test + + @discardableResult func assertGetTest(_ testId: Int) -> Test? { + + let expectation = XCTestExpectation(description: "Get Test") + + var test: Test? = nil + objectAPI.getTest(testId) { (outcome) in + test = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(test) + + if let test = test { + XCTAssertEqual(test.id, testId) + } + + return test + } + + @discardableResult func assertGetTests(in run: Run, filteredBy filters: [Filter]? = nil) -> [Test]? { + + let expectation = XCTestExpectation(description: "Get Tests") + + var tests: [Test]? = nil + objectAPI.getTests(in: run, filteredBy: filters) { (outcome) in + tests = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(tests) + + if let tests = tests { + for test in tests { + XCTAssertEqual(test.runId, run.id) + } + } + + return tests + } + + // MARK: User + + @discardableResult func assertGetUser(_ userId: Int) -> User? { + + let expectation = XCTestExpectation(description: "Get User") + + var user: User? = nil + objectAPI.getUser(userId) { (outcome) in + user = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(user) + + if let user = user { + XCTAssertEqual(user.id, userId) + } + + return user + } + + @discardableResult func assertGetUserByEmail(_ email: String) -> User? { + + let expectation = XCTestExpectation(description: "Get User by Email") + + var user: User? = nil + objectAPI.getUserByEmail(email) { (outcome) in + user = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(user) + + if let user = user { + XCTAssertEqual(user.email, email) + } + + return user + } + + @discardableResult func assertGetUsers() -> [User]? { + + let expectation = XCTestExpectation(description: "Get Users") + + var users: [User]? = nil + objectAPI.getUsers { (outcome) in + users = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(users) + + if let users = users { + XCTAssertNotEqual(users.count, 0) + } + + return users + } + +} + +// MARK: - Assertions: Object Matching + +extension ObjectAPITests { + + // MARK: Case + + @discardableResult func assertGetCaseTypeMatchingId(_ id: Int) -> CaseType? { + + let expectation = XCTestExpectation(description: "Get CaseType Matching ID") + + var caseType: CaseType? + objectAPI.getCaseType(matching: id) { (outcome) in + caseType = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(caseType) + + if let caseType = caseType { + XCTAssertEqual(caseType.id, id) + } + + return caseType + } + + // MARK: ConfigurationGroup + + @discardableResult func assertGetConfigurationGroupMatchingId(_ id: Int) -> ConfigurationGroup? { + + let expectation = XCTestExpectation(description: "Get ConfigurationGroup Matching ID") + + var configurationGroup: ConfigurationGroup? + objectAPI.getConfigurationGroup(matching: id) { (outcome) in + configurationGroup = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(configurationGroup) + + if let configurationGroup = configurationGroup { + XCTAssertEqual(configurationGroup.id, id) + } + + return configurationGroup + } + + // MARK: Priority + + @discardableResult func assertGetPriorityMatchingId(_ id: Int) -> Priority? { + + let expectation = XCTestExpectation(description: "Get Priority Matching ID") + + var priority: Priority? + objectAPI.getPriority(matching: id) { (outcome) in + priority = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(priority) + + if let priority = priority { + XCTAssertEqual(priority.id, id) + } + + return priority + } + + // MARK: Status + + @discardableResult func assertGetStatusMatchingId(_ id: Int) -> Status? { + + let expectation = XCTestExpectation(description: "Get Status Matching ID") + + var status: Status? + objectAPI.getStatus(matching: id) { (outcome) in + status = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(status) + + if let status = status { + XCTAssertEqual(status.id, id) + } + + return status + } + + // MARK: Template + + @discardableResult func assertGetTemplateMatchingId(_ id: Int) -> Template? { + + let expectation = XCTestExpectation(description: "Get Template Matching ID") + + var template: Template? + objectAPI.getTemplate(matching: id) { (outcome) in + template = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(template) + + if let template = template { + XCTAssertEqual(template.id, id) + } + + return template + } + + @discardableResult func assertGetTemplatesMatchingIds(_ ids: [Int]) -> [Template]? { + + let expectation = XCTestExpectation(description: "Get Templates Matching IDs") + + var templates: [Template]? + objectAPI.getTemplates(matching: ids) { (outcome) in + templates = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(templates) + + if let templates = templates { + let uniqueIds = Set(ids) + XCTAssertEqual(uniqueIds.count, templates.count) + for id in uniqueIds { + XCTAssertEqual(templates.filter({ $0.id == id }).count, 1) + } + } + + return templates + } + +} + +// MARK: - Assertions: Object Forward Relationships + +extension ObjectAPITests { + + // MARK: Case + + @discardableResult func assertGetCaseToCreatedByRelationship(_ `case`: Case, usingObjectToRelationshipMethod: Bool = false) -> User? { + + let expectation = XCTestExpectation(description: "Get Case to CreatedBy (User) Relationship") + + var createdBy: User? + if usingObjectToRelationshipMethod { + `case`.createdBy(objectAPI) { (outcome) in + createdBy = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } else { + objectAPI.createdBy(`case`) { (outcome) in + createdBy = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(createdBy) + + if let createdBy = createdBy { + XCTAssertEqual(createdBy.id, `case`.createdBy) + } + + return createdBy + } + + @discardableResult func assertGetCaseToMilestoneRelationship(_ `case`: Case, usingObjectToRelationshipMethod: Bool = false) -> Milestone? { + + let expectation = XCTestExpectation(description: "Get Case to Milestone Relationship") + + var milestone: Milestone? + if usingObjectToRelationshipMethod { + `case`.milestone(objectAPI) { (outcome) in + milestone = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } else { + objectAPI.milestone(`case`) { (outcome) in + milestone = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + } + + wait(for: [expectation], timeout: timeout) + + if let milestoneId = `case`.milestoneId { + XCTAssertNotNil(milestone) + if let milestone = milestone { + XCTAssertEqual(milestone.id, milestoneId) + } + } else { + XCTAssertNil(milestone) + } + + return milestone + } + + @discardableResult func assertGetCaseToPriorityRelationship(_ `case`: Case, usingObjectToRelationshipMethod: Bool = false) -> Priority? { + + let expectation = XCTestExpectation(description: "Get Case to Priority Relationship") + + var priority: Priority? + if usingObjectToRelationshipMethod { + `case`.priority(objectAPI) { (outcome) in + priority = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } else { + objectAPI.priority(`case`) { (outcome) in + priority = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(priority) + + if let priority = priority { + XCTAssertEqual(priority.id, `case`.priorityId) + } + + return priority + } + + @discardableResult func assertGetCaseToSectionRelationship(_ `case`: Case, usingObjectToRelationshipMethod: Bool = false) -> Section? { + + let expectation = XCTestExpectation(description: "Get Case to Section Relationship") + + var section: Section? + if usingObjectToRelationshipMethod { + `case`.section(objectAPI) { (outcome) in + section = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } else { + objectAPI.section(`case`) { (outcome) in + section = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } + + wait(for: [expectation], timeout: timeout) + + if let sectionId = `case`.sectionId { + XCTAssertNotNil(section) + if let section = section { + XCTAssertEqual(section.id, sectionId) + } + } else { + XCTAssertNil(section) + } + + return section + } + + @discardableResult func assertGetCaseToSuiteRelationship(_ `case`: Case, usingObjectToRelationshipMethod: Bool = false) -> Suite? { + + let expectation = XCTestExpectation(description: "Get Case to Suite Relationship") + + var suite: Suite? + if usingObjectToRelationshipMethod { + `case`.suite(objectAPI) { (outcome) in + suite = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } else { + objectAPI.suite(`case`) { (outcome) in + suite = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } + + wait(for: [expectation], timeout: timeout) + + if let suiteId = `case`.suiteId { + XCTAssertNotNil(suite) + if let suite = suite { + XCTAssertEqual(suite.id, suiteId) + } + } else { + XCTAssertNil(suite) + } + + return suite + } + + @discardableResult func assertGetCaseToTemplateRelationship(_ `case`: Case, usingObjectToRelationshipMethod: Bool = false) -> Template? { + + let expectation = XCTestExpectation(description: "Get Case to Template Relationship") + + var template: Template? + if usingObjectToRelationshipMethod { + `case`.template(objectAPI) { (outcome) in + template = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } else { + objectAPI.template(`case`) { (outcome) in + template = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(template) + + if let template = template { + XCTAssertEqual(template.id, `case`.templateId) + } + + return template + } + + @discardableResult func assertGetCaseToTypeRelationship(_ `case`: Case, usingObjectToRelationshipMethod: Bool = false) -> CaseType? { + + let expectation = XCTestExpectation(description: "Get Case to Type (CaseType) Relationship") + + var type: CaseType? + if usingObjectToRelationshipMethod { + `case`.type(objectAPI) { (outcome) in + type = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } else { + objectAPI.type(`case`) { (outcome) in + type = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(type) + + if let type = type { + XCTAssertEqual(type.id, `case`.typeId) + } + + return type + } + + @discardableResult func assertGetCaseToUpdatedByRelationship(_ `case`: Case, usingObjectToRelationshipMethod: Bool = false) -> User? { + + let expectation = XCTestExpectation(description: "Get Case to UpdatedBy (User) Relationship") + + var updatedBy: User? + if usingObjectToRelationshipMethod { + `case`.updatedBy(objectAPI) { (outcome) in + updatedBy = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } else { + objectAPI.updatedBy(`case`) { (outcome) in + updatedBy = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(updatedBy) + + if let updatedBy = updatedBy { + XCTAssertEqual(updatedBy.id, `case`.updatedBy) + } + + return updatedBy + } + + // MARK: Config + + @discardableResult func assertGetConfigToAccessibleProjectsRelationship(_ config: Config, usingObjectToRelationshipMethod: Bool = false) -> [Project]? { + + let expectation = XCTestExpectation(description: "Get Config to Accessible Projects Relationship") + + var projects: [Project]? + if usingObjectToRelationshipMethod { + config.accessibleProjects(objectAPI) { (outcome) in + projects = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } else { + objectAPI.accessibleProjects(config) { (outcome) in + projects = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(projects) + + return projects + } + + /* + failOnMatchError true will cause a failure if any project returns a 403 + not authorized error. failOnMatchError false will not fail if 403's are + returned as long as there are no other non-403 errors. + + 403 errors might be unavoidable for some projects. For details see comments + in the ObjectAPI.projects(...) method called here. + */ + @discardableResult func assertGetConfigToProjectsRelationship(_ config: Config, usingObjectToRelationshipMethod: Bool = false, failOnMatchError: Bool = false) -> [Project]? { + + let expectation = XCTestExpectation(description: "Get Config to Projects Relationship") + + var _outcome: Outcome<[Project]?, ObjectAPI.MatchError, ErrorContainer>>? + if usingObjectToRelationshipMethod { + config.projects(objectAPI) { (outcome) in + _outcome = outcome + expectation.fulfill() + } + } else { + objectAPI.projects(config) { (outcome) in + _outcome = outcome + expectation.fulfill() + } + } + + wait(for: [expectation], timeout: timeout) + + // Sanity: should never fail. + guard let outcome = _outcome else { + XCTAssertNotNil(_outcome) + return nil + } + + // Unpack outcome. + var projects: [Project]? + switch outcome { + case .failed(let error): + switch error { + case .matchError(let matchError): + if failOnMatchError { + XCTFail(error.debugDescription) + } else { + print("\(#file):\(#line):\(#function) - WARNING: failOnMatchError is disabled. Partial matches will be returned: \(error.debugDescription)") + switch matchError { + case .noMatchesFound(_): + projects = [] + case .partialMatchesFound(let matches, _): + projects = matches + } + } + default: + XCTFail(error.debugDescription) + } + case .succeeded(let _projects): + projects = _projects + } + + XCTAssertNotNil(projects) + + return projects + } + + // MARK: CaseField + + @discardableResult func assertGetCaseFieldToTemplatesRelationship(_ caseField: CaseField, usingObjectToRelationshipMethod: Bool = false) -> [Template]? { + + let expectation = XCTestExpectation(description: "Get CaseField to Templates Relationship") + + var templates: [Template]? + if usingObjectToRelationshipMethod { + caseField.templates(objectAPI) { (outcome) in + templates = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } else { + objectAPI.templates(caseField) { (outcome) in + templates = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(templates) + + if let templates = templates { + XCTAssertEqual(templates.count, caseField.templateIds.count) + for id in caseField.templateIds { + XCTAssertEqual(templates.filter({ $0.id == id }).count, 1) + } + } + + return templates + } + + // MARK: Configuration + + @discardableResult func assertGetConfigurationToConfigurationGroupRelationship(_ configuration: Configuration, usingObjectToRelationshipMethod: Bool = false) -> ConfigurationGroup? { + + let expectation = XCTestExpectation(description: "Get Configuration to ConfigurationGroup Relationship") + + var configurationGroup: ConfigurationGroup? + if usingObjectToRelationshipMethod { + configuration.configurationGroup(objectAPI) { (outcome) in + configurationGroup = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } else { + objectAPI.configurationGroup(configuration) { (outcome) in + configurationGroup = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(configurationGroup) + + if let configurationGroup = configurationGroup { + XCTAssertEqual(configurationGroup.id, configuration.groupId) + } + + return configurationGroup + } + + // MARK: ConfigurationGroup + + @discardableResult func assertGetConfigurationGroupToProjectRelationship(_ configurationGroup: ConfigurationGroup, usingObjectToRelationshipMethod: Bool = false) -> Project? { + + let expectation = XCTestExpectation(description: "Get ConfigurationGroup to Project Relationship") + + var project: Project? + if usingObjectToRelationshipMethod { + configurationGroup.project(objectAPI) { (outcome) in + project = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } else { + objectAPI.project(configurationGroup) { (outcome) in + project = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(project) + + if let project = project { + XCTAssertEqual(project.id, configurationGroup.projectId) + } + + return project + } + + // MARK: Milestone + + @discardableResult func assertGetMilestoneToParentRelationship(_ milestone: Milestone, usingObjectToRelationshipMethod: Bool = false) -> Milestone? { + + let expectation = XCTestExpectation(description: "Get Milestone to Parent (Milestone) Relationship") + + var parent: Milestone? + if usingObjectToRelationshipMethod { + milestone.parent(objectAPI) { (outcome) in + parent = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } else { + objectAPI.parent(milestone) { (outcome) in + parent = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } + + wait(for: [expectation], timeout: timeout) + + if let parentId = milestone.parentId { + XCTAssertNotNil(parent) + if let parent = parent { + XCTAssertEqual(parent.id, parentId) + } + } else { + XCTAssertNil(parent) + } + + return parent + } + + @discardableResult func assertGetMilestoneToProjectRelationship(_ milestone: Milestone, usingObjectToRelationshipMethod: Bool = false) -> Project? { + + let expectation = XCTestExpectation(description: "Get Milestone to Project Relationship") + + var project: Project? + if usingObjectToRelationshipMethod { + milestone.project(objectAPI) { (outcome) in + project = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } else { + objectAPI.project(milestone) { (outcome) in + project = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(project) + + if let project = project { + XCTAssertEqual(project.id, milestone.projectId) + } + + return project + } + + // MARK: Plan + + @discardableResult func assertGetPlanToAssignedtoRelationship(_ plan: Plan, usingObjectToRelationshipMethod: Bool = false) -> User? { + + let expectation = XCTestExpectation(description: "Get Plan to Assignedto (User) Relationship") + + var assignedto: User? + if usingObjectToRelationshipMethod { + plan.assignedto(objectAPI) { (outcome) in + assignedto = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } else { + objectAPI.assignedto(plan) { (outcome) in + assignedto = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } + + wait(for: [expectation], timeout: timeout) + + if let assignedtoId = plan.assignedtoId { + XCTAssertNotNil(assignedto) + if let assignedto = assignedto { + XCTAssertEqual(assignedto.id, assignedtoId) + } + } else { + XCTAssertNil(assignedto) + } + + return assignedto + } + + @discardableResult func assertGetPlanToCreatedByRelationship(_ plan: Plan, usingObjectToRelationshipMethod: Bool = false) -> User? { + + let expectation = XCTestExpectation(description: "Get Plan to CreatedBy (User) Relationship") + + var createdBy: User? + if usingObjectToRelationshipMethod { + plan.createdBy(objectAPI) { (outcome) in + createdBy = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } else { + objectAPI.createdBy(plan) { (outcome) in + createdBy = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(createdBy) + + if let createdBy = createdBy { + XCTAssertEqual(createdBy.id, plan.createdBy) + } + + return createdBy + } + + @discardableResult func assertGetPlanToMilestoneRelationship(_ plan: Plan, usingObjectToRelationshipMethod: Bool = false) -> Milestone? { + + let expectation = XCTestExpectation(description: "Get Plan to Milestone Relationship") + + var milestone: Milestone? + if usingObjectToRelationshipMethod { + plan.milestone(objectAPI) { (outcome) in + milestone = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } else { + objectAPI.milestone(plan) { (outcome) in + milestone = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } + + wait(for: [expectation], timeout: timeout) + + if let milestoneId = plan.milestoneId { + XCTAssertNotNil(milestone) + if let milestone = milestone { + XCTAssertEqual(milestone.id, milestoneId) + } + } else { + XCTAssertNil(milestone) + } + + return milestone + } + + @discardableResult func assertGetPlanToProjectRelationship(_ plan: Plan, usingObjectToRelationshipMethod: Bool = false) -> Project? { + + let expectation = XCTestExpectation(description: "Get Plan to Project Relationship") + + var project: Project? + if usingObjectToRelationshipMethod { + plan.project(objectAPI) { (outcome) in + project = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } else { + objectAPI.project(plan) { (outcome) in + project = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(project) + + if let project = project { + XCTAssertEqual(project.id, plan.projectId) + } + + return project + } + + // MARK: Plan.Entry + + @discardableResult func assertGetPlanEntryToSuiteRelationship(_ planEntry: Plan.Entry, usingObjectToRelationshipMethod: Bool = false) -> Suite? { + + let expectation = XCTestExpectation(description: "Get Plan.Entry to Suite Relationship") + + var suite: Suite? + if usingObjectToRelationshipMethod { + planEntry.suite(objectAPI) { (outcome) in + suite = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } else { + objectAPI.suite(planEntry) { (outcome) in + suite = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(suite) + if let suite = suite { + XCTAssertEqual(suite.id, planEntry.suiteId) + } + + return suite + } + + // MARK: Result + + @discardableResult func assertGetResultToAssignedtoRelationship(_ result: Result, usingObjectToRelationshipMethod: Bool = false) -> User? { + + let expectation = XCTestExpectation(description: "Get Result to Assignedto (User) Relationship") + + var assignedto: User? + if usingObjectToRelationshipMethod { + result.assignedto(objectAPI) { (outcome) in + assignedto = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } else { + objectAPI.assignedto(result) { (outcome) in + assignedto = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } + + wait(for: [expectation], timeout: timeout) + + if let assignedtoId = result.assignedtoId { + XCTAssertNotNil(assignedto) + if let assignedto = assignedto { + XCTAssertEqual(assignedto.id, assignedtoId) + } + } else { + XCTAssertNil(assignedto) + } + + return assignedto + } + + @discardableResult func assertGetResultToCreatedByRelationship(_ result: Result, usingObjectToRelationshipMethod: Bool = false) -> User? { + + let expectation = XCTestExpectation(description: "Get Result to CreatedBy (User) Relationship") + + var createdBy: User? + if usingObjectToRelationshipMethod { + result.createdBy(objectAPI) { (outcome) in + createdBy = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } else { + objectAPI.createdBy(result) { (outcome) in + createdBy = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(createdBy) + + if let createdBy = createdBy { + XCTAssertEqual(createdBy.id, result.createdBy) + } + + return createdBy + } + + @discardableResult func assertGetResultToStatusRelationship(_ result: Result, usingObjectToRelationshipMethod: Bool = false) -> Status? { + + let expectation = XCTestExpectation(description: "Get Result to Status Relationship") + + var status: Status? + if usingObjectToRelationshipMethod { + result.status(objectAPI) { (outcome) in + status = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } else { + objectAPI.status(result) { (outcome) in + status = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } + + wait(for: [expectation], timeout: timeout) + + if let statusId = result.statusId { + XCTAssertNotNil(status) + if let status = status { + XCTAssertEqual(status.id, statusId) + } + } else { + XCTAssertNil(status) + } + + return status + } + + @discardableResult func assertGetResultToTestRelationship(_ result: Result, usingObjectToRelationshipMethod: Bool = false) -> Test? { + + let expectation = XCTestExpectation(description: "Get Result to Test Relationship") + + var test: Test? + if usingObjectToRelationshipMethod { + result.test(objectAPI) { (outcome) in + test = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } else { + objectAPI.test(result) { (outcome) in + test = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(test) + + if let test = test { + XCTAssertEqual(test.id, result.testId) + } + + return test + } + + // MARK: ResultField + + @discardableResult func assertGetResultFieldToTemplatesRelationship(_ resultField: ResultField, usingObjectToRelationshipMethod: Bool = false) -> [Template]? { + + let expectation = XCTestExpectation(description: "Get ResultField to Templates Relationship") + + var templates: [Template]? + if usingObjectToRelationshipMethod { + resultField.templates(objectAPI) { (outcome) in + templates = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } else { + objectAPI.templates(resultField) { (outcome) in + templates = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(templates) + + if let templates = templates { + XCTAssertEqual(templates.count, resultField.templateIds.count) + for templateId in resultField.templateIds { + XCTAssertEqual(templates.filter({ $0.id == templateId }).count, 1) + } + } + + return templates + } + + // MARK: Run + + @discardableResult func assertGetRunToAssignedtoRelationship(_ run: Run, usingObjectToRelationshipMethod: Bool = false) -> User? { + + let expectation = XCTestExpectation(description: "Get Run to Assignedto (User) Relationship") + + var assignedto: User? + if usingObjectToRelationshipMethod { + run.assignedto(objectAPI) { (outcome) in + assignedto = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } else { + objectAPI.assignedto(run) { (outcome) in + assignedto = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } + + wait(for: [expectation], timeout: timeout) + + if let assignedtoId = run.assignedtoId { + XCTAssertNotNil(assignedto) + if let assignedto = assignedto { + XCTAssertEqual(assignedto.id, assignedtoId) + } + } else { + XCTAssertNil(assignedto) + } + + return assignedto + } + + @discardableResult func assertGetRunToConfigurationsRelationship(_ run: Run, usingObjectToRelationshipMethod: Bool = false) -> [Configuration]? { + + let expectation = XCTestExpectation(description: "Get Run to Configurations Relationship") + + var configurations: [Configuration]? + if usingObjectToRelationshipMethod { + run.configurations(objectAPI) { (outcome) in + configurations = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } else { + objectAPI.configurations(run) { (outcome) in + configurations = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } + + wait(for: [expectation], timeout: timeout) + + if let runConfigIds = run.configIds { + XCTAssertNotNil(configurations) + if let configurations = configurations { + XCTAssertEqual(configurations.count, runConfigIds.count) + for id in runConfigIds { + XCTAssertEqual(configurations.filter({ $0.id == id }).count, 1) + } + } + } else { + XCTAssertNil(configurations) + } + + return configurations + } + + @discardableResult func assertGetRunToCreatedByRelationship(_ run: Run, usingObjectToRelationshipMethod: Bool = false) -> User? { + + let expectation = XCTestExpectation(description: "Get Run to CreatedBy (User) Relationship") + + var createdBy: User? + if usingObjectToRelationshipMethod { + run.createdBy(objectAPI) { (outcome) in + createdBy = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } else { + objectAPI.createdBy(run) { (outcome) in + createdBy = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(createdBy) + + if let createdBy = createdBy { + XCTAssertEqual(createdBy.id, run.createdBy) + } + + return createdBy + } + + @discardableResult func assertGetRunToMilestoneRelationship(_ run: Run, usingObjectToRelationshipMethod: Bool = false) -> Milestone? { + + let expectation = XCTestExpectation(description: "Get Run to Milestone Relationship") + + var milestone: Milestone? + if usingObjectToRelationshipMethod { + run.milestone(objectAPI) { (outcome) in + milestone = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } else { + objectAPI.milestone(run) { (outcome) in + milestone = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } + + wait(for: [expectation], timeout: timeout) + + if let milestoneId = run.milestoneId { + XCTAssertNotNil(milestone) + if let milestone = milestone { + XCTAssertEqual(milestone.id, milestoneId) + } + } else { + XCTAssertNil(milestone) + } + + return milestone + } + + @discardableResult func assertGetRunToPlanRelationship(_ run: Run, usingObjectToRelationshipMethod: Bool = false) -> Plan? { + + let expectation = XCTestExpectation(description: "Get Run to Plan Relationship") + + var plan: Plan? + if usingObjectToRelationshipMethod { + run.plan(objectAPI) { (outcome) in + plan = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } else { + objectAPI.plan(run) { (outcome) in + plan = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } + + wait(for: [expectation], timeout: timeout) + + if let planId = run.planId { + XCTAssertNotNil(plan) + if let plan = plan { + XCTAssertEqual(plan.id, planId) + } + } else { + XCTAssertNil(plan) + } + + return plan + } + + @discardableResult func assertGetRunToProjectRelationship(_ run: Run, usingObjectToRelationshipMethod: Bool = false) -> Project? { + + let expectation = XCTestExpectation(description: "Get Run to Project Relationship") + + var project: Project? + if usingObjectToRelationshipMethod { + run.project(objectAPI) { (outcome) in + project = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } else { + objectAPI.project(run) { (outcome) in + project = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(project) + + if let project = project { + XCTAssertEqual(project.id, run.projectId) + } + + return project + } + + @discardableResult func assertGetRunToSuiteRelationship(_ run: Run, usingObjectToRelationshipMethod: Bool = false) -> Suite? { + + let expectation = XCTestExpectation(description: "Get Run to Suite Relationship") + + var suite: Suite? + if usingObjectToRelationshipMethod { + run.suite(objectAPI) { (outcome) in + suite = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } else { + objectAPI.suite(run) { (outcome) in + suite = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } + + wait(for: [expectation], timeout: timeout) + + if let suiteId = run.suiteId { + XCTAssertNotNil(suite) + if let suite = suite { + XCTAssertEqual(suite.id, suiteId) + } + } else { + XCTAssertNil(suite) + } + + return suite + } + + // MARK: Section + + @discardableResult func assertGetSectionToParentRelationship(_ section: Section, usingObjectToRelationshipMethod: Bool = false) -> Section? { + + let expectation = XCTestExpectation(description: "Get Section to Parent (Section) Relationship") + + var parent: Section? + if usingObjectToRelationshipMethod { + section.parent(objectAPI) { (outcome) in + parent = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } else { + objectAPI.parent(section) { (outcome) in + parent = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } + + wait(for: [expectation], timeout: timeout) + + if let parentId = section.parentId { + XCTAssertNotNil(parent) + if let parent = parent { + XCTAssertEqual(parent.id, parentId) + } + } else { + XCTAssertNil(parent) + } + + return parent + } + + @discardableResult func assertGetSectionToSuiteRelationship(_ section: Section, usingObjectToRelationshipMethod: Bool = false) -> Suite? { + + let expectation = XCTestExpectation(description: "Get Section to Suite Relationship") + + var suite: Suite? + if usingObjectToRelationshipMethod { + section.suite(objectAPI) { (outcome) in + suite = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } else { + objectAPI.suite(section) { (outcome) in + suite = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } + + wait(for: [expectation], timeout: timeout) + + if let suiteId = section.suiteId { + XCTAssertNotNil(suite) + if let suite = suite { + XCTAssertEqual(suite.id, suiteId) + } + } else { + XCTAssertNil(suite) + } + + return suite + } + + // MARK: Suite + + @discardableResult func assertGetSuiteToProjectRelationship(_ suite: Suite, usingObjectToRelationshipMethod: Bool = false) -> Project? { + + let expectation = XCTestExpectation(description: "Get Suite to Project Relationship") + + var project: Project? + if usingObjectToRelationshipMethod { + suite.project(objectAPI) { (outcome) in + project = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } else { + objectAPI.project(suite) { (outcome) in + project = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(project) + + if let project = project { + XCTAssertEqual(project.id, suite.projectId) + } + + return project + } + + // MARK: Test + + @discardableResult func assertGetTestToAssignedtoRelationship(_ test: Test, usingObjectToRelationshipMethod: Bool = false) -> User? { + + let expectation = XCTestExpectation(description: "Get Test to Assignedto (User) Relationship") + + var assignedto: User? + if usingObjectToRelationshipMethod { + test.assignedto(objectAPI) { (outcome) in + assignedto = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } else { + objectAPI.assignedto(test) { (outcome) in + assignedto = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } + + wait(for: [expectation], timeout: timeout) + + if let assignedtoId = test.assignedtoId { + XCTAssertNotNil(assignedto) + if let assignedto = assignedto { + XCTAssertEqual(assignedto.id, assignedtoId) + } + } else { + XCTAssertNil(assignedto) + } + + return assignedto + } + + @discardableResult func assertGetTestToCaseRelationship(_ test: Test, usingObjectToRelationshipMethod: Bool = false) -> Case? { + + let expectation = XCTestExpectation(description: "Get Test to Case Relationship") + + var `case`: Case? + if usingObjectToRelationshipMethod { + test.`case`(objectAPI) { (outcome) in + `case` = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } else { + objectAPI.`case`(test) { (outcome) in + `case` = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(`case`) + + if let `case` = `case` { + XCTAssertEqual(`case`.id, test.caseId) + } + + return `case` + } + + @discardableResult func assertGetTestToMilestoneRelationship(_ test: Test, usingObjectToRelationshipMethod: Bool = false) -> Milestone? { + + let expectation = XCTestExpectation(description: "Get Test to Milestone Relationship") + + var milestone: Milestone? + if usingObjectToRelationshipMethod { + test.milestone(objectAPI) { (outcome) in + milestone = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } else { + objectAPI.milestone(test) { (outcome) in + milestone = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } + + wait(for: [expectation], timeout: timeout) + + if let milestoneId = test.milestoneId { + XCTAssertNotNil(milestone) + if let milestone = milestone { + XCTAssertEqual(milestone.id, milestoneId) + } + } else { + XCTAssertNil(milestone) + } + + return milestone + } + + @discardableResult func assertGetTestToPriorityRelationship(_ test: Test, usingObjectToRelationshipMethod: Bool = false) -> Priority? { + + let expectation = XCTestExpectation(description: "Get Test to Priority Relationship") + + var priority: Priority? + if usingObjectToRelationshipMethod { + test.priority(objectAPI) { (outcome) in + priority = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } else { + objectAPI.priority(test) { (outcome) in + priority = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(priority) + + if let priority = priority { + XCTAssertEqual(priority.id, test.priorityId) + } + + return priority + } + + @discardableResult func assertGetTestToRunRelationship(_ test: Test, usingObjectToRelationshipMethod: Bool = false) -> Run? { + + let expectation = XCTestExpectation(description: "Get Test to Run Relationship") + + var run: Run? + if usingObjectToRelationshipMethod { + test.run(objectAPI) { (outcome) in + run = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } else { + objectAPI.run(test) { (outcome) in + run = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(run) + + if let run = run { + XCTAssertEqual(run.id, test.runId) + } + + return run + } + + @discardableResult func assertGetTestToStatusRelationship(_ test: Test, usingObjectToRelationshipMethod: Bool = false) -> Status? { + + let expectation = XCTestExpectation(description: "Get Test to Status Relationship") + + var status: Status? + if usingObjectToRelationshipMethod { + test.status(objectAPI) { (outcome) in + status = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } else { + objectAPI.status(test) { (outcome) in + status = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(status) + if let status = status { + XCTAssertEqual(status.id, test.statusId) + } + + return status + } + + @discardableResult func assertGetTestToTemplateRelationship(_ test: Test, usingObjectToRelationshipMethod: Bool = false) -> Template? { + + let expectation = XCTestExpectation(description: "Get Test to Template Relationship") + + var template: Template? + if usingObjectToRelationshipMethod { + test.template(objectAPI) { (outcome) in + template = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } else { + objectAPI.template(test) { (outcome) in + template = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(template) + + if let template = template { + XCTAssertEqual(template.id, test.templateId) + } + + return template + } + + @discardableResult func assertGetTestToTypeRelationship(_ test: Test, usingObjectToRelationshipMethod: Bool = false) -> CaseType? { + + let expectation = XCTestExpectation(description: "Get Test to Type (CaseType) Relationship") + + var type: CaseType? + if usingObjectToRelationshipMethod { + test.type(objectAPI) { (outcome) in + type = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } else { + objectAPI.type(test) { (outcome) in + type = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(type) + + if let type = type { + XCTAssertEqual(type.id, test.typeId) + } + + return type + } + +} diff --git a/QuizTrainTests/README.md b/QuizTrainTests/README.md new file mode 100644 index 0000000..3a96581 --- /dev/null +++ b/QuizTrainTests/README.md @@ -0,0 +1,15 @@ +# QuizTrainTests 📝🚆✅ + +QuizTrainTests provides unit tests and systems tests against a real TestRail instance. It is advised that you backup your instance fully before running tests and verify that the backup is valid. For more details see comments and code in [ObjectAPITests.swift](Network/ObjectAPITests.swift). + +## Running Tests + +1. Update [`TestCredentials.json`](Testing%20Misc/TestCredentials.json) accordingly. + - *The `username` user must have permissions to create and delete projects.* +2. Select a scheme. + - *Apple does not support unit testing on watchOS.* +3. Select a target to run tests on. + - *It must have full network connectivity to your TestRail instance.* +4. In the Xcode menu: `Product -> Test` + +For [ObjectAPITests.swift](Network/ObjectAPITests.swift) to run you must set any custom **Case Fields** and **Result Fields** in your TestRail instance either as not-required, or required with a default value, for the duration of testing. Otherwise tests might not be able to setup necessary test projects since they do not know of required customizations specific to your instance. diff --git a/QuizTrainTests/Testing Misc/Array+Random.swift b/QuizTrainTests/Testing Misc/Array+Random.swift new file mode 100644 index 0000000..3bf7b5a --- /dev/null +++ b/QuizTrainTests/Testing Misc/Array+Random.swift @@ -0,0 +1,19 @@ +import Foundation + +extension Array { + + public var randomElement: Element? { + guard let index = randomIndex else { + return nil + } + return self[index] + } + + public var randomIndex: Int? { + guard count > 0 else { + return nil + } + return Int(arc4random_uniform(UInt32(count))) + } + +} diff --git a/QuizTrainTests/Testing Misc/TestCredentials.json b/QuizTrainTests/Testing Misc/TestCredentials.json new file mode 100644 index 0000000..1ed738d --- /dev/null +++ b/QuizTrainTests/Testing Misc/TestCredentials.json @@ -0,0 +1,7 @@ +{ + "hostname": "yourInstance.testrail.net", + "port": 443, + "scheme": "https", + "secret": "your_api_key_or_password", + "username": "your@testRailAccount.email" +} diff --git a/QuizTrainTests/Testing Misc/TestCredentials.swift b/QuizTrainTests/Testing Misc/TestCredentials.swift new file mode 100644 index 0000000..4e80e34 --- /dev/null +++ b/QuizTrainTests/Testing Misc/TestCredentials.swift @@ -0,0 +1,59 @@ +import Foundation + +/* + Represents credentials used in tests. Use the load() method to load data from + TestCredentials.json. + */ +final class TestCredentials: Codable { + let hostname: String // "yourinstance.testrail.net" + let port: Int // 443, 80, 8080, etc + let scheme: String // "https", "http" + let secret: String // Your TestRail API Key or Password + let username: String // "your@testrailAccount.email" +} + +extension TestCredentials { + + enum LoadError: Error { + case couldNotFindURLForResourceInBundle(resource: String, extension: String, bundle: Bundle) + case couldNotLoadDataFromURL(url: URL, error: Error) + case couldNotDecodeObjectFromJSONData(data: Data, error: Error) + } + + /* + Note the Bundle used for tests is different than Bundle.main. Main will not + be able to see anything inside the test target (e.g. TestCredentials.json). + Inside a test case load it like so: + + let testCredentials: TestCredentials + do { + let bundle = Bundle(for: type(of: self)) + testCredentials = try TestCredentials.load(from: bundle) + } catch { + // Handle TestCredentials.LoadError "error" + } + */ + static func load(from bundle: Bundle, resource: String = "TestCredentials", withExtension `extension`: String = "json") throws -> TestCredentials { + + guard let url = bundle.url(forResource: resource, withExtension: `extension`) else { + throw LoadError.couldNotFindURLForResourceInBundle(resource: resource, extension: `extension`, bundle: bundle) + } + + let data: Data + do { + data = try Data(contentsOf: url) + } catch { + throw LoadError.couldNotLoadDataFromURL(url: url, error: error) + } + + let testCredentials: TestCredentials + do { + testCredentials = try JSONDecoder().decode(TestCredentials.self, from: data) + } catch { + throw LoadError.couldNotDecodeObjectFromJSONData(data: data, error: error) + } + + return testCredentials + } + +} diff --git a/QuizTrainTests/Testing Protocols/Asserts/AssertAddRequestJSON.swift b/QuizTrainTests/Testing Protocols/Asserts/AssertAddRequestJSON.swift new file mode 100644 index 0000000..5a48fcb --- /dev/null +++ b/QuizTrainTests/Testing Protocols/Asserts/AssertAddRequestJSON.swift @@ -0,0 +1,18 @@ +import XCTest +@testable import QuizTrain + +protocol AssertAddRequestJSON { + func assertAddRequestJSON(_ object: Object) +} + +extension AssertAddRequestJSON { + + func assertAddRequestJSON(_ object: Object) { + let addRequestJSON = object.addRequestJSON + XCTAssertEqual(addRequestJSON.count, object.addRequestJSONKeys.count) + for key in object.addRequestJSONKeys { + XCTAssertNotNil(addRequestJSON[key]) + } + } + +} diff --git a/QuizTrainTests/Testing Protocols/Asserts/AssertCustomFields.swift b/QuizTrainTests/Testing Protocols/Asserts/AssertCustomFields.swift new file mode 100644 index 0000000..b4a0041 --- /dev/null +++ b/QuizTrainTests/Testing Protocols/Asserts/AssertCustomFields.swift @@ -0,0 +1,21 @@ +import XCTest +@testable import QuizTrain + +protocol AssertCustomFields { + func assertCustomFields(in object: Object, areEmpty: Bool) +} + +extension AssertCustomFields { + + func assertCustomFields(in object: Object, areEmpty: Bool) { + if areEmpty { + XCTAssertEqual(object.customFields.count, 0) + } else { + XCTAssertGreaterThan(object.customFields.count, 0) + for (key, _) in object.customFields { + XCTAssertTrue(key.hasPrefix("custom_")) + } + } + } + +} diff --git a/QuizTrainTests/Testing Protocols/Asserts/AssertEquatable.swift b/QuizTrainTests/Testing Protocols/Asserts/AssertEquatable.swift new file mode 100644 index 0000000..a51e78b --- /dev/null +++ b/QuizTrainTests/Testing Protocols/Asserts/AssertEquatable.swift @@ -0,0 +1,18 @@ +import XCTest + +protocol AssertEquatable { + func assertEqual(_ lhs: Object?, _ rhs: Object?) + func assertNotEqual(_ lhs: Object?, _ rhs: Object?) +} + +extension AssertEquatable { + + func assertEqual(_ lhs: Object?, _ rhs: Object?) { + XCTAssertEqual(lhs, rhs) + } + + func assertNotEqual(_ lhs: Object?, _ rhs: Object?) { + XCTAssertNotEqual(lhs, rhs) + } + +} diff --git a/QuizTrainTests/Testing Protocols/Asserts/AssertJSONDeserializing.swift b/QuizTrainTests/Testing Protocols/Asserts/AssertJSONDeserializing.swift new file mode 100644 index 0000000..406c989 --- /dev/null +++ b/QuizTrainTests/Testing Protocols/Asserts/AssertJSONDeserializing.swift @@ -0,0 +1,47 @@ +import XCTest +@testable import QuizTrain + +protocol AssertJSONDeserializing { + func assertJSONDeserializing(type: Object.Type, from json: JSONDictionary) + func assertJSONDeserializing(type: Object.Type, from json: [JSONDictionary]) + func assertJSONDeserializing(type: Object.Type, failsByOmittingKeysFrom json: JSONDictionary) + func assertJSONDeserializing(type: Object.Type, failsByOmittingKeysFrom json: [JSONDictionary]) +} + +extension AssertJSONDeserializing { + + func assertJSONDeserializing(type: Object.Type, from json: JSONDictionary) { + let object: Object? = Object.deserialized(json) + XCTAssertNotNil(object) + } + + func assertJSONDeserializing(type: Object.Type, from json: [JSONDictionary]) { + let objects: [Object]? = Object.deserialized(json) + XCTAssertNotNil(objects) + XCTAssertEqual(objects!.count, json.count) + } + + func assertJSONDeserializing(type: Object.Type, failsByOmittingKeysFrom json: JSONDictionary) { + for (k, _) in json { + var incompleteJson = json + incompleteJson.removeValue(forKey: k) + let object: Object? = Object.deserialized(incompleteJson) + XCTAssertNil(object) + } + } + + func assertJSONDeserializing(type: Object.Type, failsByOmittingKeysFrom json: [JSONDictionary]) { + for index in 0..(_ object: Object) + func assertJSONSerializing(_ objects: [Object]) +} + +extension AssertJSONSerializing { + + func assertJSONSerializing(_ object: Object) { + let serializedA: JSONDictionary = Object.serialized(object) + XCTAssertNotNil(serializedA) // This will always pass. + let serializedB = object.serialized() + XCTAssertNotNil(serializedB) // This will always pass. + } + + func assertJSONSerializing(_ objects: [Object]) { + let serialized: [JSONDictionary] = Object.serialized(objects) + XCTAssertNotNil(serialized) // This will always pass. + XCTAssertEqual(serialized.count, objects.count) + } + +} diff --git a/QuizTrainTests/Testing Protocols/Asserts/AssertJSONTwoWaySerialization.swift b/QuizTrainTests/Testing Protocols/Asserts/AssertJSONTwoWaySerialization.swift new file mode 100644 index 0000000..af3828f --- /dev/null +++ b/QuizTrainTests/Testing Protocols/Asserts/AssertJSONTwoWaySerialization.swift @@ -0,0 +1,87 @@ +import XCTest +@testable import QuizTrain + +protocol AssertJSONTwoWaySerialization { + associatedtype Object: Equatable, JSONDeserializable, JSONSerializable + func assertJSONTwoWaySerialization(_ json: JSONDictionary) + func assertJSONTwoWaySerialization(_ json: [JSONDictionary]) + func assertJSONTwoWaySerialization(_ object: Object) + func assertJSONTwoWaySerialization(_ objects: [Object]) +} + +extension AssertJSONTwoWaySerialization { + + // MARK: JSON to Object(s) to JSON + + func assertJSONTwoWaySerialization(_ json: JSONDictionary) { + + let object: Object? = Object.deserialized(json) + XCTAssertNotNil(object) + + // Instance Method + let serializedA: JSONDictionary = object!.serialized() + + for (k, _) in json { + XCTAssertNotNil(serializedA[k]) + } + + let deserializedA: Object? = Object.deserialized(serializedA) + XCTAssertNotNil(deserializedA) + XCTAssertEqual(deserializedA!, object!) + + // Class Method + let serializedB: JSONDictionary = Object.serialized(object!) + + for (k, _) in json { + XCTAssertNotNil(serializedB[k]) + } + + let deserializedB: Object? = Object.deserialized(serializedB) + XCTAssertNotNil(deserializedB) + XCTAssertEqual(deserializedB!, object!) + } + + func assertJSONTwoWaySerialization(_ json: [JSONDictionary]) { + + let objects: [Object]? = Object.deserialized(json) + XCTAssertNotNil(objects) + XCTAssertEqual(objects!.count, json.count) + + let serialized: [JSONDictionary] = Object.serialized(objects!) + XCTAssertEqual(serialized.count, json.count) + + let deserialized: [Object]? = Object.deserialized(serialized) + XCTAssertNotNil(deserialized) + XCTAssertEqual(deserialized!, objects!) + } + + // MARK: Object(s) to JSON to Object(s) + + func assertJSONTwoWaySerialization(_ object: Object) { + + // Instance Method + let serializedA: JSONDictionary = object.serialized() + let deserializedA: Object? = Object.deserialized(serializedA) + + XCTAssertNotNil(deserializedA) + XCTAssertEqual(deserializedA!, object) + + // Class Method + let serializedB: JSONDictionary = Object.serialized(object) + let deserializedB: Object? = Object.deserialized(serializedB) + + XCTAssertNotNil(deserializedB) + XCTAssertEqual(deserializedB!, object) + } + + func assertJSONTwoWaySerialization(_ objects: [Object]) { + + let serialized: [JSONDictionary] = Object.serialized(objects) + XCTAssertEqual(serialized.count, objects.count) + + let deserialized: [Object]? = Object.deserialized(serialized) + XCTAssertNotNil(deserialized) + XCTAssertEqual(deserialized!, objects) + } + +} diff --git a/QuizTrainTests/Testing Protocols/Asserts/AssertProperties.swift b/QuizTrainTests/Testing Protocols/Asserts/AssertProperties.swift new file mode 100644 index 0000000..f6c1bed --- /dev/null +++ b/QuizTrainTests/Testing Protocols/Asserts/AssertProperties.swift @@ -0,0 +1,8 @@ +import XCTest + +protocol AssertProperties { + associatedtype Object + func assertRequiredProperties(in object: Object) + func assertOptionalProperties(in object: Object, areNil: Bool) + func assertVariablePropertiesCanBeChanged(in object: inout Object) +} diff --git a/QuizTrainTests/Testing Protocols/Asserts/AssertUpdateRequestJSON.swift b/QuizTrainTests/Testing Protocols/Asserts/AssertUpdateRequestJSON.swift new file mode 100644 index 0000000..5251153 --- /dev/null +++ b/QuizTrainTests/Testing Protocols/Asserts/AssertUpdateRequestJSON.swift @@ -0,0 +1,19 @@ +import XCTest +@testable import QuizTrain + +protocol AssertUpdateRequestJSON { + associatedtype Object: UpdateRequestJSON, UpdateRequestJSONKeys + func assertUpdateRequestJSON(_ object: Object) +} + +extension AssertUpdateRequestJSON { + + func assertUpdateRequestJSON(_ object: Object) { + let updateRequestJSON = object.updateRequestJSON + XCTAssertEqual(updateRequestJSON.count, object.updateRequestJSONKeys.count) + for key in object.updateRequestJSONKeys { + XCTAssertNotNil(updateRequestJSON[key]) + } + } + +} diff --git a/QuizTrainTests/Testing Protocols/Asserts/AssertValidatable.swift b/QuizTrainTests/Testing Protocols/Asserts/AssertValidatable.swift new file mode 100644 index 0000000..8e0e9b1 --- /dev/null +++ b/QuizTrainTests/Testing Protocols/Asserts/AssertValidatable.swift @@ -0,0 +1,19 @@ +import XCTest +@testable import QuizTrain + +protocol AssertValidatable { + func assertValid(_ object: Validatable) + func assertInvalid(_ object: Validatable) +} + +extension AssertValidatable { + + func assertValid(_ object: Validatable) { + XCTAssertTrue(object.isValid) + } + + func assertInvalid(_ object: Validatable) { + XCTAssertFalse(object.isValid) + } + +} diff --git a/QuizTrainTests/Testing Protocols/Providers/CustomFieldsDataProvider.swift b/QuizTrainTests/Testing Protocols/Providers/CustomFieldsDataProvider.swift new file mode 100644 index 0000000..83ea760 --- /dev/null +++ b/QuizTrainTests/Testing Protocols/Providers/CustomFieldsDataProvider.swift @@ -0,0 +1,141 @@ +@testable import QuizTrain + +protocol CustomFieldsDataProvider { + + // Valid + + var customFields: JSONDictionary { get } + var customFieldsContainer: CustomFieldsContainer { get } + var customFieldsKeys: [JSONKey] { get } + static var customFields: JSONDictionary { get } + static var customFieldsContainer: CustomFieldsContainer { get } + static var customFieldsKeys: [JSONKey] { get } + + // Empty + + var emptyCustomFields: JSONDictionary { get } + var emptyCustomFieldsContainer: CustomFieldsContainer { get } + static var emptyCustomFields: JSONDictionary { get } + static var emptyCustomFieldsContainer: CustomFieldsContainer { get } + + // Invalid + + var invalidCustomFields: JSONDictionary { get } + var invalidCustomFieldsKeys: [JSONKey] { get } + static var invalidCustomFields: JSONDictionary { get } + static var invalidCustomFieldsKeys: [JSONKey] { get } + + // Valid/Invalid + + var validAndInvalidCustomFields: JSONDictionary { get } + var validAndInvalidCustomFieldsKeys: [JSONKey] { get } + static var validAndInvalidCustomFields: JSONDictionary { get } + static var validAndInvalidCustomFieldsKeys: [JSONKey] { get } +} + +// MARK: - Valid + +extension CustomFieldsDataProvider { + + var customFields: JSONDictionary { + return Self.customFields + } + + var customFieldsContainer: CustomFieldsContainer { + return Self.customFieldsContainer + } + + var customFieldsKeys: [JSONKey] { + return Self.customFieldsKeys + } + + static var customFields: JSONDictionary { + return [ + "custom_field_0": -587, + "custom_field_1": "What is the meaning of life?", + "custom_field_2": 3.14159, + "custom_field_3": ["Hello": ["🐹🐹🐹", 4.32, true, 789]] + ] + } + + static var customFieldsContainer: CustomFieldsContainer { + return CustomFieldsContainer(json: customFields) + } + + static var customFieldsKeys: [JSONKey] { + return Self.customFields.map { $0.key } + } + +} + +// MARK: - Empty + +extension CustomFieldsDataProvider { + + var emptyCustomFields: JSONDictionary { + return Self.emptyCustomFields + } + + var emptyCustomFieldsContainer: CustomFieldsContainer { + return Self.emptyCustomFieldsContainer + } + + static var emptyCustomFields: JSONDictionary { + return [:] + } + + static var emptyCustomFieldsContainer: CustomFieldsContainer { + return CustomFieldsContainer(json: [:]) + } + +} + +// MARK: - Invalid + +extension CustomFieldsDataProvider { + + var invalidCustomFields: JSONDictionary { + return Self.invalidCustomFields + } + + var invalidCustomFieldsKeys: [JSONKey] { + return Self.invalidCustomFieldsKeys + } + + static var invalidCustomFields: JSONDictionary { + return ["_custom_field": "Invalid", + "customField": "Invalid", + "this_custom_field": "is also invalid", + "Custom_field": "Invalid", + " custom_field": ["😀😀😀": 3.14]] + } + + static var invalidCustomFieldsKeys: [JSONKey] { + return Self.invalidCustomFields.map { $0.key } + } + +} + +// MARK: - Valid/Invalid + +extension CustomFieldsDataProvider { + + var validAndInvalidCustomFields: JSONDictionary { + return Self.validAndInvalidCustomFields + } + + var validAndInvalidCustomFieldsKeys: [JSONKey] { + return Self.validAndInvalidCustomFieldsKeys + } + + static var validAndInvalidCustomFields: JSONDictionary { + var dict = CustomFieldsContainerTests.customFields + CustomFieldsContainerTests.invalidCustomFields.forEach { item in dict[item.key] = item.value } + return dict + } + + static var validAndInvalidCustomFieldsKeys: [JSONKey] { + return Self.validAndInvalidCustomFields.map { $0.key } + } + +} diff --git a/QuizTrainTests/Testing Protocols/Providers/JSONDataProvider.swift b/QuizTrainTests/Testing Protocols/Providers/JSONDataProvider.swift new file mode 100644 index 0000000..8a4c5d7 --- /dev/null +++ b/QuizTrainTests/Testing Protocols/Providers/JSONDataProvider.swift @@ -0,0 +1,46 @@ +import XCTest +@testable import QuizTrain + +protocol JSONDataProvider { + + var requiredJSON: JSONDictionary { get } + var optionalJSON: JSONDictionary { get } + var requiredAndOptionalJSON: JSONDictionary { get } + + static var requiredJSON: JSONDictionary { get } + static var optionalJSON: JSONDictionary { get } + static var requiredAndOptionalJSON: JSONDictionary { get } +} + +extension JSONDataProvider { + + var requiredJSON: JSONDictionary { + return Self.requiredJSON + } + + var optionalJSON: JSONDictionary { + return Self.optionalJSON + } + + var requiredAndOptionalJSON: JSONDictionary { + return Self.requiredAndOptionalJSON + } + + static var requiredAndOptionalJSON: JSONDictionary { + var dict = Self.requiredJSON + Self.optionalJSON.forEach { item in dict[item.key] = item.value } + return dict + } + +} + +extension JSONDataProvider where Self: CustomFieldsDataProvider { + + static var requiredAndOptionalJSON: JSONDictionary { + var dict = Self.requiredJSON + Self.optionalJSON.forEach { item in dict[item.key] = item.value } + customFields.forEach { item in dict[item.key] = item.value } + return dict + } + +} diff --git a/QuizTrainTests/Testing Protocols/Providers/ObjectProvider.swift b/QuizTrainTests/Testing Protocols/Providers/ObjectProvider.swift new file mode 100644 index 0000000..3da1eed --- /dev/null +++ b/QuizTrainTests/Testing Protocols/Providers/ObjectProvider.swift @@ -0,0 +1,45 @@ +import XCTest +@testable import QuizTrain + +protocol ObjectProvider { + + associatedtype Object + + var objectWithRequiredProperties: Object { get } + var objectWithRequiredAndOptionalProperties: Object { get } + + static var objectWithRequiredProperties: Object { get } + static var objectWithRequiredAndOptionalProperties: Object { get } +} + +extension ObjectProvider { + + var objectWithRequiredProperties: Object { + return Self.objectWithRequiredProperties + } + + var objectWithRequiredAndOptionalProperties: Object { + return Self.objectWithRequiredAndOptionalProperties + } + +} + +extension ObjectProvider where Self: JSONDataProvider, Object: JSONDeserializable { + + var objectWithRequiredPropertiesFromJSON: Object? { + return Self.objectWithRequiredPropertiesFromJSON + } + + var objectWithRequiredAndOptionalPropertiesFromJSON: Object? { + return Self.objectWithRequiredAndOptionalPropertiesFromJSON + } + + static var objectWithRequiredPropertiesFromJSON: Object? { + return Object(json: requiredJSON) + } + + static var objectWithRequiredAndOptionalPropertiesFromJSON: Object? { + return Object(json: requiredAndOptionalJSON) + } + +} diff --git a/QuizTrainTests/Testing Protocols/Providers/ValidatableObjectProvider.swift b/QuizTrainTests/Testing Protocols/Providers/ValidatableObjectProvider.swift new file mode 100644 index 0000000..df6f501 --- /dev/null +++ b/QuizTrainTests/Testing Protocols/Providers/ValidatableObjectProvider.swift @@ -0,0 +1,7 @@ +import XCTest +@testable import QuizTrain + +protocol ValidatableObjectProvider { + var validObject: Validatable { get } + var invalidObject: Validatable { get } +} diff --git a/QuizTrainTests/Testing Protocols/Tests/AddRequestJSONTests.swift b/QuizTrainTests/Testing Protocols/Tests/AddRequestJSONTests.swift new file mode 100644 index 0000000..133078b --- /dev/null +++ b/QuizTrainTests/Testing Protocols/Tests/AddRequestJSONTests.swift @@ -0,0 +1,18 @@ +import XCTest +@testable import QuizTrain + +protocol AddRequestJSONTests { + + func testAddRequestJSON() + func _testAddRequestJSON() + +} + +extension AddRequestJSONTests where Self: AssertAddRequestJSON & ObjectProvider, Self.Object: AddRequestJSON & AddRequestJSONKeys { + + func _testAddRequestJSON() { + let object = objectWithRequiredAndOptionalProperties + assertAddRequestJSON(object) + } + +} diff --git a/QuizTrainTests/Testing Protocols/Tests/EquatableTests.swift b/QuizTrainTests/Testing Protocols/Tests/EquatableTests.swift new file mode 100644 index 0000000..ee5d4e0 --- /dev/null +++ b/QuizTrainTests/Testing Protocols/Tests/EquatableTests.swift @@ -0,0 +1,19 @@ +import XCTest + +protocol EquatableTests { + + func testEquatable() + func _testEquatable() + +} + +extension EquatableTests where Self: AssertEquatable & ObjectProvider, Self.Object: Equatable { + + func _testEquatable() { + let objectA = objectWithRequiredAndOptionalProperties + let objectB = objectWithRequiredAndOptionalProperties + assertEqual(objectA, objectB) + assertNotEqual(objectA, nil) + } + +} diff --git a/QuizTrainTests/Testing Protocols/Tests/InitTests.swift b/QuizTrainTests/Testing Protocols/Tests/InitTests.swift new file mode 100644 index 0000000..8c60b7d --- /dev/null +++ b/QuizTrainTests/Testing Protocols/Tests/InitTests.swift @@ -0,0 +1,46 @@ +import XCTest +@testable import QuizTrain + +protocol InitTests { + + func testInit() + func testInitWithOptionalProperties() + + func _testInit() + func _testInitWithOptionalProperties() + +} + +extension InitTests where Self: AssertProperties & ObjectProvider { + + func _testInit() { + let object = objectWithRequiredProperties + assertRequiredProperties(in: object) + assertOptionalProperties(in: object, areNil: true) + } + + func _testInitWithOptionalProperties() { + let object = objectWithRequiredAndOptionalProperties + assertRequiredProperties(in: object) + assertOptionalProperties(in: object, areNil: false) + } + +} + +extension InitTests where Self: AssertCustomFields & AssertProperties & ObjectProvider, Self.Object: CustomFields { + + func _testInit() { + let object = objectWithRequiredProperties + assertRequiredProperties(in: object) + assertOptionalProperties(in: object, areNil: true) + assertCustomFields(in: object, areEmpty: true) + } + + func _testInitWithOptionalProperties() { + let object = objectWithRequiredAndOptionalProperties + assertRequiredProperties(in: object) + assertOptionalProperties(in: object, areNil: false) + assertCustomFields(in: object, areEmpty: false) + } + +} diff --git a/QuizTrainTests/Testing Protocols/Tests/JSONDeserializingTests.swift b/QuizTrainTests/Testing Protocols/Tests/JSONDeserializingTests.swift new file mode 100644 index 0000000..ca11c87 --- /dev/null +++ b/QuizTrainTests/Testing Protocols/Tests/JSONDeserializingTests.swift @@ -0,0 +1,82 @@ +import XCTest +@testable import QuizTrain + +protocol JSONDeserializingTests { + + func testJSONDeserializing() + func testJSONDeserializingWithOptionalProperties() + func testJSONDeserializingASingleObject() + func testJSONDeserializingMultipleObjects() + func testJSONDeserializingASingleObjectMissingRequiredProperties() + func testJSONDeserializingMultipleObjectsMissingRequiredProperties() + + func _testJSONDeserializing() + func _testJSONDeserializingWithOptionalProperties() + func _testJSONDeserializingASingleObject() + func _testJSONDeserializingMultipleObjects() + func _testJSONDeserializingASingleObjectMissingRequiredProperties() + func _testJSONDeserializingMultipleObjectsMissingRequiredProperties() + +} + +extension JSONDeserializingTests where Self: AssertJSONDeserializing & AssertProperties & JSONDataProvider & ObjectProvider, Self.Object: JSONDeserializable { + + func _testJSONDeserializing() { + guard let object = objectWithRequiredPropertiesFromJSON else { + XCTFail("nil object returned.") + return + } + assertRequiredProperties(in: object) + assertOptionalProperties(in: object, areNil: true) + } + + func _testJSONDeserializingWithOptionalProperties() { + guard let object = objectWithRequiredAndOptionalPropertiesFromJSON else { + XCTFail("nil object returned.") + return + } + assertRequiredProperties(in: object) + assertOptionalProperties(in: object, areNil: false) + } + + func _testJSONDeserializingASingleObject() { + assertJSONDeserializing(type: Object.self, from: requiredAndOptionalJSON) + } + + func _testJSONDeserializingMultipleObjects() { + assertJSONDeserializing(type: Object.self, from: [requiredAndOptionalJSON, requiredAndOptionalJSON, requiredAndOptionalJSON]) + } + + func _testJSONDeserializingASingleObjectMissingRequiredProperties() { + assertJSONDeserializing(type: Object.self, failsByOmittingKeysFrom: requiredJSON) + } + + func _testJSONDeserializingMultipleObjectsMissingRequiredProperties() { + assertJSONDeserializing(type: Object.self, failsByOmittingKeysFrom: [requiredJSON, requiredJSON, requiredJSON]) + } + +} + +extension JSONDeserializingTests where Self: AssertCustomFields & AssertJSONDeserializing & AssertProperties & JSONDataProvider & ObjectProvider, Self.Object: CustomFields & JSONDeserializable { + + func _testJSONDeserializing() { + guard let object = objectWithRequiredPropertiesFromJSON else { + XCTFail("nil object returned.") + return + } + assertRequiredProperties(in: object) + assertOptionalProperties(in: object, areNil: true) + assertCustomFields(in: object, areEmpty: true) + } + + func _testJSONDeserializingWithOptionalProperties() { + guard let object = objectWithRequiredAndOptionalPropertiesFromJSON else { + XCTFail("nil object returned.") + return + } + assertRequiredProperties(in: object) + assertOptionalProperties(in: object, areNil: false) + assertCustomFields(in: object, areEmpty: false) + } + +} diff --git a/QuizTrainTests/Testing Protocols/Tests/JSONSerializingTests.swift b/QuizTrainTests/Testing Protocols/Tests/JSONSerializingTests.swift new file mode 100644 index 0000000..e66d7aa --- /dev/null +++ b/QuizTrainTests/Testing Protocols/Tests/JSONSerializingTests.swift @@ -0,0 +1,26 @@ +import XCTest +@testable import QuizTrain + +protocol JSONSerializingTests { + + func testJSONSerializingSingleObjects() + func testJSONSerializingMultipleObjects() + + func _testJSONSerializingSingleObjects() + func _testJSONSerializingMultipleObjects() + +} + +extension JSONSerializingTests where Self: AssertJSONSerializing & ObjectProvider, Self.Object: JSONSerializable { + + func _testJSONSerializingSingleObjects() { + assertJSONSerializing(objectWithRequiredProperties) + assertJSONSerializing(objectWithRequiredAndOptionalProperties) + } + + func _testJSONSerializingMultipleObjects() { + assertJSONSerializing([objectWithRequiredProperties, objectWithRequiredProperties, objectWithRequiredProperties]) + assertJSONSerializing([objectWithRequiredAndOptionalProperties, objectWithRequiredAndOptionalProperties, objectWithRequiredAndOptionalProperties]) + } + +} diff --git a/QuizTrainTests/Testing Protocols/Tests/JSONTwoWaySerializationTests.swift b/QuizTrainTests/Testing Protocols/Tests/JSONTwoWaySerializationTests.swift new file mode 100644 index 0000000..227dda5 --- /dev/null +++ b/QuizTrainTests/Testing Protocols/Tests/JSONTwoWaySerializationTests.swift @@ -0,0 +1,33 @@ +import XCTest + +protocol JSONTwoWaySerializationTests { + + func testJSONTwoWaySerializationForSingleItems() + func testJSONTwoWaySerializationForMultipleItems() + + func _testJSONTwoWaySerializationForSingleItems() + func _testJSONTwoWaySerializationForMultipleItems() + +} + +extension JSONTwoWaySerializationTests where Self: AssertJSONTwoWaySerialization & JSONDataProvider & ObjectProvider { + + func _testJSONTwoWaySerializationForSingleItems() { + // Object -> JSON -> Object + assertJSONTwoWaySerialization(objectWithRequiredProperties) + assertJSONTwoWaySerialization(objectWithRequiredAndOptionalProperties) + // JSON -> Object -> JSON + assertJSONTwoWaySerialization(requiredJSON) + assertJSONTwoWaySerialization(requiredAndOptionalJSON) + } + + func _testJSONTwoWaySerializationForMultipleItems() { + // Object -> JSON -> Object + assertJSONTwoWaySerialization([objectWithRequiredProperties, objectWithRequiredProperties, objectWithRequiredProperties]) + assertJSONTwoWaySerialization([objectWithRequiredAndOptionalProperties, objectWithRequiredAndOptionalProperties, objectWithRequiredAndOptionalProperties]) + // JSON -> Object -> JSON + assertJSONTwoWaySerialization([requiredJSON, requiredJSON, requiredJSON]) + assertJSONTwoWaySerialization([requiredAndOptionalJSON, requiredAndOptionalJSON, requiredAndOptionalJSON]) + } + +} diff --git a/QuizTrainTests/Testing Protocols/Tests/README.md b/QuizTrainTests/Testing Protocols/Tests/README.md new file mode 100644 index 0000000..43a83c6 --- /dev/null +++ b/QuizTrainTests/Testing Protocols/Tests/README.md @@ -0,0 +1,20 @@ +## XCTestCase Protocol Extensions + +Apple's testing framework is unable to identify `test*()` methods defined in protocol extensions applied to `XCTestCase` appearing in a different file than the `XCTestCase`. Because of this methods are defined in protocols appearing in the `../Tests/` group as `_test()` and implemented in a protocol extension. Objects conforming to this protocol must call the corresponding `_test*()` method inside of their `test*()` implementation. For example: + + // SomeProtocol + + func testSomething() + func _testSomething() + + // SomeProtocol Extension + + func _testSomething() { + ...test code... + } + + // Some XCTestCase conforming to SomeProtocol + + func testSomething() { + _testSomething() + } diff --git a/QuizTrainTests/Testing Protocols/Tests/UpdateRequestJSONTests.swift b/QuizTrainTests/Testing Protocols/Tests/UpdateRequestJSONTests.swift new file mode 100644 index 0000000..553f058 --- /dev/null +++ b/QuizTrainTests/Testing Protocols/Tests/UpdateRequestJSONTests.swift @@ -0,0 +1,23 @@ +import XCTest + +protocol UpdateRequestJSONTests { + + func testUpdateRequestJSON() + func _testUpdateRequestJSON() + +} + +extension UpdateRequestJSONTests { + + func _testUpdateRequestJSON() { /* Applies only to tests conforming to AssertUpdateRequestJSON/ObjectProvider. */ } + +} + +extension UpdateRequestJSONTests where Self: AssertUpdateRequestJSON & ObjectProvider { + + func _testUpdateRequestJSON() { + let object = objectWithRequiredAndOptionalProperties + assertUpdateRequestJSON(object) + } + +} diff --git a/QuizTrainTests/Testing Protocols/Tests/ValidatableTests.swift b/QuizTrainTests/Testing Protocols/Tests/ValidatableTests.swift new file mode 100644 index 0000000..4d11ced --- /dev/null +++ b/QuizTrainTests/Testing Protocols/Tests/ValidatableTests.swift @@ -0,0 +1,22 @@ +import XCTest + +protocol ValidatableTests { + + func testIsValid() + func testIsInvalid() + func _testIsValid() + func _testIsInvalid() + +} + +extension ValidatableTests where Self: AssertValidatable & ValidatableObjectProvider { + + func _testIsValid() { + assertValid(validObject) + } + + func _testIsInvalid() { + assertInvalid(invalidObject) + } + +} diff --git a/QuizTrainTests/Testing Protocols/Tests/VariablePropertyTests.swift b/QuizTrainTests/Testing Protocols/Tests/VariablePropertyTests.swift new file mode 100644 index 0000000..ca031b8 --- /dev/null +++ b/QuizTrainTests/Testing Protocols/Tests/VariablePropertyTests.swift @@ -0,0 +1,17 @@ +import XCTest + +protocol VariablePropertyTests { + + func testVariableProperties() + func _testVariableProperties() + +} + +extension VariablePropertyTests where Self: AssertProperties & ObjectProvider { + + func _testVariableProperties() { + var object = objectWithRequiredAndOptionalProperties + assertVariablePropertiesCanBeChanged(in: &object) + } + +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..542adaf --- /dev/null +++ b/README.md @@ -0,0 +1,256 @@ +# QuizTrain 📝🚆 + +QuizTrain is a framework created at Venmo allowing you to interact with [TestRail's API](http://docs.gurock.com/testrail-api2/start) using Swift. It supports iOS, macOS, tvOS, and watchOS. + +To use QuizTrain you must have a valid [TestRail](http://www.gurock.com/testrail/) license and instance to access. + +## Licensing + +QuizTrain is open source software released under the MIT License. See the [LICENSE](LICENSE) file for details. + +## Installation + +[Carthage](https://github.com/Carthage/Carthage) is the recommended way to install QuizTrain. Add the following to your `Cartfile` or `Cartfile.private` file: + + github "venmo/QuizTrain" ~> 1.0.0 + +See [Adding frameworks to an application](https://github.com/Carthage/Carthage#adding-frameworks-to-an-application) for further instructions. Once complete `import QuizTrain` in any Swift files you wish to use QuizTrain in. + +## Usage + +Create an `ObjectAPI` to get, add, update, delete, and close items on your TestRail instance. + + let objectAPI = ObjectAPI(username: "your@testRailAccount.email", secret: "your_api_key_or_password", hostname: "yourInstance.testrail.net", port: 443, scheme: "https") + +Alternatively you can use `API` directly if you would rather work with basic Swift types. Generally it is better to use `ObjectAPI` as `API` is a lower level of abstraction. For differences see comments in [API.swift](QuizTrain/Network/API.swift) and [ObjectAPI.swift](QuizTrain/Network/ObjectAPI.swift). + +## Examples + +Below shows a limited number of examples. For all examples see [ObjectAPITests.swift](QuizTrainTests/Network/ObjectAPITests.swift). + +#### Get all Cases in a Project + + objectAPI.getCases(inProjectWithId: 5) { (outcome) in + switch outcome { + case .failed(let error): + print(error.debugDescription) + case .succeeded(let cases): + print(cases) // Do something with cases. + } + } + +#### Add a Case + + let section: Section = ... + let newCase = NewCase(estimate: nil, milestoneId: nil, priorityId: nil, refs: nil, templateId: nil, title: "New Case Title", typeId: nil, customFields: nil) + + objectAPI.addCase(newCase, to: section) { (outcome) in + switch outcome { + case .failed(let error): + print(error.debugDescription) + case .succeeded(let `case`): + print(`case`.title) // Do something with the newly created `case`. + } + } + +#### Update a Suite + + var suite: Suite = ... + suite.description = "Updated description for this suite." + suite.name = "Updated name of this suite." + + objectAPI.updateSuite(suite) { (outcome) in + switch outcome { + case .failed(let error): + print(error.debugDescription) + case .succeeded(let updatedSuite): + print(updatedSuite.description) // "Updated description for this suite." + print(updatedSuite.name) // "Updated name of this suite." + } + } + +#### Delete a Section + + let section: Section = ... + + objectAPI.deleteSection(section) { (outcome) in + switch outcome { + case .failed(let error): + print(error.debugDescription) + case .succeeded(_): // nil on successful deletes + print("The section has been successfully deleted.") + } + } + +#### Close a Plan + + let plan: Plan = ... + + objectAPI.closePlan(plan) { (outcome) in + switch outcome { + case .failed(let error): + print(error.debugDescription) + case .succeeded(let closedPlan): + print(closedPlan.isCompleted) // true + print(closedPlan.completedOn) // timestamp + } + } + +#### Get a Relationship + + let milestone: Milestone = ... + + milestone.parent(objectAPI) { (outcome) in + switch outcome { + case .failed(let error): + print(error.debugDescription) + case .succeeded(let optionalParent): + if let parent = optionalParent { + print("Milestone \(milestone.id) has a parent with an id of \(parent.id).") + } else { + print("Milestone \(milestone.id) does not have a parent.") + } + } + } + +#### Get Completed Runs in a Project using a single Filter + + let filters = [Filter(named: "is_completed", matching: true)] + + objectAPI.getRuns(inProjectWithId: 3, filteredBy: filters) { (outcome) in + switch outcome { + case .failed(let error): + print(error.debugDescription) + case .succeeded(let completedRuns): + for completedRun in completedRuns { + print(completedRun.isCompleted) // true + } + } + } + +#### Get Plans in a Project using multiple Filters + + let project: Project = ... + let filters = [Filter(named: "offset", matching: 3), + Filter(named: "limit", matching: 5)] + + objectAPI.getPlans(in: project, filteredBy: filters) { (outcome) in + switch outcome { + case .failed(let error): + print(error.debugDescription) + case .succeeded(let plans): // There will be 5 or less plans. + for plan in plans { + print(plan.name) + } + } + } + +## Error Handling + +It is up to the consumer of QuizTrain to handle all errors. However `ObjectAPI` will handle 429 Too Many Request (rate limit reached) errors automatically unless you set `.handle429TooManyRequestErrors` to `false`. + +Errors are defined here: + +- [API.swift](QuizTrain/Network/API.swift) in the *Errors* section. +- [ObjectAPI.swift](QuizTrain/Network/ObjectAPI.swift) in the *Errors* section. + +You can handle errors two ways: + +1. Simply by using `error.debugDescription` to print a rich description of an error. + - *Provided by errors conforming to `DebugDescription`.* +2. Advanced by `switch`'ing on an error. + +Simple is best for debugging and logging. Advanced is best for everything else. + +All API and ObjectAPI errors conform to [`DebugDescription`](QuizTrain/Misc/Debug/DebugDescription.swift). If these errors print a `URLRequest` through this protocol then its `AUTHORIZATION` header will be stripped to avoid exposing your TestRail credentials. + +### Example + +This shows both simple and advanced error handling when adding a new Case to a Section. + +#### Setup + + let newCase = NewCase(estimate: nil, milestoneId: nil, priorityId: nil, refs: nil, templateId: nil, title: "New Case Title", typeId: nil, customFields: nil) + +#### Simple + + objectAPI.addCase(newCase, toSectionWithId: 5) { (outcome) in + switch outcome { + case .failed(let error): + print(error.debugDescription) + case .succeeded(let `case`): + print(`case`.title) // Do something with the newly created `case`. + } + } + +#### Advanced + + objectAPI.addCase(newCase, toSectionWithId: 5) { (outcome) in + switch outcome { + case .failed(let error): + switch error { + case .apiError(let apiError): // API.RequestError + switch apiError { + case .error(let request, let error): + print(request) + print(error) + case .invalidResponse(let request, let response): + print(request) + print(response) + case .nilResponse(let request): + print(request) + } + case .dataProcessingError(let dataProcessingError): // ObjectAPI.DataProcessingError + switch dataProcessingError { + case .couldNotConvertDataToJSON(let data, let error): + print(data) + print(error) + case .couldNotDeserializeFromJSON(let objectType, let json): + print(objectType) + print(json) + case .invalidJSONFormat(let json): + print(json) + } + case .objectConversionError(let objectConversionError): // ObjectAPI.ObjectConversionError + switch objectConversionError { + case .couldNotConvertObjectsToData(let objects, let json, let error): + print(objects) + print(json) + print(error) + case .couldNotConvertObjectToData(let object, let json, let error): + print(object) + print(json) + print(error) + } + case .statusCodeError(let statusCodeError): // ObjectAPI.StatusCodeError + switch statusCodeError { + case .clientError(let clientError): // ObjectAPI.ClientError + print(clientError.message) + print(clientError.statusCode) + print(clientError.requestResult.request) + print(clientError.requestResult.response) + print(clientError.requestResult.data) + case .otherError(let otherError): // API.RequestResult + print(otherError.request) + print(otherError.response) + print(otherError.data) + case .serverError(let serverError): // ObjectAPI.ServerError + print(serverError.message) + print(serverError.statusCode) + print(serverError.requestResult.request) + print(serverError.requestResult.response) + print(serverError.requestResult.data) + } + } + case .succeeded(let `case`): + print(`case`.title) // Do something with the newly created `case`. + } + } + +## Testing + +See the [QuizTrainTests Readme](QuizTrainTests/README.md). + +## Entities + +![Image of Entities](Entities.png)

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⪼U@8iXweO=9$jZOo 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 z03