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 7 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
12 changes: 12 additions & 0 deletions src/Test/AtomicTests/atomic_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 atomictests = Dict{String, Function}()

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

@moitestset atomic
24 changes: 24 additions & 0 deletions src/Test/AtomicTests/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)
Copy link
Member

Choose a reason for hiding this comment

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

It's weird that we have to call MOI.empty! at the beginning of every test. This should happen somewhere else.

Copy link
Member Author

Choose a reason for hiding this comment

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

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
atomictests["getconstraint"] = getconstraint
105 changes: 105 additions & 0 deletions src/Test/AtomicTests/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
atomictests["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
atomictests["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
atomictests["feasibility_sense"] = feasibility_sense

"""
Test constant in objective.
"""
function constant_obj(model::MOI.ModelLike, config::TestConfig)
Copy link
Member

Choose a reason for hiding this comment

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

If the test calls optimize!, I'd propose to have "solve" somewhere in the name, like solve_constant_obj. You could easily think that constant_obj would just set and get the objective attribute without solving.

atol, rtol = config.atol, config.rtol
MOI.empty!(model)
@test MOI.isempty(model)
MOIU.loadfromstring!(model,"""
variables: x
Copy link
Member

Choose a reason for hiding this comment

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

This is a great use of loadfromstring!

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
atomictests["constant_obj"] = constant_obj

"""
Test blank objective.
"""
function 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
atomictests["blank_obj"] = blank_obj

"""
Test SingleVariable objective.
"""
function 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
atomictests["singlevariable_obj"] = singlevariable_obj
178 changes: 178 additions & 0 deletions src/Test/AtomicTests/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
atomictests["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
atomictests["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
atomictests["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
atomictests["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
atomictests["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
atomictests["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.

Another example where "solve" in the name would be informative, e.g., solve_with_upperbound.

"""
function 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
atomictests["upperbound"] = upperbound

"""
Test the setting of an lower bound
"""
function 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
atomictests["lowerbound"] = lowerbound
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("AtomicTests/atomic_tests.jl")

end # module
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