44# Use of this source code is governed by an MIT-style license that can be found
55# in the LICENSE.md file or at https://opensource.org/licenses/MIT.
66
7+ # !!! info "COV_EXCL_LINE"
8+ #
9+ # The Julia coverage check is not perfect, particularly when it comes to
10+ # macros that produce code that is not executed. To work around
11+ # false-negatives, some lines in this file are excluded from coverage with
12+ # `# COV_EXCL_LINE`. (In most of the excluded cases, the default is for the
13+ # tests to pass, so the failure case of the testset macro is not executed,
14+ # and so no code is executed that can be tied back to the excluded lines.
15+
716module Bridges
817
918import MathOptInterface as MOI
@@ -153,15 +162,21 @@ function _test_structural_identical(
153162 # the variables are added in the same order to both models.
154163 a_x = MOI. get (a, MOI. ListOfVariableIndices ())
155164 b_x = MOI. get (b, MOI. ListOfVariableIndices ())
156- attr = MOI. NumberOfVariables ()
157- Test. @test MOI. get (a, attr) == MOI. get (b, attr)
158- Test. @test length (a_x) == length (b_x)
165+ Test. @testset " Test NumberOfVariables" begin # COV_EXCL_LINE
166+ Test. @test MOI. get (a, MOI. NumberOfVariables ()) ==
167+ MOI. get (b, MOI. NumberOfVariables ())
168+ end
169+ Test. @testset " Test length ListOfVariableIndices" begin # COV_EXCL_LINE
170+ Test. @test length (a_x) == length (b_x)
171+ end
159172 # A dictionary that maps things from `b`-space to `a`-space.
160173 x_map = Dict (bx => a_x[i] for (i, bx) in enumerate (b_x))
161174 # To check that the constraints, we need to first cache all of the
162175 # constraints in `a`.
163176 constraints = Dict {Any,Any} ()
164- for (F, S) in MOI. get (a, MOI. ListOfConstraintTypesPresent ())
177+ a_constraint_types = MOI. get (a, MOI. ListOfConstraintTypesPresent ())
178+ b_constraint_types = MOI. get (b, MOI. ListOfConstraintTypesPresent ())
179+ Test. @testset " get $F and $S " for (F, S) in a_constraint_types
165180 Test. @test MOI. supports_constraint (a, F, S)
166181 constraints[(F, S)] =
167182 map (MOI. get (a, MOI. ListOfConstraintIndices {F,S} ())) do ci
@@ -170,20 +185,16 @@ function _test_structural_identical(
170185 MOI. get (a, MOI. ConstraintSet (), ci),
171186 )
172187 end
173- end
174- # Now compare the constraints in `b` with the cache in `constraints`.
175- b_constraint_types = MOI. get (b, MOI. ListOfConstraintTypesPresent ())
176- # There may be constraint types reported in `a` that are not in `b`, but
177- # have zero constraints in `a`.
178- for (F, S) in keys (constraints)
179- attr = MOI. NumberOfConstraints {F,S} ()
180- Test. @test (F, S) in b_constraint_types || MOI. get (a, attr) == 0
181- end
182- for (F, S) in b_constraint_types
188+ # There may be constraint types reported in `a` that are not in `b`, but
189+ # have zero constraints in `a`.
190+ Test. @test (F, S) in b_constraint_types ||
191+ MOI. get (a, MOI. NumberOfConstraints {F,S} ()) == 0
192+ end # COV_EXCL_LINE
193+ Test. @testset " $F -in-$S " for (F, S) in b_constraint_types
183194 Test. @test haskey (constraints, (F, S))
184195 # Check that the same number of constraints are present
185- attr = MOI. NumberOfConstraints {F,S} ()
186- Test . @test MOI . get (a, attr) == MOI. get (b, attr )
196+ Test . @test MOI . get (a, MOI. NumberOfConstraints {F,S} ()) ==
197+ MOI. get (b, MOI . NumberOfConstraints {F,S} () )
187198 # Check that supports_constraint is implemented
188199 Test. @test MOI. supports_constraint (b, F, S)
189200 # Check that each function in `b` matches a function in `a`
@@ -202,12 +213,14 @@ function _test_structural_identical(
202213 return s_b == s && isapprox (f, f_b) && typeof (f) == typeof (f_b)
203214 end
204215 end
205- end
216+ end # COV_EXCL_LINE
206217 # Test model attributes are set, like ObjectiveSense and ObjectiveFunction.
207218 a_attrs = MOI. get (a, MOI. ListOfModelAttributesSet ())
208219 b_attrs = MOI. get (b, MOI. ListOfModelAttributesSet ())
209- Test. @test length (a_attrs) == length (b_attrs)
210- for attr in b_attrs
220+ Test. @testset " Test length ListOfModelAttributesSet" begin # COV_EXCL_LINE
221+ Test. @test length (a_attrs) == length (b_attrs)
222+ end
223+ Test. @testset " $attr " for attr in b_attrs
211224 Test. @test attr in a_attrs
212225 if attr == MOI. ObjectiveSense ()
213226 # map_indices isn't defined for `OptimizationSense`
@@ -216,7 +229,7 @@ function _test_structural_identical(
216229 attr_b = MOI. Utilities. map_indices (x_map, MOI. get (b, attr))
217230 Test. @test isapprox (MOI. get (a, attr), attr_b)
218231 end
219- end
232+ end # COV_EXCL_LINE
220233 return
221234end
222235
@@ -259,7 +272,7 @@ and [`MOI.ConstraintPrimalStart`](@ref) to throw [`MOI.GetAttributeNotAllowed`](
259272
260273## Example
261274
262- ```jldoctest; setup=:(import MathOptInterface as MOI)
275+ ```jldoctest; setup=:(import MathOptInterface as MOI), filter=r"[0-9.]+s"
263276julia> MOI.Bridges.runtests(
264277 MOI.Bridges.Constraint.ZeroOneBridge,
265278 model -> MOI.add_constrained_variable(model, MOI.ZeroOne()),
@@ -268,9 +281,18 @@ julia> MOI.Bridges.runtests(
268281 MOI.add_constraint(model, 1.0 * x, MOI.Interval(0.0, 1.0))
269282 end,
270283 )
284+ Test Summary: | Pass Total Time
285+ Bridges.runtests | 32 32 0.8s
271286```
272287"""
273- function runtests (
288+ function runtests (args... ; kwargs... )
289+ Test. @testset " Bridges.runtests" begin
290+ _runtests (args... ; kwargs... )
291+ end
292+ return
293+ end
294+
295+ function _runtests (
274296 Bridge:: Type{<:AbstractBridge} ,
275297 input_fn:: Function ,
276298 output_fn:: Function ;
@@ -290,50 +312,61 @@ function runtests(
290312 if print_inner_model
291313 print (inner)
292314 end
293- # Load a non-bridged input model, and check that getters are the same.
294- test = MOI. Utilities. UniversalFallback (MOI. Utilities. Model {eltype} ())
295- input_fn (test)
296- _test_structural_identical (test, model; cannot_unbridge = cannot_unbridge)
297- # Load a bridged target model, and check that getters are the same.
298- target = MOI. Utilities. UniversalFallback (MOI. Utilities. Model {eltype} ())
299- output_fn (target)
300- _test_structural_identical (target, inner)
301- # Test VariablePrimalStart
302- attr = MOI. VariablePrimalStart ()
303- bridge_supported = all (values (Variable. bridges (model))) do bridge
304- return MOI. supports (model, attr, typeof (bridge))
315+ Test. @testset " Test outer bridged model appears like the input" begin # COV_EXCL_LINE
316+ test = MOI. Utilities. UniversalFallback (MOI. Utilities. Model {eltype} ())
317+ input_fn (test)
318+ _test_structural_identical (
319+ test,
320+ model;
321+ cannot_unbridge = cannot_unbridge,
322+ )
305323 end
306- if MOI. supports (model, attr, MOI. VariableIndex) && bridge_supported
307- x = MOI. get (model, MOI. ListOfVariableIndices ())
308- MOI. set (model, attr, x, fill (nothing , length (x)))
309- Test. @test all (isnothing, MOI. get (model, attr, x))
310- primal_start = fill (variable_start, length (x))
311- MOI. set (model, attr, x, primal_start)
312- if ! isempty (x)
313- # ≈ does not work if x is empty because the return of get is Any[]
314- Test. @test MOI. get (model, attr, x) ≈ primal_start
315- end
324+ Test. @testset " Test inner bridged model appears like the target" begin # COV_EXCL_LINE
325+ target = MOI. Utilities. UniversalFallback (MOI. Utilities. Model {eltype} ())
326+ output_fn (target)
327+ _test_structural_identical (target, inner)
316328 end
317- # Test ConstraintPrimalStart and ConstraintDualStart
318- for (F, S) in MOI. get (model, MOI. ListOfConstraintTypesPresent ())
319- for ci in MOI. get (model, MOI. ListOfConstraintIndices {F,S} ())
320- set = try
321- MOI. get (model, MOI. ConstraintSet (), ci)
322- catch err
323- _runtests_error_handler (err, cannot_unbridge)
324- continue
329+ Test. @testset " Test MOI.VariablePrimalStart" begin # COV_EXCL_LINE
330+ attr = MOI. VariablePrimalStart ()
331+ bridge_supported = all (values (Variable. bridges (model))) do bridge
332+ return MOI. supports (model, attr, typeof (bridge))
333+ end
334+ if MOI. supports (model, attr, MOI. VariableIndex) && bridge_supported
335+ x = MOI. get (model, MOI. ListOfVariableIndices ())
336+ MOI. set (model, attr, x, fill (nothing , length (x)))
337+ Test. @test all (isnothing, MOI. get (model, attr, x))
338+ primal_start = fill (variable_start, length (x))
339+ MOI. set (model, attr, x, primal_start)
340+ if ! isempty (x)
341+ # ≈ does not work if x is empty because the return of get is Any[]
342+ Test. @test MOI. get (model, attr, x) ≈ primal_start
325343 end
326- for attr in (MOI. ConstraintPrimalStart (), MOI. ConstraintDualStart ())
327- if MOI. supports (model, attr, MOI. ConstraintIndex{F,S})
344+ end
345+ end
346+ Test. @testset " Test ConstraintPrimalStart and ConstraintDualStart" begin # COV_EXCL_LINE
347+ list_of_constraints = MOI. get (model, MOI. ListOfConstraintTypesPresent ())
348+ Test. @testset " $F -in-$S " for (F, S) in list_of_constraints
349+ for ci in MOI. get (model, MOI. ListOfConstraintIndices {F,S} ())
350+ set = try
351+ MOI. get (model, MOI. ConstraintSet (), ci)
352+ catch err
353+ _runtests_error_handler (err, cannot_unbridge)
354+ continue
355+ end
356+ attrs = (MOI. ConstraintPrimalStart (), MOI. ConstraintDualStart ())
357+ Test. @testset " $attr " for attr in attrs
358+ if ! MOI. supports (model, attr, MOI. ConstraintIndex{F,S})
359+ continue
360+ end
328361 MOI. set (model, attr, ci, nothing )
329362 Test. @test MOI. get (model, attr, ci) === nothing
330363 start = _fake_start (constraint_start, set)
331364 MOI. set (model, attr, ci, start)
332365 returned_start = try
333366 MOI. get (model, attr, ci)
334367 catch err
335- # For a Constraint bridge for which the map is not invertible, the constraint primal cannot
336- # be inverted
368+ # For a Constraint bridge for which the map is not
369+ # invertible, the constraint primal cannot be inverted
337370 _runtests_error_handler (
338371 err,
339372 Bridge <: MOI.Bridges.Constraint.AbstractBridge &&
@@ -342,21 +375,30 @@ function runtests(
342375 continue
343376 end
344377 Test. @test returned_start ≈ start
345- end
378+ end # COV_EXCL_LINE
346379 end
347- end
348- end
349- # Test other bridge functions
350- for b in values (Constraint. bridges (model))
351- _general_bridge_tests (something (b))
380+ end # COV_EXCL_LINE
352381 end
353- for b in values (Objective. bridges (model))
354- _general_bridge_tests (something (b))
382+ Test. @testset " Test general bridge tests" begin # COV_EXCL_LINE
383+ Test. @testset " Constraint" begin # COV_EXCL_LINE
384+ for b in values (Constraint. bridges (model))
385+ _general_bridge_tests (something (b))
386+ end
387+ end
388+ Test. @testset " Objective" begin # COV_EXCL_LINE
389+ for b in values (Objective. bridges (model))
390+ _general_bridge_tests (something (b))
391+ end
392+ end
393+ Test. @testset " Variable" begin # COV_EXCL_LINE
394+ for b in values (Variable. bridges (model))
395+ _general_bridge_tests (something (b))
396+ end
397+ end
355398 end
356- for b in values (Variable . bridges (model))
357- _general_bridge_tests ( something (b) )
399+ Test . @testset " Test delete " begin # COV_EXCL_LINE
400+ _test_delete (Bridge, model, inner )
358401 end
359- _test_delete (Bridge, model, inner)
360402 return
361403end
362404
@@ -377,7 +419,7 @@ Run a series of tests that check the correctness of `Bridge`.
377419
378420## Example
379421
380- ```jldoctest; setup=:(import MathOptInterface as MOI)
422+ ```jldoctest; setup=:(import MathOptInterface as MOI), filter=r"[0-9.]+s"
381423julia> MOI.Bridges.runtests(
382424 MOI.Bridges.Constraint.ZeroOneBridge,
383425 \"\"\"
@@ -390,6 +432,8 @@ julia> MOI.Bridges.runtests(
390432 1.0 * x in Interval(0.0, 1.0)
391433 \"\"\" ,
392434 )
435+ Test Summary: | Pass Total Time
436+ Bridges.runtests | 32 32 0.0s
393437```
394438"""
395439function runtests (
0 commit comments