-
Notifications
You must be signed in to change notification settings - Fork 4
/
locator.gleam
155 lines (139 loc) 路 3.96 KB
/
locator.gleam
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
import bigben/clock
import birl
import birl/duration.{type Duration}
import gleam/dynamic.{type Dynamic}
import gleam/list
import gleam/regex
import gleam/result.{try}
import gleam/string
import simplifile
import startest/context.{type Context}
import startest/logger
import startest/test_case.{type Test, Test}
import startest/test_tree.{type TestTree, decode_test_tree}
/// A file that contains tests.
pub type TestFile {
TestFile(
/// The name of the Gleam module.
module_name: String,
/// The filepath to the `.gleam` file.
filepath: String,
/// The list of tests in the file.
tests: List(TestTree),
/// The time it took to collect the tests in the file.
collect_duration: Duration,
)
}
/// A file in the `test/` directory that likely contains tests.
pub type TestSourceFile {
TestSourceFile(
/// The name of the Gleam module.
module_name: String,
/// The filepath to the `.gleam` file.
filepath: String,
/// The list of tests in the file.
tests: List(TestFunction),
)
}
/// Returns the list of files in the `test/` directory.
pub fn locate_test_files(ctx: Context) -> Result(List(TestSourceFile), Nil) {
use test_files <- try(
simplifile.get_files(in: "test")
|> result.nil_error,
)
test_files
|> list.filter(fn(filepath) { string.ends_with(filepath, ".gleam") })
|> list.filter(fn(filepath) {
case ctx.config.filters {
[] -> True
filters ->
list.any(in: filters, satisfying: fn(filter) {
string.contains(does: filepath, contain: filter)
})
}
})
|> list.map(fn(filepath) {
TestSourceFile(
module_name: filepath_to_module_name(filepath),
filepath: filepath,
tests: [],
)
})
|> Ok
}
/// Returns the Gleam module name from the given filepath.
fn filepath_to_module_name(filepath: String) -> String {
filepath
|> string.slice(
at_index: string.length("test/"),
length: string.length(filepath),
)
|> string.replace(".gleam", "")
}
pub type TestFunction {
TestFunction(module_name: String, name: String, body: fn() -> Dynamic)
}
/// Identifies all of the tests contained in the given list of `TestSourceFile`s.
///
/// Any files that don't have any tests will be excluded from the result.
pub fn identify_tests(
test_files: List(TestSourceFile),
ctx: Context,
) -> List(TestFile) {
test_files
|> list.filter_map(identify_tests_in_file(_, ctx))
}
fn identify_tests_in_file(
test_file: TestSourceFile,
ctx: Context,
) -> Result(TestFile, Nil) {
let started_at = clock.now(ctx.clock)
let #(standalone_tests, test_functions) =
test_file.tests
|> list.partition(is_standalone_test(_, ctx))
let standalone_tests =
standalone_tests
|> list.map(fn(test_function) {
let function: fn() -> Nil =
test_function.body
|> dynamic.from
|> dynamic.unsafe_coerce
Test(test_function.name, function, False)
|> test_tree.Test
})
let #(test_suites, _test_functions) =
test_functions
|> list.partition(is_test_suite(_, ctx))
let test_suites =
test_suites
|> list.filter_map(fn(test_function) {
test_function.body()
|> decode_test_tree
|> result.map_error(fn(error) {
logger.error(ctx.logger, string.inspect(error))
})
})
let tests = list.concat([test_suites, standalone_tests])
case tests {
[] -> Error(Nil)
tests -> {
let collect_duration =
clock.now(ctx.clock)
|> birl.difference(started_at)
Ok(TestFile(
module_name: test_file.module_name,
filepath: test_file.filepath,
tests: tests,
collect_duration: collect_duration,
))
}
}
}
fn is_standalone_test(test_function: TestFunction, ctx: Context) -> Bool {
test_function.name
|> regex.check(with: ctx.config.discover_standalone_tests_pattern)
}
fn is_test_suite(test_function: TestFunction, ctx: Context) -> Bool {
test_function.name
|> regex.check(with: ctx.config.discover_describe_tests_pattern)
}