Skip to content

Commit c288a3e

Browse files
authored
[FileFormats] improve test coverage (#2630)
1 parent 3f64195 commit c288a3e

File tree

9 files changed

+216
-27
lines changed

9 files changed

+216
-27
lines changed

src/FileFormats/LP/LP.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1001,7 +1001,7 @@ function _parse_section(
10011001
for token in tokens[3:end]
10021002
items = String.(split(token, ":"))
10031003
if length(items) != 2
1004-
error("Invalid sequence: $(token)")
1004+
error("Invalid token in SOS constraint: $(token)")
10051005
end
10061006
push!(variables, _get_variable_from_name(model, cache, items[1]))
10071007
push!(weights, parse(Float64, items[2]))

test/FileFormats/CBF/CBF.jl

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,15 @@ const _WRITE_READ_MODELS = [
293293
c1: [x, y, z] in ExponentialCone()
294294
""",
295295
),
296+
(
297+
"VectorOfVariables as function in ExponentialCone",
298+
"""
299+
variables: x, y, z
300+
minobjective: x
301+
c0: [x, y, z] in Nonnegatives(3)
302+
c1: [x, y, z] in ExponentialCone()
303+
""",
304+
),
296305
(
297306
"VectorOfVariables in DualExponentialCone",
298307
"""
@@ -301,6 +310,15 @@ const _WRITE_READ_MODELS = [
301310
c1: [x, y, z] in DualExponentialCone()
302311
""",
303312
),
313+
(
314+
"VectorOfVariables as function in DualExponentialCone",
315+
"""
316+
variables: x, y, z
317+
minobjective: x
318+
c0: [x, y, z] in Nonnegatives(3)
319+
c1: [x, y, z] in DualExponentialCone()
320+
""",
321+
),
304322
(
305323
"VectorOfVariables in PowerCone",
306324
"""
@@ -674,6 +692,13 @@ function test_roundtrip_DualExponentialCone()
674692
return
675693
end
676694

695+
function test_supports_quadratic_objective()
696+
model = CBF.Model()
697+
F = MOI.ScalarQuadraticFunction{Float64}
698+
@test !MOI.supports(model, MOI.ObjectiveFunction{F}())
699+
return
677700
end
678701

702+
end # module
703+
679704
TestCBF.runtests()

test/FileFormats/FileFormats.jl

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,9 +117,10 @@ function test_compression()
117117
filename = joinpath(@__DIR__, "free_integer.mps")
118118
MOI.write_to_file(model, filename * ".garbage")
119119
for ext in ["", ".bz2", ".gz"]
120-
MOI.write_to_file(model, filename * ext)
121-
model2 = MOI.FileFormats.Model(filename = filename * ext)
122-
MOI.read_from_file(model2, filename)
120+
filename_ext = filename * ext
121+
MOI.write_to_file(model, filename_ext)
122+
model2 = MOI.FileFormats.Model(filename = filename_ext)
123+
MOI.read_from_file(model2, filename_ext)
123124
end
124125

125126
sleep(1.0) # Allow time for unlink to happen.
@@ -167,6 +168,27 @@ function test_Model()
167168
)
168169
end
169170

171+
function test_generic_names()
172+
# These methods were added to MOI, but then we changed the REW model to not
173+
# need them.
174+
model = MOI.Utilities.Model{Float64}()
175+
x = MOI.add_variables(model, 2)
176+
MOI.set.(model, MOI.VariableName(), x, "x")
177+
c = MOI.add_constraint.(model, 1.0 .* x, MOI.EqualTo(1.0))
178+
MOI.set.(model, MOI.ConstraintName(), c, "c")
179+
c2 = MOI.add_constraint(model, 1.0 * x[2], MOI.LessThan(1.0))
180+
MOI.set(model, MOI.ConstraintName(), c2, "R2")
181+
MOI.FileFormats.create_generic_names(model)
182+
@test MOI.get.(model, MOI.VariableName(), x) == ["C1", "C2"]
183+
c_name = MOI.get.(model, MOI.ConstraintName(), c)
184+
c2_name = MOI.get(model, MOI.ConstraintName(), c2)
185+
names = vcat(c_name, c2_name)
186+
# The names depend on the order that the constraints are parsed. Either is
187+
# okay.
188+
@test names == ["R1", "R2", "R3"] || names == ["R1", "R3", "R2"]
189+
return
190+
end
191+
170192
end
171193

172194
TestFileFormats.runtests()

test/FileFormats/LP/LP.jl

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,19 @@ import MathOptInterface.FileFormats: LP
1313

1414
const LP_TEST_FILE = "test.lp"
1515

16+
function runtests()
17+
for name in names(@__MODULE__, all = true)
18+
if startswith("$(name)", "test_")
19+
@testset "$name" begin
20+
getfield(@__MODULE__, name)()
21+
end
22+
end
23+
end
24+
sleep(1.0) # Allow time for unlink to happen.
25+
rm(LP_TEST_FILE, force = true)
26+
return
27+
end
28+
1629
function test_show()
1730
@test sprint(summary, LP.Model()) == "MOI.FileFormats.LP.Model"
1831
return
@@ -1065,19 +1078,27 @@ function test_VectorAffineFunction_SOS()
10651078
return
10661079
end
10671080

1068-
function runtests()
1069-
for name in names(@__MODULE__, all = true)
1070-
if startswith("$(name)", "test_")
1071-
@testset "$name" begin
1072-
getfield(@__MODULE__, name)()
1073-
end
1074-
end
1075-
end
1076-
sleep(1.0) # Allow time for unlink to happen.
1077-
rm(LP_TEST_FILE, force = true)
1081+
function test_comprehensive_write()
1082+
model = LP.Model()
1083+
io = IOBuffer()
1084+
print(
1085+
io,
1086+
"""
1087+
minimize
1088+
obj: x + y
1089+
subject to
1090+
SOS
1091+
c11: S1:: x 1.0 y 2.0
1092+
""",
1093+
)
1094+
seekstart(io)
1095+
@test_throws(
1096+
ErrorException("Invalid token in SOS constraint: x"),
1097+
read!(io, model),
1098+
)
10781099
return
10791100
end
10801101

1081-
end
1102+
end # module
10821103

10831104
TestLP.runtests()

test/FileFormats/MOF/MOF.jl

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1497,6 +1497,27 @@ function test_nonlinear_variable_real_nodes()
14971497
return
14981498
end
14991499

1500+
function test_nonlinear_variable_complex_nodes()
1501+
x = MOI.VariableIndex(1)
1502+
object = Dict{String,Any}(
1503+
"type" => "ScalarNonlinearFunction",
1504+
"root" => MOF.Dict{String,Any}(
1505+
"type" => "^",
1506+
"args" => Any[
1507+
Dict{String,Any}("type" => "node", "index" => 1),
1508+
Dict{String,Any}("type" => "node", "index" => 2),
1509+
],
1510+
),
1511+
"node_list" => Any[
1512+
Dict{String,Any}("type" => "variable", "name" => "x"),
1513+
Dict{String,Any}("type" => "complex", "real" => 2.0, "imag" => 3.0),
1514+
],
1515+
)
1516+
f = MOI.ScalarNonlinearFunction(:^, Any[x, 2.0+3im])
1517+
@test MOF.function_to_moi(object, Dict("x" => x)) f
1518+
return
1519+
end
1520+
15001521
function test_mof_scalaraffinefunction()
15011522
x = MOI.VariableIndex(1)
15021523
f = 1.0 * x + 2.0
@@ -1535,6 +1556,40 @@ function test_mof_scalarquadraticfunction()
15351556
return
15361557
end
15371558

1559+
function test_nonlinear_expression_not_call()
1560+
model = MOF.Model()
1561+
x = MOI.add_variable(model)
1562+
expr = :($x[1])
1563+
con = MOI.add_constraint(model, MOF.Nonlinear(expr), MOI.EqualTo(1.0))
1564+
io = IOBuffer()
1565+
@test_throws(
1566+
ErrorException(
1567+
"Expected an expression that was a function. Got $expr.",
1568+
),
1569+
write(io, model),
1570+
)
1571+
return
1572+
end
1573+
1574+
function test_write_NLPBlock_no_objective()
1575+
model = MOF.Model()
1576+
x = MOI.add_variables(model, 4)
1577+
for (index, variable) in enumerate(x)
1578+
MOI.set(model, MOI.VariableName(), variable, "var_$(index)")
1579+
end
1580+
MOI.add_constraints(model, x, Ref(MOI.Interval(1.0, 5.0)))
1581+
block = HS071(x)
1582+
new_block =
1583+
MOI.NLPBlockData(block.constraint_bounds, block.evaluator, false)
1584+
MOI.set(model, MOI.NLPBlock(), new_block)
1585+
io = IOBuffer()
1586+
write(io, model)
1587+
seekstart(io)
1588+
contents = read(io, String)
1589+
@test occursin(""""objective":{"sense":"feasibility"}""", contents)
1590+
return
1591+
end
1592+
15381593
end
15391594

15401595
TestMOF.runtests()

test/FileFormats/MPS/MPS.jl

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1451,6 +1451,39 @@ function test_qcmatrix_read_cplex()
14511451
return
14521452
end
14531453

1454+
function test_model_Name()
1455+
model = MPS.Model()
1456+
MOI.set(model, MOI.Name(), "TestFoo")
1457+
io = IOBuffer()
1458+
write(io, model)
1459+
seekstart(io)
1460+
contents = read(io, String)
1461+
@test occursin("NAME TestFoo", contents)
1462+
return
1463+
end
1464+
1465+
function test_model_large_integer()
1466+
model = MPS.Model()
1467+
x = MOI.add_constrained_variable(model, MOI.GreaterThan(123.0))
1468+
io = IOBuffer()
1469+
write(io, model)
1470+
seekstart(io)
1471+
contents = read(io, String)
1472+
@test occursin("LO bounds x1 123", contents)
1473+
return
1474+
end
1475+
1476+
function test_model_not_empty()
1477+
model = MPS.Model()
1478+
x = MOI.add_variable(model)
1479+
io = IOBuffer()
1480+
@test_throws(
1481+
ErrorException("Cannot read in file because model is not empty."),
1482+
read!(io, model),
1483+
)
1484+
return
1485+
end
1486+
14541487
end # TestMPS
14551488

14561489
TestMPS.runtests()

test/FileFormats/MPS/free_integer.mps

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1+
* A model with blank lines and comments
12
NAME
3+
24
OBJSENSE MIN
5+
36
ROWS
47
N obj
58
G con1
9+
610
COLUMNS
711
MARKER 'MARKER' 'INTORG'
812
x obj 1 con1 1

test/FileFormats/NL/sol.jl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,16 @@ function test_result_count()
358358
return
359359
end
360360

361+
function test_eol()
362+
model = NL.Model()
363+
io = IOBuffer()
364+
@test_throws(
365+
ErrorException("Reached end of sol file unexpectedly."),
366+
NL.SolFileResults(io, model),
367+
)
368+
return
369+
end
370+
361371
end
362372

363373
TestNonlinearSolFiles.runtests()

test/FileFormats/SDPA/SDPA.jl

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,24 @@ using Test
1010

1111
import MathOptInterface as MOI
1212
import MathOptInterface.Utilities as MOIU
13-
const SDPA = MOI.FileFormats.SDPA
13+
import MathOptInterface.FileFormats: SDPA
14+
1415
const SDPA_TEST_FILE = "test.sdpa"
1516
const SDPA_MODELS_DIR = joinpath(@__DIR__, "models")
1617

18+
function runtests()
19+
for name in names(@__MODULE__, all = true)
20+
if startswith("$(name)", "test_")
21+
@testset "$name" begin
22+
getfield(@__MODULE__, name)()
23+
end
24+
end
25+
end
26+
sleep(1.0) # Allow time for unlink to happen.
27+
rm(SDPA_TEST_FILE, force = true)
28+
return
29+
end
30+
1731
function _set_var_and_con_names(model::MOI.ModelLike)
1832
variable_names = String[]
1933
for j in MOI.get(model, MOI.ListOfVariableIndices())
@@ -330,19 +344,24 @@ function test_dim_reader()
330344
end
331345
end
332346

333-
function runtests()
334-
for name in names(@__MODULE__, all = true)
335-
if startswith("$(name)", "test_")
336-
@testset "$name" begin
337-
getfield(@__MODULE__, name)()
338-
end
339-
end
340-
end
341-
sleep(1.0) # Allow time for unlink to happen.
342-
rm(SDPA_TEST_FILE, force = true)
347+
function test_integer_before_variables()
348+
file = """
349+
*INTEGER
350+
*1
351+
"""
352+
io = IOBuffer()
353+
print(io, file)
354+
seekstart(io)
355+
model = SDPA.Model()
356+
@test_throws(
357+
ErrorException(
358+
"The number of variables should be given before *INTEGER section.",
359+
),
360+
read!(io, model),
361+
)
343362
return
344363
end
345364

346-
end
365+
end # module
347366

348367
TestSDPA.runtests()

0 commit comments

Comments
 (0)