-
Notifications
You must be signed in to change notification settings - Fork 25
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
Add check in unfeasible analyzer: unit tests #1716
Add check in unfeasible analyzer: unit tests #1716
Conversation
Signed-off-by: Sylvain Leclerc <[email protected]>
Signed-off-by: Sylvain Leclerc <[email protected]>
Signed-off-by: Sylvain Leclerc <[email protected]>
Signed-off-by: Sylvain Leclerc <[email protected]>
Signed-off-by: Sylvain Leclerc <[email protected]>
Signed-off-by: Sylvain Leclerc <[email protected]>
Signed-off-by: Sylvain Leclerc <[email protected]>
…analyzer' into feature/add_check_in_unfeasible_analyzer_unit_tests
Signed-off-by: Sylvain Leclerc <[email protected]>
Signed-off-by: Sylvain Leclerc <[email protected]>
/*! | ||
* Analysis mock, used to assess which step has been run by the analyzer | ||
*/ | ||
class AnalysisMock : public UnfeasibilityAnalysis |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
with another C++ unit test library (see GoogleTest, catch 2, CppUTest, ...), we wouldn't need this mock class, and we would need variables like hasRun1, hasPrinted1, ....
We should consider adopting one of these unit tests framework
analysis.push_back(std::make_unique<AnalysisMock>(true, hasRun2, hasPrinted2)); | ||
std::unique_ptr<MPSolver> problem(MPSolver::CreateSolver("GLOP")); | ||
|
||
UnfeasiblePbAnalyzer analyzer(std::move(analysis)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need the use of std::move here, as it's used as well in the constructor initialization list ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, necessary for the compiler, because the vector is move-only (because its content is move-only).
without std::move
I guess the compiler tries to perform a copy.
|
||
BOOST_AUTO_TEST_SUITE(unfeasible_problem_analyzer) | ||
|
||
BOOST_AUTO_TEST_CASE(test_problem_analyzer) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test name is starts with test_ : we know the context is unit test, so this prefix is useless, don't you think ?
Here we could call the test : probe every step of unfeasible analysis ? (may be there's a better name)
Please see next comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall, I changed the namings according to your input, thank you!
In particular, I indeed removed the test_ prefix, and tried to give a small explanation of the expected behavior ("should" do this).
}; | ||
|
||
|
||
BOOST_AUTO_TEST_SUITE(unfeasible_problem_analyzer) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm wondering (it's your choice to take this into account) :
We would expect to test an object's behavior (not methods), using it's interface.
We would test simple behaviors, and then more tricky ones.
For example, the names of the tests could be (in the first tests, no need to print a report) :
- run_analyzer_with_no_analysis (what should we expect in this case ?)
- run_analyzer_with_one_analysis_detecting_a_pb
- run_analyzer_with_one_analysis_unable_to_detect_pb
- run_analyzer_with_2_analyses__none_detects_a_pb
- run_analyzer_with_2_analyses__only_one_detects_pb
- run_analyzer_with_2_analyses_detecting_a_pb__only the first_one_is_run
- what else ?
About printing reports :
@sylvlecl : we just had a chat about it, run() method should produce a report (for example a vector of std::string), either return by run(), or retrievable with a get method.
This would allow to remove the dependency to the logger.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I left the test as is to test the actual workflow. I agree that for more important piece of codes we should bother to have finer grained tests though.
{ | ||
ConstraintSlackAnalysis analysis; | ||
std::unique_ptr<MPSolver> problem(MPSolver::CreateSolver("GLOP")); | ||
const double inf = problem->infinity(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should not hesitate naming infinity "infinity" instead of "inf", which can be confused with inferior
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
* - Variable 2 must be lesser than -1 | ||
* - but if feasible is false, constraint enforces that variable 2 is greater than variable 1 --> infeasible | ||
*/ | ||
std::unique_ptr<MPSolver> createProblem(const std::string& constraintName, bool feasible) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Calling createProblem() like this createProblem(constraintName, false);
is a bit unclear.
A solution could be to have 3 functions :
- std::unique_ptr<MPSolver> createSimpleProblem()
- void createFeasibleProblem(MPSolver* pb)
- void createUnfeasibleProblem(MPSolver* pb)
The last 2 functions would call the first for for the creation a problem with one constraint of 2 variables.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I provided the 2 methods createFeasible
and createUnfeasible
which just call the initial one.
"AreaHydroLevel::hour<8>", | ||
}; | ||
|
||
BOOST_DATA_TEST_CASE(test_slack_variables_with_unfeasible_constraint, bdata::make(validConstraintNames), constraintName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Test_ prefix not needed.
This test name does not really reflects what it does.
What it does is :
slack variable analysis detects unfeasibility on many well names constraints.
A bit long, but much clearer.
BOOST_CHECK(analysis.hasDetectedInfeasibilityCause()); | ||
} | ||
|
||
BOOST_AUTO_TEST_CASE(test_slack_variables_with_unfeasible_constraint_name_ignored) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A name could be :
slack variable analysis does not detect unfeasibilty on ill-named constraint
BOOST_CHECK(!analysis.hasDetectedInfeasibilityCause()); | ||
} | ||
|
||
BOOST_DATA_TEST_CASE(test_slack_variables_no_problematic_constraint, bdata::make(validConstraintNames), constraintName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to parameterize a test for an analysis that won't detect anything ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not very useful indeed, a little redundant with the other test case, I removed the parameterization.
Signed-off-by: Sylvain Leclerc <[email protected]>
Kudos, SonarCloud Quality Gate passed! |
Description
Adding some unit tests on top of #1689 and of existing code.