File: StressTestOperation.swift

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (158 lines) | stat: -rw-r--r-- 5,392 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

import Foundation
import Common

struct ParsedMessages {
  fileprivate(set) var sourceKitErrors: [SourceKitError] = []
  fileprivate(set) var sourceKitResponses: [SourceKitResponseData] = []
}

final class StressTestOperation: Operation {
  enum Status {
    /// Indicates the operation is still pending
    case unexecuted
    /// Indicates the operation was cancelled
    case cancelled
    /// Indicates the operation was executed and no issues were found
    case passed
    /// Indicates the operation was executed and issues were found
    case failed(sourceKitError: [SourceKitError])
    /// Indicates the operation was executed, but the stress tester itself failed
    case errored(status: Int32)

    var name: String {
      switch self {
      case .unexecuted:
        return "unexecuted"
      case .cancelled:
        return "cancelled"
      case .passed:
        return "passed"
      case .failed:
        return "failed"
      case .errored:
        return "errored"
      }
    }

    var isPassed: Bool {
      if case .passed = self {
        return true
      }
      return false
    }
  }

  let file: String
  let args: [String]
  var status: Status = .unexecuted
  var responses = [SourceKitResponseData]()

  private let part: (Int, of: Int)
  private let mode: RewriteMode
  private let process: ProcessRunner

  init(file: String, rewriteMode: RewriteMode, requests: Set<RequestKind>,
       conformingMethodTypes: [String]?, limit: Int?, part: (Int, of: Int),
       offsetFilter: Int?,
       reportResponses: Bool, compilerArgs: [String], executable: String,
       swiftc: String, extraCodeCompleteOptions: [String],
       requestDurationsOutputFile: URL?) {
    var stressTesterArgs = ["--format", "json", "--rewrite-mode", rewriteMode.rawValue]
    if let offsetFilter = offsetFilter {
      stressTesterArgs += ["--offset-filter", String(offsetFilter)]
    } else {
      stressTesterArgs += ["--page", "\(part.0)/\(part.of)"]
    }
    if let limit = limit {
      stressTesterArgs += ["--limit", String(limit)]
    }
    if let requestDurationsOutputFile = requestDurationsOutputFile {
      stressTesterArgs += ["--request-durations-output-file", requestDurationsOutputFile.path]
    }
    stressTesterArgs += requests.flatMap { ["--request", $0.rawValue] }
    if let types = conformingMethodTypes {
      stressTesterArgs += types.flatMap { ["--type-list-item", $0] }
    }
    if reportResponses {
      stressTesterArgs += ["--report-responses"]
    }
    stressTesterArgs += extraCodeCompleteOptions.flatMap { ["--extra-code-complete-options", $0] }
    stressTesterArgs += ["--swiftc", swiftc]
    stressTesterArgs.append(file)
    stressTesterArgs.append("--")
    stressTesterArgs.append(contentsOf: compilerArgs)

    self.file = file
    self.args = stressTesterArgs
    self.part = part
    self.mode = rewriteMode
    self.process = ProcessRunner(launchPath: executable,
                                 arguments: stressTesterArgs)
  }

  var summary: String {
    return "rewrite \(mode.rawValue) \(part.0)/\(part.of)"
  }

  override func main() {
    guard !isCancelled else {
      status = .cancelled
      return
    }

    let result = process.run()
    if isCancelled {
      status = .cancelled
    } else if let parsed = parseMessages(result.stdout) {
      if !parsed.sourceKitErrors.isEmpty {
        status = .failed(sourceKitError: parsed.sourceKitErrors)
        self.responses = parsed.sourceKitResponses
      } else if result.status == EXIT_SUCCESS {
        status = .passed
        self.responses = parsed.sourceKitResponses
      } else {
        // Non-empty unparseable output -> treat this as a stress tester failure
        status = .errored(status: result.status)
      }
    } else {
      // Non-empty unparseable output -> treat this as a stress tester failure
      status = .errored(status: result.status)
    }
  }

  /// Parses the given data as a sequence of newline-separated, JSON-encoded `StressTesterMessage`s.
  ///
  /// - returns: A tuple of the detected `SourceKitError` (if one was produced) and a possibly-empty list of `SourceKitReponseData`s. If the input data was non-empty and couldn't be parsed, or if more than one detected error was produced, returns nil.
  private func parseMessages(_ data: Data) -> ParsedMessages? {
    let terminator = UInt8(ascii: "\n")
    var parsed = ParsedMessages()

    for data in data.split(separator: terminator, omittingEmptySubsequences: true) {
      guard let message = StressTesterMessage(from: data) else { return nil }
      switch message {
      case .detected(let error):
        parsed.sourceKitErrors.append(error)
      case .produced(let responseData):
        parsed.sourceKitResponses.append(responseData)
      }
    }
    return parsed
  }

  override func cancel() {
    super.cancel()
    process.terminate()
  }
}