File: IndexStoreDBTests.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 (118 lines) | stat: -rw-r--r-- 4,761 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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
//===--- IndexStoreDBTests.swift ------------------------------------------===//
//
// 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 IndexStoreDB
import ISDBTibs
import XCTest
import Foundation

let isTSanEnabled: Bool = {
  if let value = ProcessInfo.processInfo.environment["INDEXSTOREDB_ENABLED_THREAD_SANITIZER"] {
    return value == "1" || value == "YES"
  }
  return false
}()

func checkThrows(_ expected: IndexStoreDBError, file: StaticString = #file, line: UInt = #line, _ body: () throws -> ()) {
  do {
    try body()
    XCTFail("missing expected error \(expected)", file: file, line: line)

  } catch let error as IndexStoreDBError {

    switch (error, expected) {
    case (.create(let msg), .create(let expected)), (.loadIndexStore(let msg), .loadIndexStore(let expected)):
      XCTAssert(msg.hasPrefix(expected), "error \(error) does not match expected \(expected)", file: file, line: line)
    default:
      XCTFail("error \(error) does not match expected \(expected)", file: file, line: line)
    }
  } catch {
    XCTFail("error \(error) does not match expected \(expected)", file: file, line: line)
  }
}

final class IndexStoreDBTests: XCTestCase {

  var tmp: String = NSTemporaryDirectory()

  override func setUp() {
    tmp += "/indexstoredb_index_test\(getpid())"
  }

  override func tearDown() {
    _ = try? FileManager.default.removeItem(atPath: tmp)
  }

  func testErrors() {
    checkThrows(.create("failed creating directory")) {
      _ = try IndexStoreDB(storePath: "/nope:", databasePath: "/nope:", library: nil)
    }

    checkThrows(.create("could not determine indexstore library")) {
      _ = try IndexStoreDB(storePath: "\(tmp)/idx", databasePath: "\(tmp)/db", library: nil)
    }
  }

  func testCreateIndexStoreAndDBDirs() {
    let toolchain = TibsToolchain.testDefault
    let libIndexStore = try! IndexStoreLibrary(dylibPath: toolchain.libIndexStore.path)

    // Normal - create the missing directories.
    XCTAssertNoThrow(try IndexStoreDB(storePath: tmp + "/store", databasePath: tmp + "/db", library: libIndexStore))

    // Readonly - do not create.
    checkThrows(.create("failed opening database")) {
      _ = try IndexStoreDB(storePath: tmp + "/store", databasePath: tmp + "/db-readonly", library: libIndexStore, readonly: true)
    }
    // Readonly - do not create.
    checkThrows(.create("index store path does not exist")) {
      _ = try IndexStoreDB(storePath: tmp + "/store-readonly", databasePath: tmp + "/db", library: libIndexStore, readonly: true)
    }
  }

  func testSymlinkedDBPaths() throws {
    // FIXME: This test seems to trigger a false-positive in TSan.
    try XCTSkipIf(isTSanEnabled, "skipping because TSan is enabled")

    let toolchain = TibsToolchain.testDefault
    let libIndexStore = try! IndexStoreLibrary(dylibPath: toolchain.libIndexStore.path)

    // This tests against a crash that was manifesting when creating separate `IndexStoreDB` instances against 2 DB paths
    // that resolve to the same underlying directory (e.g. they were symlinked).
    // It runs a number of iterations though it was not guaranteed that the issue would hit within a specific number
    // of iterations, only that at *some* point, if you let it run indefinitely, it could trigger.
    let iterations = 100

    let indexStorePath: String
    do {
      // Don't care about specific index data, just want an index store directory containing *something*.
      guard let ws = try staticTibsTestWorkspace(name: "SingleUnit") else { return }
      try ws.buildAndIndex()
      indexStorePath =  ws.builder.indexstore.path
    }

    let fileMgr = FileManager.default
    let mainDBPath = tmp + "/db"
    let symlinkDBPath1 = tmp + "/db-link1"
    let symlinkDBPath2 = tmp + "/db-link2"
    try fileMgr.createDirectory(atPath: mainDBPath, withIntermediateDirectories: true, attributes: nil)
    try fileMgr.createSymbolicLink(atPath: symlinkDBPath1, withDestinationPath: mainDBPath)
    try fileMgr.createSymbolicLink(atPath: symlinkDBPath2, withDestinationPath: mainDBPath)

    for _ in 0..<iterations {
      DispatchQueue.concurrentPerform(iterations: 2) { idx in
        let dbPath = idx == 0 ? symlinkDBPath1 : symlinkDBPath2
        _ = try! IndexStoreDB(storePath: indexStorePath, databasePath: dbPath, library: libIndexStore, waitUntilDoneInitializing: true)
      }
    }
  }
}