@@ -2,7 +2,8 @@ defmodule ExUnit.Runner do
22 @ moduledoc false
33
44 defrecord Config , formatter: ExUnit.CLIFormatter , formatter_id: nil ,
5- max_cases: 4 , taken_cases: 0 , async_cases: [ ] , sync_cases: [ ]
5+ max_cases: 4 , taken_cases: 0 , async_cases: [ ] ,
6+ sync_cases: [ ] , include: nil , exclude: nil
67
78 def run ( async , sync , opts , load_us ) do
89 opts = normalize_opts ( opts )
@@ -125,9 +126,22 @@ defmodule ExUnit.Runner do
125126 end
126127
127128 defp run_test ( config , test , context ) do
128- case_name = test . case
129129 config . formatter . test_started ( config . formatter_id , test )
130130
131+ filters = combine_filters ( [ include: config . include , exclude: config . exclude ] )
132+ result = evaluate_filters ( filters , test . tags )
133+
134+ test = case result do
135+ { :error , tag } -> skip_test ( test , tag )
136+ :ok -> spawn_test ( config , test , context )
137+ end
138+
139+ config . formatter . test_finished ( config . formatter_id , test )
140+ end
141+
142+ defp spawn_test ( _config , test , context ) do
143+ case_name = test . case
144+
131145 # Run test in a new process so that we can trap exits for a single test
132146 self_pid = self
133147 { test_pid , test_ref } = Process . spawn_monitor fn ->
@@ -157,13 +171,16 @@ defmodule ExUnit.Runner do
157171
158172 receive do
159173 { ^ test_pid , :test_finished , test } ->
160- config . formatter . test_finished ( config . formatter_id , test )
174+ test
161175 { :DOWN , ^ test_ref , :process , ^ test_pid , { error , stacktrace } } ->
162- test = test . state { :failed , { :EXIT , error , prune_stacktrace ( stacktrace ) } }
163- config . formatter . test_finished ( config . formatter_id , test )
176+ test . state { :failed , { :EXIT , error , prune_stacktrace ( stacktrace ) } }
164177 end
165178 end
166179
180+ defp skip_test ( test , mismatch ) do
181+ test . state { :skip , "due to #{ mismatch } filter" }
182+ end
183+
167184 ## Helpers
168185
169186 defp take_async_cases ( Config [ ] = config , count ) do
@@ -182,6 +199,42 @@ defmodule ExUnit.Runner do
182199 end
183200 end
184201
202+ def evaluate_filters ( filters , tags ) do
203+ Enum . find_value tags , :ok , fn { tag_key , _ } = tag ->
204+ unless tag_accepted? ( filters , tag ) , do: { :error , tag_key }
205+ end
206+ end
207+
208+ defp tag_accepted? ( [ include: include , exclude: exclude ] , tag ) do
209+ tag_included? ( include , tag ) and not tag_excluded? ( exclude , tag )
210+ end
211+
212+ defp tag_included? ( include , { tag_key , tag_value } ) do
213+ case Dict . fetch ( include , tag_key ) do
214+ { :ok , allowed } -> tag_value in allowed
215+ :error -> true
216+ end
217+ end
218+
219+ defp tag_excluded? ( exclude , { tag_key , tag_value } ) do
220+ case Dict . fetch ( exclude , tag_key ) do
221+ { :ok , forbidden } -> tag_value in forbidden
222+ :error -> false
223+ end
224+ end
225+
226+ defp combine_filters ( [ include: include , exclude: exclude ] ) do
227+ include = group_by_key ( include )
228+ exclude = group_by_key ( exclude )
229+ [ include: include , exclude: exclude ]
230+ end
231+
232+ defp group_by_key ( dict ) do
233+ Enum . reduce dict , HashDict . new , fn { key , value } , acc ->
234+ Dict . update acc , key , [ value ] , & [ value | & 1 ]
235+ end
236+ end
237+
185238 defp pruned_stacktrace , do: prune_stacktrace ( System . stacktrace )
186239
187240 # Assertions can pop-up in the middle of the stack
0 commit comments