Emcee

Форк
0
/
RunTestsCommand.swift 
225 строк · 11.5 Кб
1
import ArgLib
2
import AutomaticTermination
3
import BuildArtifacts
4
import CommonTestModels
5
import Deployer
6
import EmceeDI
7
import EmceeVersion
8
import Foundation
9
import LocalHostDeterminer
10
import MetricsExtensions
11
import PathLib
12
import QueueModels
13
import QueueServerConfiguration
14
import RESTServer
15
import ResourceLocationResolver
16
import SimulatorPoolModels
17
import TestArgFile
18
import TestDestination
19
import Tmp
20
import UniqueIdentifierGenerator
21

22
public final class RunTestsCommand: Command {
23
    public let name = "runTests"
24
    public let description = "Runs tests (easy to use command)"
25
    public let arguments: Arguments = [
26
        ArgumentDescriptions.queue.asRequired.asMultiple,
27
        ArgumentDescriptions.worker.asRequired.asMultiple,
28
        ArgumentDescriptions.device.asRequired,
29
        ArgumentDescriptions.runtime.asRequired,
30
        ArgumentDescriptions.testBundle.asRequired,
31
        ArgumentDescriptions.kind.asOptional,
32
        ArgumentDescriptions.app.asOptional,
33
        ArgumentDescriptions.runner.asOptional,
34
        ArgumentDescriptions.test.asOptional,
35
        ArgumentDescriptions.retries.asOptional,
36
        ArgumentDescriptions.testTimeout.asOptional,
37
        ArgumentDescriptions.junit.asOptional,
38
        ArgumentDescriptions.trace.asOptional,
39
        ArgumentDescriptions.hostname.asOptional,
40
        ArgumentDescriptions.locale.asOptional,
41
        ArgumentDescriptions.language.asMultiple.asOptional,
42
        ArgumentDescriptions.keyboard.asMultiple.asOptional,
43
    ]
44
    
45
    private let di: DI
46
    private let uniqueIdentifierGenerator: UniqueIdentifierGenerator
47
    
48
    public init(di: DI) throws {
49
        self.di = di
50
        self.uniqueIdentifierGenerator = try di.get()
51
    }
52
    
53
    public func run(payload: CommandPayload) throws {
54
        let hostname: String = try payload.optionalSingleTypedValue(argumentName: ArgumentDescriptions.hostname.name) ?? LocalHostDeterminer.currentHostAddress
55
        try HostnameSetup.update(hostname: hostname, di: di)
56
        
57
        let httpRestServer = HTTPRESTServer(
58
            automaticTerminationController: StayAliveTerminationController(),
59
            logger: try di.get(),
60
            portProvider: AnyAvailablePortProvider(),
61
            useOnlyIPv4: true
62
        )
63
        
64
        let queueUrls: [URL] = try payload.nonEmptyCollectionOfValues(argumentName: ArgumentDescriptions.queue.name)
65
        let queueDeploymentDestinations = try queueUrls.map { try $0.deploymentDestination() }
66
        
67
        let workerUrls: [URL] = try payload.nonEmptyCollectionOfValues(argumentName: ArgumentDescriptions.worker.name)
68
        let workerDeploymentDestinations = try workerUrls.map { try $0.deploymentDestination() }
69
        
70
        let resourceLocationResolver: ResourceLocationResolver = try di.get()
71
        let testBundlePath: AbsolutePath = try resourceLocationResolver.resolvePath(
72
            resourceLocation: .localFilePath(
73
                try payload.expectedSingleTypedValue(argumentName: ArgumentDescriptions.testBundle.name)
74
            )
75
        ).directlyAccessibleResourcePath()
76
        let xcTestBundle = XcTestBundle(location: TestBundleLocation(.localFilePath(testBundlePath.pathString)), testDiscoveryMode: .parseFunctionSymbols)
77
        
78
        let appBundleLocation: String? = try payload.optionalSingleTypedValue(argumentName: ArgumentDescriptions.app.name)
79
        let appBundlePath: AbsolutePath?
80
        if let appBundleLocation = appBundleLocation {
81
            appBundlePath = try resourceLocationResolver.resolvePath(
82
                resourceLocation: .localFilePath(appBundleLocation)
83
            ).directlyAccessibleResourcePath()
84
        } else {
85
            appBundlePath = nil
86
        }
87
        
88
        let runnerBundleLocation: String? = try payload.optionalSingleTypedValue(argumentName: ArgumentDescriptions.runner.name)
89
        let runnerBundlePath: AbsolutePath?
90
        if let runnerBundleLocation = runnerBundleLocation {
91
            runnerBundlePath = try resourceLocationResolver.resolvePath(
92
                resourceLocation: .localFilePath(runnerBundleLocation)
93
            ).directlyAccessibleResourcePath()
94
        } else {
95
            runnerBundlePath = nil
96
        }
97
        
98
        let buildArtifacts: AppleBuildArtifacts
99
        if let runnerBundlePath = runnerBundlePath, let appBundlePath = appBundlePath {
100
            buildArtifacts = .iosUiTests(
101
                xcTestBundle: xcTestBundle,
102
                appBundle: AppBundleLocation(.localFilePath(appBundlePath.pathString)),
103
                runner: RunnerAppLocation(.localFilePath(runnerBundlePath.pathString)),
104
                additionalApplicationBundles: []
105
            )
106
        } else if let appBundlePath = appBundlePath {
107
            buildArtifacts = .iosApplicationTests(
108
                xcTestBundle: xcTestBundle,
109
                appBundle: AppBundleLocation(.localFilePath(appBundlePath.pathString))
110
            )
111
        } else {
112
            buildArtifacts = .iosLogicTests(xcTestBundle: xcTestBundle)
113
        }
114
        
115
        let testNamesToRun: [TestName] = try payload.possiblyEmptyCollectionOfValues(argumentName: ArgumentDescriptions.test.name)
116
        let numberOfRetries: UInt = try payload.optionalSingleTypedValue(argumentName: ArgumentDescriptions.retries.name) ?? TestArgFileDefaultValues.numberOfRetries
117
        let testTimeout: TimeInterval = try payload.optionalSingleTypedValue(argumentName: ArgumentDescriptions.testTimeout.name) ?? TestArgFileDefaultValues.testTimeoutConfiguration.singleTestMaximumDuration
118
        let testDestination = TestDestination.appleSimulator(
119
            deviceType: try payload.expectedSingleTypedValue(argumentName: ArgumentDescriptions.device.name),
120
            kind: try payload.optionalSingleTypedValue(argumentName: ArgumentDescriptions.kind.name) ?? .iOS,
121
            version: try payload.expectedSingleTypedValue(argumentName: ArgumentDescriptions.runtime.name)
122
        )
123
        
124
        let simulatorLocale: String = try payload.optionalSingleTypedValue(argumentName: ArgumentDescriptions.locale.name) ?? TestArgFileDefaultValues.simulatorLocalizationSettings.localeIdentifier
125
        let simulatorLanguages: [String] = try payload.possiblyEmptyCollectionOfValues(argumentName: ArgumentDescriptions.language.name).orIfEmpty(TestArgFileDefaultValues.simulatorLocalizationSettings.languages)
126
        let simulatorKeyboards: [String] = try payload.possiblyEmptyCollectionOfValues(argumentName: ArgumentDescriptions.keyboard.name).orIfEmpty(TestArgFileDefaultValues.simulatorLocalizationSettings.keyboards)
127
        let passcodeKeyboards: [String] = try payload.possiblyEmptyCollectionOfValues(argumentName: ArgumentDescriptions.passcodeKeyboard.name).orIfEmpty(TestArgFileDefaultValues.simulatorLocalizationSettings.passcodeKeyboards)
128
        
129
        var testsToRun: [TestToRun] = []
130
        if testNamesToRun.isEmpty {
131
            testsToRun = [.allDiscoveredTests]
132
        } else {
133
            testsToRun = testNamesToRun.map { .testName($0) }
134
        }
135
        
136
        var environment = [String: String]()
137
        ProcessInfo.processInfo.environment.forEach { (key: String, value: String) in
138
            if key.starts(with: "EMCEE_") {
139
                environment[String(key.dropFirst("EMCEE_".count))] = value
140
            }
141
        }
142
        
143
        let testArgFile = TestArgFile(
144
            entries: [
145
                TestArgFileEntry(
146
                    buildArtifacts: buildArtifacts,
147
                    developerDir: TestArgFileDefaultValues.developerDir,
148
                    environment: environment,
149
                    userInsertedLibraries: [],
150
                    numberOfRetries: numberOfRetries,
151
                    testRetryMode: TestArgFileDefaultValues.testRetryMode,
152
                    logCapturingMode: TestArgFileDefaultValues.logCapturingMode,
153
                    runnerWasteCleanupPolicy: TestArgFileDefaultValues.runnerWasteCleanupPolicy,
154
                    pluginLocations: [],
155
                    scheduleStrategy: TestArgFileDefaultValues.scheduleStrategy,
156
                    simulatorOperationTimeouts: TestArgFileDefaultValues.simulatorOperationTimeouts,
157
                    simulatorSettings: SimulatorSettings(
158
                        simulatorLocalizationSettings: TestArgFileDefaultValues.simulatorLocalizationSettings
159
                            .with(localeIdentifier: simulatorLocale)
160
                            .with(languages: simulatorLanguages)
161
                            .with(keyboards: simulatorKeyboards)
162
                            .with(passcodeKeyboards: passcodeKeyboards),
163
                        simulatorKeychainSettings: TestArgFileDefaultValues.simulatorSettings.simulatorKeychainSettings,
164
                        watchdogSettings: TestArgFileDefaultValues.simulatorSettings.watchdogSettings
165
                    ),
166
                    testDestination: testDestination,
167
                    testTimeoutConfiguration: TestTimeoutConfiguration(singleTestMaximumDuration: testTimeout, testRunnerMaximumSilenceDuration: testTimeout),
168
                    testAttachmentLifetime: TestArgFileDefaultValues.testAttachmentLifetime,
169
                    testsToRun: testsToRun,
170
                    workerCapabilityRequirements: TestArgFileDefaultValues.workerCapabilityRequirements
171
                )
172
            ],
173
            prioritizedJob: PrioritizedJob(
174
                analyticsConfiguration: AnalyticsConfiguration(),
175
                jobGroupId: JobGroupId(uniqueIdentifierGenerator.generate()),
176
                jobGroupPriority: .medium,
177
                jobId: JobId("autoJobId_" + uniqueIdentifierGenerator.generate()),
178
                jobPriority: .medium
179
            ),
180
            testDestinationConfigurations: []
181
        )
182
        
183
        let queueServerConfiguration = QueueServerConfiguration(
184
            globalAnalyticsConfiguration: AnalyticsConfiguration(),
185
            checkAgainTimeInterval: QueueServerConfigurationDefaultValues.checkAgainTimeInterval,
186
            queueServerDeploymentDestinations: queueDeploymentDestinations,
187
            queueServerTerminationPolicy: QueueServerConfigurationDefaultValues.queueServerTerminationPolicy,
188
            workerDeploymentDestinations: workerDeploymentDestinations,
189
            defaultWorkerSpecificConfiguration: WorkerSpecificConfigurationDefaultValues.defaultWorkerConfiguration,
190
            workerStartMode: QueueServerConfigurationDefaultValues.workerStartMode,
191
            useOnlyIPv4: QueueServerConfigurationDefaultValues.useOnlyIPv4,
192
            portRange: QueueServerConfigurationDefaultValues.defaultQueuePortRange
193
        )
194
        
195
        try RunTestsOnRemoteQueueLogic(di: di).run(
196
            commonReportOutput: ReportOutput(
197
                junit: try payload.optionalSingleTypedValue(argumentName: ArgumentDescriptions.junit.name),
198
                tracingReport: try payload.optionalSingleTypedValue(argumentName: ArgumentDescriptions.trace.name)
199
            ),
200
            emceeVersion: EmceeVersion.version,
201
            hostname: hostname,
202
            logger: try di.get(),
203
            queueServerConfiguration: queueServerConfiguration,
204
            remoteCacheConfig: nil,
205
            tempFolder: try TemporaryFolder(),
206
            testArgFile: testArgFile,
207
            httpRestServer: httpRestServer
208
        )
209
    }
210
}
211

212
extension TestName: ParsableArgument {
213
    public init(argumentValue: String) throws {
214
        self = try Self.createFromTestNameString(stringValue: argumentValue)
215
    }
216
}
217

218
extension Double: ParsableArgument {
219
    public init(argumentValue: String) throws {
220
        guard let value = Self(argumentValue) else {
221
            throw GenericParseError<Self>(argumentValue: argumentValue)
222
        }
223
        self = value
224
    }
225
}
226

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.