File: TestSources.swift

package info (click to toggle)
swiftlang 6.1.3-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 2,791,532 kB
  • sloc: cpp: 9,901,743; ansic: 2,201,431; asm: 1,091,827; python: 308,252; objc: 82,166; f90: 80,126; lisp: 38,358; pascal: 25,559; sh: 20,429; ml: 5,058; perl: 4,745; makefile: 4,484; awk: 3,535; javascript: 3,018; xml: 918; fortran: 664; cs: 573; ruby: 396
file content (102 lines) | stat: -rw-r--r-- 3,593 bytes parent folder | download | duplicates (2)
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
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2019 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

/// Test helper for looking up source location mappings and contents of source files in a project,
/// as well as managing modifications to the sources.
public final class TestSources {

  /// The root source directory.
  public var rootDirectory: URL

  /// The source contents.
  public let sourceCache: SourceFileCache = SourceFileCache()

  /// The map of known source locations.
  public var locations: [String: TestLocation]

  public init(rootDirectory: URL) throws {
    self.rootDirectory = rootDirectory
    self.locations = try scanLocations(rootDirectory: rootDirectory, sourceCache: sourceCache)
  }

  public struct ChangeSet {
    public var remove: [URL] = []
    public var rename: [(URL, URL)] = []
    public var write: [(URL, String)] = []

    public func isDirty(_ url: URL) -> Bool {
      return remove.contains(url)
        || rename.contains { $0 == url || $1 == url }
        || write.contains { $0.0 == url }
    }
  }

  public struct ChangeBuilder {
    public var changes: ChangeSet = ChangeSet()
    var seen: Set<URL> = []

    public mutating func write(_ content: String, to url: URL) {
      precondition(seen.insert(url).inserted, "multiple edits to same file")
      changes.write.append((url, content))
    }
    public mutating func remove(_ url: URL) {
      precondition(seen.insert(url).inserted, "multiple edits to same file")
      changes.remove.append(url)
    }
    public mutating func rename(from: URL, to: URL) {
      precondition(seen.insert(from).inserted && seen.insert(to).inserted, "multiple edits to same file")
      changes.rename.append((from, to))
    }
  }

  public func apply(_ changes: ChangeSet) throws {
    for (url, content) in changes.write {
      guard let data = content.data(using: .utf8) else {
        fatalError("failed to encode edited contents to utf8")
      }
      try data.write(to: url)
      sourceCache.set(url, to: content)
    }

    let fm = FileManager.default
    for (from, to) in changes.rename {
      try fm.moveItem(at: from, to: to)
      sourceCache.set(to, to: sourceCache.cache[from])
      sourceCache.set(from, to: nil)
    }

    for url in changes.remove {
      try fm.removeItem(at: url)
      sourceCache.set(url, to: nil)
    }

    // FIXME: update incrementally without rescanning everything.
    locations = try scanLocations(rootDirectory: rootDirectory, sourceCache: sourceCache)
  }

  /// Perform a set of edits (modifications, removals, renames) to the sources and return the
  /// change set. This modifies the files on disk.
  ///
  /// * parameters:
  ///   * block: Callback to collect the desired changes.
  /// * returns: The ChangeSet corresponding to these changes.
  /// * throws: Any file system errors seen while modifying the sources. If this happens, the state
  ///   of the source files is not defined.
  public func edit(_ block: (_ builder: inout ChangeBuilder) throws -> ()) throws -> ChangeSet {
    var builder = ChangeBuilder()
    try block(&builder)
    try apply(builder.changes)
    return builder.changes
  }
}