1*da0073e9SAndroid Build Coastguard Worker# How to write tests using FileCheck 2*da0073e9SAndroid Build Coastguard Worker 3*da0073e9SAndroid Build Coastguard Worker## What is FileCheck 4*da0073e9SAndroid Build Coastguard Worker 5*da0073e9SAndroid Build Coastguard WorkerFileCheck can be seen as an advanced version of grep. We use it for writing 6*da0073e9SAndroid Build Coastguard Workersmall annotated unit tests for optimization passes. FileCheck used in PyTorch is 7*da0073e9SAndroid Build Coastguard Workerinspired by [LLVM FileCheck 8*da0073e9SAndroid Build Coastguard WorkerTool](https://llvm.org/docs/CommandGuide/FileCheck.html), but is not the same. 9*da0073e9SAndroid Build Coastguard WorkerFileCheck is available for writing both C++ and python tests. 10*da0073e9SAndroid Build Coastguard Worker 11*da0073e9SAndroid Build Coastguard Worker## How does it work 12*da0073e9SAndroid Build Coastguard Worker 13*da0073e9SAndroid Build Coastguard WorkerLet's look at a test written with FileCheck. The following test verifies that 14*da0073e9SAndroid Build Coastguard WorkerCSE pass removes one out of two similar `aten::mul` nodes. Here is how the test 15*da0073e9SAndroid Build Coastguard Workerlooks like: 16*da0073e9SAndroid Build Coastguard Worker 17*da0073e9SAndroid Build Coastguard Worker```python 18*da0073e9SAndroid Build Coastguard Workerdef test_cse(): 19*da0073e9SAndroid Build Coastguard Worker input_str = """graph(%a : Tensor, %b : Tensor): 20*da0073e9SAndroid Build Coastguard Worker # CHECK: aten::mul 21*da0073e9SAndroid Build Coastguard Worker %x : Tensor = aten::mul(%a, %b) 22*da0073e9SAndroid Build Coastguard Worker # Check that the second aten::mul is removed by CSE. 23*da0073e9SAndroid Build Coastguard Worker # CHECK-NOT: aten::mul 24*da0073e9SAndroid Build Coastguard Worker %y : Tensor = aten::mul(%a, %b) 25*da0073e9SAndroid Build Coastguard Worker # CHECK: return 26*da0073e9SAndroid Build Coastguard Worker return (%x, %y) 27*da0073e9SAndroid Build Coastguard Worker """ 28*da0073e9SAndroid Build Coastguard Worker parsed = parse_ir(input_str) 29*da0073e9SAndroid Build Coastguard Worker optimized = run_cse(parsed) 30*da0073e9SAndroid Build Coastguard Worker FileCheck().run(input_str, optimized) 31*da0073e9SAndroid Build Coastguard Worker``` 32*da0073e9SAndroid Build Coastguard Worker 33*da0073e9SAndroid Build Coastguard WorkerLet's look in detail at how it works. First, the input string is parsed by 34*da0073e9SAndroid Build Coastguard Worker`parse_ir`. At that stage all annotations are ignored since they are written in 35*da0073e9SAndroid Build Coastguard Workercomments, so this is what parser essentially sees: 36*da0073e9SAndroid Build Coastguard Worker 37*da0073e9SAndroid Build Coastguard Worker``` 38*da0073e9SAndroid Build Coastguard Workergraph(%a : Tensor, %b : Tensor): 39*da0073e9SAndroid Build Coastguard Worker %x : Tensor = aten::mul(%a, %b) 40*da0073e9SAndroid Build Coastguard Worker %y : Tensor = aten::mul(%a, %b) 41*da0073e9SAndroid Build Coastguard Worker return (%x, %y) 42*da0073e9SAndroid Build Coastguard Worker``` 43*da0073e9SAndroid Build Coastguard Worker 44*da0073e9SAndroid Build Coastguard WorkerWe then run CSE on the parsed IR and expect it to remove the second `aten::mul`, 45*da0073e9SAndroid Build Coastguard Workerwhich is redundant. After CSE our IR looks like this: 46*da0073e9SAndroid Build Coastguard Worker 47*da0073e9SAndroid Build Coastguard Worker``` 48*da0073e9SAndroid Build Coastguard Workergraph(%a : Tensor, %b : Tensor): 49*da0073e9SAndroid Build Coastguard Worker %x : Tensor = aten::mul(%a, %b) 50*da0073e9SAndroid Build Coastguard Worker return (%x, %x) 51*da0073e9SAndroid Build Coastguard Worker``` 52*da0073e9SAndroid Build Coastguard Worker 53*da0073e9SAndroid Build Coastguard WorkerAnd now we run `FileCheck` passing to it both original input string and the 54*da0073e9SAndroid Build Coastguard Workeroptimized IR. From the input string `FileCheck` ignores everything except `# 55*da0073e9SAndroid Build Coastguard WorkerCHECK` pragmas and essentially it sees the input string like this: 56*da0073e9SAndroid Build Coastguard Worker 57*da0073e9SAndroid Build Coastguard Worker``` 58*da0073e9SAndroid Build Coastguard Worker # CHECK: aten::mul (1) 59*da0073e9SAndroid Build Coastguard Worker # CHECK-NOT: aten::mul (2) 60*da0073e9SAndroid Build Coastguard Worker # CHECK: return (3) 61*da0073e9SAndroid Build Coastguard Worker``` 62*da0073e9SAndroid Build Coastguard Worker 63*da0073e9SAndroid Build Coastguard WorkerIt then checks that the optimized IR satisfies the specified annotations. It 64*da0073e9SAndroid Build Coastguard Workerfirst finds string `%x : Tensor = aten::mul(%a, %b)` matching the annotation (1), 65*da0073e9SAndroid Build Coastguard Workerthen it finds string `return (%x, %x)` matching the annotation (3), and since 66*da0073e9SAndroid Build Coastguard Workerthere were no lines matching `aten::mul` after the match (1) and before the 67*da0073e9SAndroid Build Coastguard Workermatch (3), the annotation (2) is also satisfied. 68*da0073e9SAndroid Build Coastguard Worker 69*da0073e9SAndroid Build Coastguard WorkerOne could also register FileCheck annotations using a builder API. To generate 70*da0073e9SAndroid Build Coastguard Workerannotations from the example above one would write: 71*da0073e9SAndroid Build Coastguard Worker```python 72*da0073e9SAndroid Build Coastguard Worker FileCheck().check("aten::mul") \ 73*da0073e9SAndroid Build Coastguard Worker .check_not("aten::mul") \ 74*da0073e9SAndroid Build Coastguard Worker .check("return") \ 75*da0073e9SAndroid Build Coastguard Worker .run(optimized) 76*da0073e9SAndroid Build Coastguard Worker``` 77*da0073e9SAndroid Build Coastguard Worker 78*da0073e9SAndroid Build Coastguard Worker## Supported pragmas 79*da0073e9SAndroid Build Coastguard Worker 80*da0073e9SAndroid Build Coastguard Worker* `CHECK: <pattern>` 81*da0073e9SAndroid Build Coastguard Worker Scans the input until `PATTERN` is found. Fails if the pattern is not found. 82*da0073e9SAndroid Build Coastguard Worker* `CHECK-NEXT: <pattern>` 83*da0073e9SAndroid Build Coastguard Worker Scans the input on the line immediately following the previous CHECK until 84*da0073e9SAndroid Build Coastguard Worker `PATTERN` is found. Fails if the pattern is not found on that line. 85*da0073e9SAndroid Build Coastguard Worker* `CHECK-NOT: <pattern>` 86*da0073e9SAndroid Build Coastguard Worker Scans the input and fails if `PATTERN` is found on any line. The scan stops when 87*da0073e9SAndroid Build Coastguard Worker a match for a next `CHECK` is found. 88*da0073e9SAndroid Build Coastguard Worker* `CHECK-SAME: <pattern>` 89*da0073e9SAndroid Build Coastguard Worker Checks that PATTERN is found in the line of the last match. 90*da0073e9SAndroid Build Coastguard Worker* `CHECK-COUNT-<num>: <pattern>` 91*da0073e9SAndroid Build Coastguard Worker Scans the input and succeeds when a line containing at least `NUM` entries of 92*da0073e9SAndroid Build Coastguard Worker `PATTERN` is found. 93*da0073e9SAndroid Build Coastguard Worker* `CHECK-COUNT-EXACTLY-<num>: <pattern>` 94*da0073e9SAndroid Build Coastguard Worker Scans the input and succeeds when a line containing exactly `NUM` entries of 95*da0073e9SAndroid Build Coastguard Worker `PATTERN` is found. 96*da0073e9SAndroid Build Coastguard Worker* `CHECK-DAG: <pattern>` 97*da0073e9SAndroid Build Coastguard Worker Works similar to the usual `CHECK` pragma, but also matches if there exists a 98*da0073e9SAndroid Build Coastguard Worker way to reorder the CHECK-DAG pragmas to satisfy all patterns. 99*da0073e9SAndroid Build Coastguard Worker For example the following pattern: 100*da0073e9SAndroid Build Coastguard Worker ``` 101*da0073e9SAndroid Build Coastguard Worker # CHECK: foo 102*da0073e9SAndroid Build Coastguard Worker # CHECK-DAG: bar 103*da0073e9SAndroid Build Coastguard Worker # CHECK-DAG: ham 104*da0073e9SAndroid Build Coastguard Worker # CHECK: end 105*da0073e9SAndroid Build Coastguard Worker ``` 106*da0073e9SAndroid Build Coastguard Worker would match the following input (note that `ham` and `bar` are swapped): 107*da0073e9SAndroid Build Coastguard Worker ``` 108*da0073e9SAndroid Build Coastguard Worker foo 109*da0073e9SAndroid Build Coastguard Worker ham 110*da0073e9SAndroid Build Coastguard Worker bar 111*da0073e9SAndroid Build Coastguard Worker end 112*da0073e9SAndroid Build Coastguard Worker ``` 113*da0073e9SAndroid Build Coastguard Worker* `CHECK-SOURCE-HIGHLIGHTED: <pattern>` 114*da0073e9SAndroid Build Coastguard Worker Check for highlighted source ranges. This is useful when writing tests regarding generated error messages that require source code highlighting. 115*da0073e9SAndroid Build Coastguard Worker For example the following pattern: 116*da0073e9SAndroid Build Coastguard Worker ``` 117*da0073e9SAndroid Build Coastguard Worker # CHECK-SOURCE-HIGHLIGHTED: raise Exception("raised exception 118*da0073e9SAndroid Build Coastguard Worker ``` 119*da0073e9SAndroid Build Coastguard Worker would match the following input: 120*da0073e9SAndroid Build Coastguard Worker ``` 121*da0073e9SAndroid Build Coastguard Worker def method_that_raises() -> torch.Tensor: 122*da0073e9SAndroid Build Coastguard Worker raise Exception("raised exception") # noqa: TRY002 123*da0073e9SAndroid Build Coastguard Worker ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ <--- HERE 124*da0073e9SAndroid Build Coastguard Worker builtins.Exception: raised exception 125*da0073e9SAndroid Build Coastguard Worker ``` 126*da0073e9SAndroid Build Coastguard Worker* `CHECK-REGEX: <pattern>` 127*da0073e9SAndroid Build Coastguard Worker Scans the input until `PATTERN` is matched, accepts RE syntax for std::regex. 128