Merge pull request #6999 from akyrtzi/index-kinds-test-subclass
[index] Mark unit test methods if the class subclasses XCTestCase.
diff --git a/lib/Index/Index.cpp b/lib/Index/Index.cpp
index 79f6b7e..054424e 100644
--- a/lib/Index/Index.cpp
+++ b/lib/Index/Index.cpp
@@ -860,7 +860,19 @@
return Ty->getAnyNominal();
}
-static bool isTestCandidate(ValueDecl *D) {
+/// \returns true if \c D is a subclass of 'XCTestCase'.
+static bool isUnitTestCase(const ClassDecl *D) {
+ if (!D)
+ return false;
+ while (auto *SuperD = D->getSuperclassDecl()) {
+ if (SuperD->getNameStr() == "XCTestCase")
+ return true;
+ D = SuperD;
+ }
+ return false;
+}
+
+static bool isUnitTest(ValueDecl *D) {
if (!D->hasName())
return false;
@@ -872,12 +884,14 @@
if (!D->isInstanceMember())
return false;
- // 2. ...on a class or extension (not a struct)...
+ // 2. ...on a class or extension (not a struct) subclass of XCTestCase...
auto parentNTD = getNominalParent(D);
if (!parentNTD)
return false;
if (!isa<ClassDecl>(parentNTD))
return false;
+ if (!isUnitTestCase(cast<ClassDecl>(parentNTD)))
+ return false;
// 3. ...that returns void...
Type RetTy = FD->getResultInterfaceType();
@@ -909,7 +923,7 @@
if (initIndexSymbol(D, D->getLoc(), /*IsRef=*/false, Info))
return true;
- if (isTestCandidate(D))
+ if (isUnitTest(D))
Info.symInfo.Properties |= SymbolProperty::UnitTest;
if (auto Group = D->getGroupName())
diff --git a/test/Index/kinds.swift b/test/Index/kinds.swift
index 5f966d2..9a1f18c 100644
--- a/test/Index/kinds.swift
+++ b/test/Index/kinds.swift
@@ -175,5 +175,24 @@
func +(a: AStruct, b: AStruct) -> AStruct { return a }
// CHECK: [[@LINE-1]]:6 | function/infix-operator/Swift | +(_:_:) | s:F14swift_ide_testoi1pFTVS_7AStructS0__S0_ | Def | rel: 0
-
-// TODO: UnitTest
+class XCTestCase {}
+class MyTestCase : XCTestCase {
+ func testMe() {}
+ // CHECK: [[@LINE-1]]:8 | instance-method(test)/Swift | testMe() |
+ func testResult() -> Int? { return nil }
+ // CHECK: [[@LINE-1]]:8 | instance-method/Swift | testResult() |
+ func test(withInt: Int) {}
+ // CHECK: [[@LINE-1]]:8 | instance-method/Swift | test(withInt:) |
+}
+class SubTestCase : MyTestCase {
+ func testIt2() {}
+ // CHECK: [[@LINE-1]]:8 | instance-method(test)/Swift | testIt2() |
+}
+extension SubTestCase {
+ func testIt3() {}
+ // CHECK: [[@LINE-1]]:8 | instance-method(test)/Swift | testIt3() |
+}
+class NonTestCase {
+ func testMeNot() {}
+ // CHECK: [[@LINE-1]]:8 | instance-method/Swift | testMeNot() |
+}
diff --git a/test/SourceKit/Indexing/index.swift b/test/SourceKit/Indexing/index.swift
index bc37807..8f08060 100644
--- a/test/SourceKit/Indexing/index.swift
+++ b/test/SourceKit/Indexing/index.swift
@@ -130,22 +130,6 @@
s.sfoo()
}
-// Test candidates.
-struct S3 {
- func test() {} // no.
-}
-protocol P2 {
- func test() // no.
-}
-class CC3 {
- func meth() {} // no.
- class func test1() {} // no.
- func test2() {} // yes.
-}
-extension CC3 {
- func test3() {} // yes.
-}
-
extension Undeclared {
func meth() {}
}
diff --git a/test/SourceKit/Indexing/index.swift.response b/test/SourceKit/Indexing/index.swift.response
index 47bfa66..1663765 100644
--- a/test/SourceKit/Indexing/index.swift.response
+++ b/test/SourceKit/Indexing/index.swift.response
@@ -1108,125 +1108,38 @@
]
},
{
- key.kind: source.lang.swift.decl.struct,
- key.name: "S3",
- key.usr: "s:V5index2S3",
- key.line: 134,
- key.column: 8,
- key.entities: [
- {
- key.kind: source.lang.swift.decl.function.method.instance,
- key.name: "test()",
- key.usr: "s:FV5index2S34testFT_T_",
- key.line: 135,
- key.column: 8
- }
- ]
- },
- {
- key.kind: source.lang.swift.decl.protocol,
- key.name: "P2",
- key.usr: "s:P5index2P2",
- key.line: 137,
- key.column: 10,
- key.entities: [
- {
- key.kind: source.lang.swift.decl.function.method.instance,
- key.name: "test()",
- key.usr: "s:FP5index2P24testFT_T_",
- key.line: 138,
- key.column: 8
- }
- ]
- },
- {
- key.kind: source.lang.swift.decl.class,
- key.name: "CC3",
- key.usr: "s:C5index3CC3",
- key.line: 140,
- key.column: 7,
- key.entities: [
- {
- key.kind: source.lang.swift.decl.function.method.instance,
- key.name: "meth()",
- key.usr: "s:FC5index3CC34methFT_T_",
- key.line: 141,
- key.column: 8
- },
- {
- key.kind: source.lang.swift.decl.function.method.class,
- key.name: "test1()",
- key.usr: "s:ZFC5index3CC35test1FT_T_",
- key.line: 142,
- key.column: 14
- },
- {
- key.kind: source.lang.swift.decl.function.method.instance,
- key.name: "test2()",
- key.usr: "s:FC5index3CC35test2FT_T_",
- key.line: 143,
- key.column: 8,
- key.is_test_candidate: 1
- }
- ]
- },
- {
- key.kind: source.lang.swift.decl.extension.class,
- key.name: "CC3",
- key.usr: "s:C5index3CC3",
- key.line: 145,
- key.column: 11,
- key.entities: [
- {
- key.kind: source.lang.swift.ref.class,
- key.name: "CC3",
- key.usr: "s:C5index3CC3",
- key.line: 145,
- key.column: 11
- },
- {
- key.kind: source.lang.swift.decl.function.method.instance,
- key.name: "test3()",
- key.usr: "s:FC5index3CC35test3FT_T_",
- key.line: 146,
- key.column: 8,
- key.is_test_candidate: 1
- }
- ]
- },
- {
key.kind: source.lang.swift.decl.function.method.instance,
key.name: "meth()",
key.usr: "s:F5index4methERR",
- key.line: 150,
+ key.line: 134,
key.column: 8
},
{
key.kind: source.lang.swift.decl.class,
key.name: "CC4",
key.usr: "s:C5index3CC4",
- key.line: 153,
+ key.line: 137,
key.column: 7,
key.entities: [
{
key.kind: source.lang.swift.decl.function.constructor,
key.name: "init(x:)",
key.usr: "s:FC5index3CC4cFT1xSi_S0_",
- key.line: 154,
+ key.line: 138,
key.column: 15,
key.entities: [
{
key.kind: source.lang.swift.ref.struct,
key.name: "Int",
key.usr: "s:Si",
- key.line: 154,
+ key.line: 138,
key.column: 23
},
{
key.kind: source.lang.swift.ref.function.constructor,
key.name: "init(x:)",
key.usr: "s:FC5index3CC4cFT1xSi_S0_",
- key.line: 155,
+ key.line: 139,
key.column: 10,
key.receiver_usr: "s:C5index3CC4",
key.is_dynamic: 1
@@ -1244,14 +1157,14 @@
key.kind: source.lang.swift.decl.class,
key.name: "SubCC4",
key.usr: "s:C5index6SubCC4",
- key.line: 159,
+ key.line: 143,
key.column: 7,
key.related: [
{
key.kind: source.lang.swift.ref.class,
key.name: "CC4",
key.usr: "s:C5index3CC4",
- key.line: 159,
+ key.line: 143,
key.column: 16
}
],
@@ -1260,14 +1173,14 @@
key.kind: source.lang.swift.ref.class,
key.name: "CC4",
key.usr: "s:C5index3CC4",
- key.line: 159,
+ key.line: 143,
key.column: 16
},
{
key.kind: source.lang.swift.decl.function.constructor,
key.name: "init(x:)",
key.usr: "s:FC5index6SubCC4cFT1xSi_S0_",
- key.line: 160,
+ key.line: 144,
key.column: 3,
key.related: [
{
@@ -1281,14 +1194,14 @@
key.kind: source.lang.swift.ref.struct,
key.name: "Int",
key.usr: "s:Si",
- key.line: 160,
+ key.line: 144,
key.column: 11
},
{
key.kind: source.lang.swift.ref.function.constructor,
key.name: "init(x:)",
key.usr: "s:FC5index3CC4cFT1xSi_S0_",
- key.line: 161,
+ key.line: 145,
key.column: 11,
key.receiver_usr: "s:C5index3CC4"
}
@@ -1300,35 +1213,35 @@
key.kind: source.lang.swift.decl.class,
key.name: "Observing",
key.usr: "s:C5index9Observing",
- key.line: 165,
+ key.line: 149,
key.column: 7,
key.entities: [
{
key.kind: source.lang.swift.decl.function.constructor,
key.name: "init()",
key.usr: "s:FC5index9ObservingcFT_S0_",
- key.line: 166,
+ key.line: 150,
key.column: 3
},
{
key.kind: source.lang.swift.decl.var.instance,
key.name: "globObserving",
key.usr: "s:vC5index9Observing13globObservingSi",
- key.line: 167,
+ key.line: 151,
key.column: 7,
key.entities: [
{
key.kind: source.lang.swift.decl.function.accessor.willset,
key.name: "willSet:globObserving",
key.usr: "s:FC5index9Observingw13globObservingSi",
- key.line: 168,
+ key.line: 152,
key.column: 5,
key.entities: [
{
key.kind: source.lang.swift.ref.function.free,
key.name: "test2()",
key.usr: "s:F5index5test2FT_T_",
- key.line: 169,
+ key.line: 153,
key.column: 7
}
],
@@ -1342,14 +1255,14 @@
key.kind: source.lang.swift.decl.function.accessor.didset,
key.name: "didSet:globObserving",
key.usr: "s:FC5index9ObservingW13globObservingSi",
- key.line: 171,
+ key.line: 155,
key.column: 5,
key.entities: [
{
key.kind: source.lang.swift.ref.function.free,
key.name: "test2()",
key.usr: "s:F5index5test2FT_T_",
- key.line: 172,
+ key.line: 156,
key.column: 7
}
],
@@ -1365,7 +1278,7 @@
key.kind: source.lang.swift.ref.struct,
key.name: "Int",
key.usr: "s:Si",
- key.line: 167,
+ key.line: 151,
key.column: 23
}
]
@@ -1374,21 +1287,21 @@
key.kind: source.lang.swift.decl.class,
key.name: "rdar18640140",
key.usr: "s:C5index12rdar18640140",
- key.line: 178,
+ key.line: 162,
key.column: 7,
key.entities: [
{
key.kind: source.lang.swift.decl.var.instance,
key.name: "S1",
key.usr: "s:vC5index12rdar186401402S1Si",
- key.line: 180,
+ key.line: 164,
key.column: 7,
key.entities: [
{
key.kind: source.lang.swift.decl.function.accessor.didset,
key.name: "didSet:S1",
key.usr: "s:FC5index12rdar18640140W2S1Si",
- key.line: 186,
+ key.line: 170,
key.column: 5,
key.attributes: [
{
@@ -1402,7 +1315,7 @@
key.kind: source.lang.swift.ref.struct,
key.name: "Int",
key.usr: "s:Si",
- key.line: 180,
+ key.line: 164,
key.column: 11
}
]
@@ -1411,28 +1324,28 @@
key.kind: source.lang.swift.decl.protocol,
key.name: "rdar18640140Protocol",
key.usr: "s:P5index20rdar18640140Protocol",
- key.line: 191,
+ key.line: 175,
key.column: 10,
key.entities: [
{
key.kind: source.lang.swift.decl.var.instance,
key.name: "S1",
key.usr: "s:vP5index20rdar18640140Protocol2S1Si",
- key.line: 192,
+ key.line: 176,
key.column: 7,
key.entities: [
{
key.kind: source.lang.swift.decl.function.accessor.getter,
key.name: "getter:S1",
key.usr: "s:FP5index20rdar18640140Protocolg2S1Si",
- key.line: 195,
+ key.line: 179,
key.column: 5
},
{
key.kind: source.lang.swift.decl.function.accessor.setter,
key.name: "setter:S1",
key.usr: "s:FP5index20rdar18640140Protocols2S1Si",
- key.line: 194,
+ key.line: 178,
key.column: 5
}
]
@@ -1441,7 +1354,7 @@
key.kind: source.lang.swift.ref.struct,
key.name: "Int",
key.usr: "s:Si",
- key.line: 192,
+ key.line: 176,
key.column: 11
}
]
@@ -1450,7 +1363,7 @@
key.kind: source.lang.swift.decl.class,
key.name: "ConditionalUnavailableClass1",
key.usr: "s:C5index28ConditionalUnavailableClass1",
- key.line: 204,
+ key.line: 188,
key.column: 7,
key.attributes: [
{
@@ -1462,7 +1375,7 @@
key.kind: source.lang.swift.decl.class,
key.name: "ConditionalUnavailableClass2",
key.usr: "s:C5index28ConditionalUnavailableClass2",
- key.line: 208,
+ key.line: 192,
key.column: 7,
key.attributes: [
{
diff --git a/test/SourceKit/Indexing/index_is_test_candidate.swift b/test/SourceKit/Indexing/index_is_test_candidate.swift
index 3d0d9f6..f919927 100644
--- a/test/SourceKit/Indexing/index_is_test_candidate.swift
+++ b/test/SourceKit/Indexing/index_is_test_candidate.swift
@@ -12,12 +12,12 @@
struct MyStruct {
func test_startsWithTest_takesNoParams_returnsVoid_butIsDefinedOnAStruct() {}
}
-
-private class MyPrivateClass {
+class XCTestCase {}
+private class MyPrivateClass : XCTestCase {
func test_startsWithTest_takesNoParams_returnsVoid_butIsPrivate() {}
}
-public class MyClass {
+public class MyClass : XCTestCase {
func doesNotStartWithTest() {}
func test_startsWithTest_butTakesAParam(param: Int) {}
func test_startsWithTest_andTakesNoParams_butReturnsNonVoid() -> Int {}
diff --git a/test/SourceKit/Indexing/index_is_test_candidate.swift.response b/test/SourceKit/Indexing/index_is_test_candidate.swift.response
index c761e49..0f8335c 100644
--- a/test/SourceKit/Indexing/index_is_test_candidate.swift.response
+++ b/test/SourceKit/Indexing/index_is_test_candidate.swift.response
@@ -35,12 +35,35 @@
},
{
key.kind: source.lang.swift.decl.class,
+ key.name: "XCTestCase",
+ key.usr: "s:C28index_is_test_candidate_objc10XCTestCase",
+ key.line: 15,
+ key.column: 7
+ },
+ {
+ key.kind: source.lang.swift.decl.class,
key.name: "MyPrivateClass",
key.usr: "s:C23index_is_test_candidateP33_E06F4E7BC5F577AB6E2EC6D3ECA1C8B914MyPrivateClass",
key.line: 16,
key.column: 15,
+ key.related: [
+ {
+ key.kind: source.lang.swift.ref.class,
+ key.name: "XCTestCase",
+ key.usr: "s:C28index_is_test_candidate_objc10XCTestCase",
+ key.line: 16,
+ key.column: 32
+ }
+ ],
key.entities: [
{
+ key.kind: source.lang.swift.ref.class,
+ key.name: "XCTestCase",
+ key.usr: "s:C28index_is_test_candidate_objc10XCTestCase",
+ key.line: 16,
+ key.column: 32
+ },
+ {
key.kind: source.lang.swift.decl.function.method.instance,
key.name: "test_startsWithTest_takesNoParams_returnsVoid_butIsPrivate()",
key.usr: "s:FC23index_is_test_candidateP33_E06F4E7BC5F577AB6E2EC6D3ECA1C8B914MyPrivateClass58test_startsWithTest_takesNoParams_returnsVoid_butIsPrivateFT_T_",
@@ -55,8 +78,24 @@
key.usr: "s:C23index_is_test_candidate7MyClass",
key.line: 20,
key.column: 14,
+ key.related: [
+ {
+ key.kind: source.lang.swift.ref.class,
+ key.name: "XCTestCase",
+ key.usr: "s:C28index_is_test_candidate_objc10XCTestCase",
+ key.line: 20,
+ key.column: 24
+ }
+ ],
key.entities: [
{
+ key.kind: source.lang.swift.ref.class,
+ key.name: "XCTestCase",
+ key.usr: "s:C28index_is_test_candidate_objc10XCTestCase",
+ key.line: 20,
+ key.column: 24
+ },
+ {
key.kind: source.lang.swift.decl.function.method.instance,
key.name: "doesNotStartWithTest()",
key.usr: "s:FC23index_is_test_candidate7MyClass20doesNotStartWithTestFT_T_",
diff --git a/test/SourceKit/Indexing/index_is_test_candidate_objc.swift b/test/SourceKit/Indexing/index_is_test_candidate_objc.swift
index cfb74af..23b7766 100644
--- a/test/SourceKit/Indexing/index_is_test_candidate_objc.swift
+++ b/test/SourceKit/Indexing/index_is_test_candidate_objc.swift
@@ -11,12 +11,12 @@
struct MyStruct {
func test_startsWithTest_takesNoParams_returnsVoid_butIsDefinedOnAStruct() {}
}
-
-private class MyPrivateClass {
+class XCTestCase {}
+private class MyPrivateClass : XCTestCase {
func test_startsWithTest_takesNoParams_returnsVoid_andIsPrivate() {}
}
-public class MyClass {
+public class MyClass : XCTestCase {
func doesNotStartWithTest() {}
func test_startsWithTest_butTakesAParam(param: Int) {}
func test_startsWithTest_andTakesNoParams_butReturnsNonVoid() -> Int {}
diff --git a/test/SourceKit/Indexing/index_is_test_candidate_objc.swift.response b/test/SourceKit/Indexing/index_is_test_candidate_objc.swift.response
index e953402..9dfb1ee 100644
--- a/test/SourceKit/Indexing/index_is_test_candidate_objc.swift.response
+++ b/test/SourceKit/Indexing/index_is_test_candidate_objc.swift.response
@@ -35,12 +35,35 @@
},
{
key.kind: source.lang.swift.decl.class,
+ key.name: "XCTestCase",
+ key.usr: "s:C28index_is_test_candidate_objc10XCTestCase",
+ key.line: 14,
+ key.column: 7
+ },
+ {
+ key.kind: source.lang.swift.decl.class,
key.name: "MyPrivateClass",
key.usr: "s:C28index_is_test_candidate_objcP33_32FED72643814BE1A523406CD2E729AA14MyPrivateClass",
key.line: 15,
key.column: 15,
+ key.related: [
+ {
+ key.kind: source.lang.swift.ref.class,
+ key.name: "XCTestCase",
+ key.usr: "s:C28index_is_test_candidate_objc10XCTestCase",
+ key.line: 15,
+ key.column: 32
+ }
+ ],
key.entities: [
{
+ key.kind: source.lang.swift.ref.class,
+ key.name: "XCTestCase",
+ key.usr: "s:C28index_is_test_candidate_objc10XCTestCase",
+ key.line: 15,
+ key.column: 32
+ },
+ {
key.kind: source.lang.swift.decl.function.method.instance,
key.name: "test_startsWithTest_takesNoParams_returnsVoid_andIsPrivate()",
key.usr: "s:FC28index_is_test_candidate_objcP33_32FED72643814BE1A523406CD2E729AA14MyPrivateClass58test_startsWithTest_takesNoParams_returnsVoid_andIsPrivateFT_T_",
@@ -56,8 +79,24 @@
key.usr: "s:C28index_is_test_candidate_objc7MyClass",
key.line: 19,
key.column: 14,
+ key.related: [
+ {
+ key.kind: source.lang.swift.ref.class,
+ key.name: "XCTestCase",
+ key.usr: "s:C28index_is_test_candidate_objc10XCTestCase",
+ key.line: 19,
+ key.column: 24
+ }
+ ],
key.entities: [
{
+ key.kind: source.lang.swift.ref.class,
+ key.name: "XCTestCase",
+ key.usr: "s:C28index_is_test_candidate_objc10XCTestCase",
+ key.line: 19,
+ key.column: 24
+ },
+ {
key.kind: source.lang.swift.decl.function.method.instance,
key.name: "doesNotStartWithTest()",
key.usr: "s:FC28index_is_test_candidate_objc7MyClass20doesNotStartWithTestFT_T_",