Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC: unit tests #329

Merged
merged 10 commits into from
May 1, 2018
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/MathOptInterface.jl
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,8 @@ include("variables.jl")
include("nlp.jl")

# submodules
include("Test/Test.jl") # MOI.Test
include("Utilities/Utilities.jl") # MOI.Utilities
include("Test/Test.jl") # MOI.Test
include("Bridges/Bridges.jl") # MOI.Bridges

end
3 changes: 3 additions & 0 deletions src/Test/Test.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ module Test

using MathOptInterface
const MOI = MathOptInterface
const MOIU = MOI.Utilities

using Base.Test

Expand All @@ -18,4 +19,6 @@ include("intconic.jl")

include("nlp.jl")

include("UnitTests/unit_tests.jl")

end # module
24 changes: 24 additions & 0 deletions src/Test/UnitTests/constraints.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
"""
Test getting constraints by name.
"""
function getconstraint(model::MOI.ModelLike, config::TestConfig)
MOI.empty!(model)
MOIU.loadfromstring!(model,"""
variables: x
minobjective: 2.0x
c1: x >= 1.0
c2: x <= 2.0
""")
@test !MOI.canget(model, MOI.ConstraintIndex, "c3")
@test MOI.canget(model, MOI.ConstraintIndex, "c1")
@test MOI.canget(model, MOI.ConstraintIndex{MOI.SingleVariable, MOI.GreaterThan{Float64}}, "c1")
@test !MOI.canget(model, MOI.ConstraintIndex{MOI.SingleVariable, MOI.LessThan{Float64}}, "c1")
@test MOI.canget(model, MOI.ConstraintIndex, "c2")
@test !MOI.canget(model, MOI.ConstraintIndex{MOI.SingleVariable, MOI.GreaterThan{Float64}}, "c2")
@test MOI.canget(model, MOI.ConstraintIndex{MOI.SingleVariable, MOI.LessThan{Float64}}, "c2")
c1 = MOI.get(model, MOI.ConstraintIndex{MOI.SingleVariable, MOI.GreaterThan{Float64}}, "c1")
@test MOI.isvalid(model, c1)
c2 = MOI.get(model, MOI.ConstraintIndex{MOI.SingleVariable, MOI.LessThan{Float64}}, "c2")
@test MOI.isvalid(model, c2)
end
unittests["getconstraint"] = getconstraint
105 changes: 105 additions & 0 deletions src/Test/UnitTests/objectives.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#=
Functions in this file test functionality relating to objectives in MOI.

### Requires
- optimize!

### Functionality currently tested
- get/set ObjectiveSense
- a constant in a affine objective
- a blank objective

### Functionality not yet tested
- Quadratic Objectives
- Modifications
=#

"""
Set objective to MaxSense
"""
function max_sense(model::MOI.ModelLike, config::TestConfig)
MOI.empty!(model)
@test MOI.isempty(model)
@test MOI.canset(model, MOI.ObjectiveSense())
MOI.set!(model, MOI.ObjectiveSense(), MOI.MaxSense)
@test MOI.canget(model, MOI.ObjectiveSense())
@test MOI.get(model, MOI.ObjectiveSense()) == MOI.MaxSense
end
unittests["max_sense"] = max_sense

"""
Set objective to MinSense
"""
function min_sense(model::MOI.ModelLike, config::TestConfig)
MOI.empty!(model)
@test MOI.isempty(model)
@test MOI.canset(model, MOI.ObjectiveSense())
MOI.set!(model, MOI.ObjectiveSense(), MOI.MinSense)
@test MOI.canget(model, MOI.ObjectiveSense())
@test MOI.get(model, MOI.ObjectiveSense()) == MOI.MinSense
end
unittests["min_sense"] = min_sense

"""
Set objective to FeasibilitySense
"""
function feasibility_sense(model::MOI.ModelLike, config::TestConfig)
MOI.empty!(model)
@test MOI.isempty(model)
@test MOI.canset(model, MOI.ObjectiveSense())
MOI.set!(model, MOI.ObjectiveSense(), MOI.FeasibilitySense)
@test MOI.canget(model, MOI.ObjectiveSense())
@test MOI.get(model, MOI.ObjectiveSense()) == MOI.FeasibilitySense
end
unittests["feasibility_sense"] = feasibility_sense

"""
Test constant in objective.
"""
function solve_constant_obj(model::MOI.ModelLike, config::TestConfig)
atol, rtol = config.atol, config.rtol
MOI.empty!(model)
@test MOI.isempty(model)
MOIU.loadfromstring!(model,"""
variables: x
minobjective: 2.0x + 1.0
c: x >= 1.0
""")
MOI.optimize!(model)
@test MOI.get(model, MOI.ObjectiveValue()) ≈ 3.0 atol=atol rtol=rtol
end
unittests["solve_constant_obj"] = solve_constant_obj

"""
Test blank objective.
"""
function solve_blank_obj(model::MOI.ModelLike, config::TestConfig)
atol, rtol = config.atol, config.rtol
MOI.empty!(model)
@test MOI.isempty(model)
MOIU.loadfromstring!(model,"""
variables: x
minobjective: 0.0x + 0.0
c: x >= 1.0
""")
MOI.optimize!(model)
@test MOI.get(model, MOI.ObjectiveValue()) ≈ 0.0 atol=atol rtol=rtol
end
unittests["solve_blank_obj"] = solve_blank_obj

"""
Test SingleVariable objective.
"""
function solve_singlevariable_obj(model::MOI.ModelLike, config::TestConfig)
atol, rtol = config.atol, config.rtol
MOI.empty!(model)
@test MOI.isempty(model)
MOIU.loadfromstring!(model,"""
variables: x
minobjective: x
c: x >= 1.0
""")
MOI.optimize!(model)
@test MOI.get(model, MOI.ObjectiveValue()) ≈ 1.0 atol=atol rtol=rtol
end
unittests["solve_singlevariable_obj"] = solve_singlevariable_obj
12 changes: 12 additions & 0 deletions src/Test/UnitTests/unit_tests.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#=
These tests aim to minimally test each expected feature in MOI, in addition
to the full end-to-end tests in contlinear.jl etc
=#

const unittests = Dict{String, Function}()

include("variables.jl")
include("objectives.jl")
include("constraints.jl")

@moitestset unit
178 changes: 178 additions & 0 deletions src/Test/UnitTests/variables.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
#=
Functions in this file test functionality relating to variables in MOI.

### Functionality currently tested
- canaddvariable
- addvariables!
- addvariable!
- deleting variables
- get/set! VariableName
- isvalid for VariableIndex
- get VariableIndex by name
- NumberOfVariables

### Functionality not yet tested
- VariablePrimalStart
- VariablePrimal
- VariableBasisStatus
- ListOfVariableIndices
=#

"""
This function tests adding a single variable.
"""
function add_variable(model::MOI.ModelLike, config::TestConfig)
MOI.empty!(model)
@test MOI.isempty(model)
@test MOI.canaddvariable(model)
@test MOI.get(model, MOI.NumberOfVariables()) == 0
v = MOI.addvariable!(model)
@test MOI.get(model, MOI.NumberOfVariables()) == 1
end
unittests["add_variable"] = add_variable

"""
This function tests adding multiple variables.
"""
function add_variables(model::MOI.ModelLike, config::TestConfig)
MOI.empty!(model)
@test MOI.isempty(model)
@test MOI.canaddvariable(model)
@test MOI.get(model, MOI.NumberOfVariables()) == 0
v = MOI.addvariables!(model, 2)
@test MOI.get(model, MOI.NumberOfVariables()) == 2
end
unittests["add_variables"] = add_variables

"""
This function tests adding, and then deleting,
a single variable.
"""
function delete_variable(model::MOI.ModelLike, config::TestConfig)
MOI.empty!(model)
@test MOI.isempty(model)
@test MOI.canaddvariable(model)
@test MOI.get(model, MOI.NumberOfVariables()) == 0
v = MOI.addvariable!(model)
@test MOI.get(model, MOI.NumberOfVariables()) == 1
@test MOI.candelete(model, v)
MOI.delete!(model, v)
@test MOI.get(model, MOI.NumberOfVariables()) == 0
end
unittests["delete_variable"] = delete_variable

"""
This function tests adding, and then deleting,
multiple variables.
"""
function delete_variables(model::MOI.ModelLike, config::TestConfig)
MOI.empty!(model)
@test MOI.isempty(model)
@test MOI.canaddvariable(model)
@test MOI.get(model, MOI.NumberOfVariables()) == 0
v = MOI.addvariables!(model, 2)
@test MOI.get(model, MOI.NumberOfVariables()) == 2
@test MOI.candelete(model, v)
MOI.delete!(model, v)
@test MOI.get(model, MOI.NumberOfVariables()) == 0
v = MOI.addvariables!(model, 2)
@test MOI.get(model, MOI.NumberOfVariables()) == 2
@test MOI.candelete(model, v[1])
MOI.delete!(model, v[1])
@test MOI.get(model, MOI.NumberOfVariables()) == 1
@test !MOI.candelete(model, v[1])
@test MOI.candelete(model, v[2])
@test !MOI.isvalid(model, v[1])
@test MOI.isvalid(model, v[2])
end
unittests["delete_variables"] = delete_variable

"""
Test getting variables by name.
"""
function getvariable(model::MOI.ModelLike, config::TestConfig)
MOI.empty!(model)
MOIU.loadfromstring!(model,"""
variables: x
minobjective: 2.0x
c1: x >= 1.0
c2: x <= 2.0
""")
@test MOI.canget(model, MOI.VariableIndex, "x")
@test !MOI.canget(model, MOI.VariableIndex, "y")
x = MOI.get(model, MOI.VariableIndex, "x")
@test MOI.isvalid(model, x)
end
unittests["getvariable"] = getvariable

"""
Test getting and setting varaible names.
"""
function variablenames(model::MOI.ModelLike, config::TestConfig)
MOI.empty!(model)
v = MOI.addvariable!(model)
@test MOI.get(model, MOI.VariableName(), v) == ""
@test MOI.canset(model, MOI.VariableName(), typeof(v))
MOI.set!(model, MOI.VariableName(), v, "x")
@test MOI.get(model, MOI.VariableName(), v) == "x"
MOI.set!(model, MOI.VariableName(), v, "y")
@test MOI.get(model, MOI.VariableName(), v) == "y"
x = MOI.addvariable!(model)
MOI.set!(model, MOI.VariableName(), x, "x")
@test MOI.get(model, MOI.VariableName(), x) == "x"
end
unittests["variablenames"] = variablenames

"""
Test the setting of an upper bound
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also update the docstring to mention that the test calls optimize!.

"""
function solve_with_upperbound(model::MOI.ModelLike, config::TestConfig)
atol, rtol = config.atol, config.rtol
MOI.empty!(model)
@test MOI.isempty(model)
MOIU.loadfromstring!(model,"""
variables: x
maxobjective: 2.0x
c1: x <= 1.0
c2: x >= 0.0
""")
MOI.optimize!(model)
@test MOI.get(model, MOI.PrimalStatus()) == MOI.FeasiblePoint
v = MOI.get(model, MOI.VariableIndex, "x")
@test MOI.get(model, MOI.VariablePrimal(), v) ≈ 1 atol=atol rtol=rtol
if config.duals
@test MOI.get(model, MOI.DualStatus()) == MOI.FeasiblePoint
c1 = MOI.get(model, MOI.ConstraintIndex{MOI.SingleVariable,MOI.LessThan{Float64}}, "c1")
@test MOI.get(model, MOI.ConstraintDual(), c1) ≈ -2.0 atol=atol rtol=rtol
c2 = MOI.get(model, MOI.ConstraintIndex{MOI.SingleVariable,MOI.GreaterThan{Float64}}, "c2")
@test MOI.get(model, MOI.ConstraintDual(), c2) ≈ 0.0 atol=atol rtol=rtol
end
end
unittests["solve_with_upperbound"] = solve_with_upperbound

"""
Test the setting of an lower bound
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

"""
function solve_with_lowerbound(model::MOI.ModelLike, config::TestConfig)
atol, rtol = config.atol, config.rtol
MOI.empty!(model)
@test MOI.isempty(model)
MOIU.loadfromstring!(model,"""
variables: x
minobjective: 2.0x
c1: x >= 1.0
c2: x <= 2.0
""")
MOI.optimize!(model)
@test MOI.get(model, MOI.PrimalStatus()) == MOI.FeasiblePoint
v = MOI.get(model, MOI.VariableIndex, "x")
@test MOI.get(model, MOI.VariablePrimal(), v) ≈ 1 atol=atol rtol=rtol
if config.duals
@test MOI.get(model, MOI.DualStatus()) == MOI.FeasiblePoint
c1 = MOI.get(model, MOI.ConstraintIndex{MOI.SingleVariable,MOI.GreaterThan{Float64}}, "c1")
@test MOI.get(model, MOI.ConstraintDual(), c1) ≈ 2.0 atol=atol rtol=rtol
c2 = MOI.get(model, MOI.ConstraintIndex{MOI.SingleVariable,MOI.LessThan{Float64}}, "c2")
@test MOI.get(model, MOI.ConstraintDual(), c2) ≈ 0.0 atol=atol rtol=rtol
end
end
unittests["solve_with_lowerbound"] = solve_with_lowerbound
13 changes: 6 additions & 7 deletions src/Test/modellike.jl
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,6 @@ function nametest(model::MOI.ModelLike)
MOI.set!(model, MOI.VariableName(), v, ["VarX","Var2"])
@test MOI.get(model, MOI.VariableName(), v) == ["VarX", "Var2"]

if MOI.candelete(model, v[2])
MOI.delete!(model, v[2])
@test !MOI.canget(model, MOI.VariableIndex, "Var2")
@test_throws KeyError MOI.get(model, MOI.VariableIndex, "Var2")
end

@test MOI.canaddconstraint(model, MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64})
c = MOI.addconstraint!(model, MOI.ScalarAffineFunction(v, [1.0,1.0], 0.0), MOI.LessThan(1.0))
@test MOI.canaddconstraint(model, MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64})
Expand All @@ -68,7 +62,6 @@ function nametest(model::MOI.ModelLike)
MOI.set!(model, MOI.ConstraintName(), [c], ["Con1"])
@test MOI.get(model, MOI.ConstraintName(), [c]) == ["Con1"]


@test MOI.canget(model, MOI.ConstraintIndex{MOI.ScalarAffineFunction{Float64},MOI.LessThan{Float64}}, "Con1")
@test !MOI.canget(model, MOI.ConstraintIndex{MOI.ScalarAffineFunction{Float64},MOI.LessThan{Float64}}, "Con2")
@test MOI.canget(model, MOI.ConstraintIndex, "Con1")
Expand All @@ -79,6 +72,12 @@ function nametest(model::MOI.ModelLike)
@test MOI.get(model, MOI.ConstraintIndex, "Con1") == c
@test_throws KeyError MOI.get(model, MOI.ConstraintIndex, "Con2")

if MOI.candelete(model, v[2])
MOI.delete!(model, v[2])
@test !MOI.canget(model, MOI.VariableIndex, "Var2")
@test_throws KeyError MOI.get(model, MOI.VariableIndex, "Var2")
end

if MOI.candelete(model, c)
MOI.delete!(model, c)
@test !MOI.canget(model, MOI.ConstraintIndex{MOI.ScalarAffineFunction{Float64},MOI.LessThan{Float64}}, "Con1")
Expand Down
Loading