[apps/graph] Added tests for graph caching

Wrote some tests to check that the caching process does not lead to
aberrant values.

Change-Id: I28137e66f430dbcf4417733d1274319dbadfc9af
This commit is contained in:
Gabriel Ozouf
2020-06-23 17:08:12 +02:00
committed by Émilie Feral
parent b0533e6836
commit aea9176f86
4 changed files with 144 additions and 4 deletions

View File

@@ -84,7 +84,7 @@ $(eval $(call depends_on_image,apps/title_bar_view.cpp,apps/exam_icon.png))
$(call object_for,$(apps_src) $(tests_src)): $(BUILD_DIR)/apps/i18n.h
$(call object_for,$(apps_src) $(tests_src)): $(BUILD_DIR)/python/port/genhdr/qstrdefs.generated.h
apps_tests_src = $(app_calculation_test_src) $(app_code_test_src) $(app_probability_test_src) $(app_regression_test_src) $(app_sequence_test_src) $(app_shared_test_src) $(app_statistics_test_src) $(app_settings_test_src) $(app_solver_test_src)
apps_tests_src = $(app_calculation_test_src) $(app_code_test_src) $(app_graph_test_src) $(app_probability_test_src) $(app_regression_test_src) $(app_sequence_test_src) $(app_shared_test_src) $(app_statistics_test_src) $(app_settings_test_src) $(app_solver_test_src)
apps_tests_src += $(addprefix apps/,\
alternate_empty_nested_menu_controller.cpp \

View File

@@ -1,9 +1,12 @@
apps += Graph::App
app_headers += apps/graph/app.h
app_graph_test_src = $(addprefix apps/graph/,\
continuous_function_store.cpp \
)
app_graph_src = $(addprefix apps/graph/,\
app.cpp \
continuous_function_store.cpp \
graph/banner_view.cpp \
graph/calculation_graph_controller.cpp \
graph/calculation_parameter_controller.cpp \
@@ -31,8 +34,13 @@ app_graph_src = $(addprefix apps/graph/,\
values/values_controller.cpp \
)
app_graph_src += $(app_graph_test_src)
apps_src += $(app_graph_src)
i18n_files += $(call i18n_without_universal_for,graph/base)
tests_src += $(addprefix apps/graph/test/,\
caching.cpp\
)
$(eval $(call depends_on_image,apps/graph/app.cpp,apps/graph/graph_icon.png))

132
apps/graph/test/caching.cpp Normal file
View File

@@ -0,0 +1,132 @@
#include <quiz.h>
#include "../app.h"
#include <cmath>
using namespace Poincare;
using namespace Shared;
namespace Graph {
bool floatEquals(float a, float b, float tolerance = 1.f/static_cast<float>(Ion::Display::Height)) {
/* The default value for the tolerance is chosen so that the error introduced
* by caching would not typically be visible on screen. */
return a == b || std::abs(a - b) <= tolerance * std::abs(a + b) / 2.f || (std::isnan(a) && std::isnan(b));
}
ContinuousFunction * addFunction(ContinuousFunctionStore * store, ContinuousFunction::PlotType type, const char * definition, Context * context) {
Ion::Storage::Record::ErrorStatus err = store->addEmptyModel();
assert(err == Ion::Storage::Record::ErrorStatus::None);
(void) err; // Silence compilation warning about unused variable.
Ion::Storage::Record record = store->recordAtIndex(store->numberOfModels() - 1);
ContinuousFunction * f = static_cast<ContinuousFunction *>(store->modelForRecord(record).operator->());
f->setPlotType(type, Poincare::Preferences::AngleUnit::Degree, context);
f->setContent(definition, context);
return f;
}
void assert_check_cartesian_cache_against_function(ContinuousFunction * function, ContinuousFunctionCache * cache, Context * context, float tMin) {
/* We set the cache to nullptr to force the evaluation (otherwise we would be
* comparing the cache against itself). */
function->setCache(nullptr);
float t;
for (int i = 0; i < Ion::Display::Width; i++) {
t = tMin + i*cache->step();
Coordinate2D<float> cacheValues = cache->valueForParameter(function, context, t);
Coordinate2D<float> functionValues = function->evaluateXYAtParameter(t, context);
quiz_assert(floatEquals(t, cacheValues.x1()));
quiz_assert(floatEquals(t, functionValues.x1()));
quiz_assert(floatEquals(cacheValues.x2(), functionValues.x2()));
}
/* We set back the cache, so that it will not be invalidated in
* PrepareForCaching later. */
function->setCache(cache);
}
void assert_cartesian_cache_stays_valid_while_panning(ContinuousFunction * function, Context * context, InteractiveCurveViewRange * range, CurveViewCursor * cursor, ContinuousFunctionStore * store, float step) {
ContinuousFunctionCache * cache = store->cacheAtIndex(0);
assert(cache);
float tMin, tStep;
constexpr float margin = 0.04f;
constexpr int numberOfMoves = 30;
for (int i = 0; i < numberOfMoves; i++) {
cursor->moveTo(cursor->t() + step, cursor->x() + step, function->evaluateXYAtParameter(cursor->x() + step, context).x2());
range->panToMakePointVisible(cursor->x(), cursor->y(), margin, margin, margin, margin, (range->xMax() - range->xMin()) / (Ion::Display::Width - 1));
tMin = range->xMin();
tStep = (range->xMax() - range->xMin()) / (Ion::Display::Width - 1);
ContinuousFunctionCache::PrepareForCaching(function, cache, tMin, tStep);
assert_check_cartesian_cache_against_function(function, cache, context, tMin);
}
}
void assert_check_polar_cache_against_function(ContinuousFunction * function, Context * context, InteractiveCurveViewRange * range, ContinuousFunctionStore * store) {
ContinuousFunctionCache * cache = store->cacheAtIndex(0);
assert(cache);
float tMin = range->xMin();
float tMax = range->xMax();
float tStep = ((tMax - tMin) / Graph::GraphView::k_graphStepDenominator) / ContinuousFunctionCache::k_parametricStepFactor;
ContinuousFunctionCache::PrepareForCaching(function, cache, tMin, tStep);
// Fill the cache
float t;
for (int i = 0; i < Ion::Display::Width / 2; i++) {
t = tMin + i*cache->step();
function->evaluateXYAtParameter(t, context);
}
function->setCache(nullptr);
for (int i = 0; i < Ion::Display::Width / 2; i++) {
t = tMin + i*cache->step();
Coordinate2D<float> cacheValues = cache->valueForParameter(function, context, t);
Coordinate2D<float> functionValues = function->evaluateXYAtParameter(t, context);
quiz_assert(floatEquals(cacheValues.x1(), functionValues.x1()));
quiz_assert(floatEquals(cacheValues.x2(), functionValues.x2()));
}
}
void assert_cache_stays_valid(ContinuousFunction::PlotType type, const char * definition, float rangeXMin = -5, float rangeXMax = 5) {
GlobalContext globalContext;
ContinuousFunctionStore functionStore;
InteractiveCurveViewRange graphRange;
graphRange.setXMin(rangeXMin);
graphRange.setXMax(rangeXMax);
graphRange.setYMin(-3.f);
graphRange.setYMax(3.f);
CurveViewCursor cursor;
ContinuousFunction * function = addFunction(&functionStore, type, definition, &globalContext);
Coordinate2D<float> origin = function->evaluateXYAtParameter(0.f, &globalContext);
cursor.moveTo(0.f, origin.x1(), origin.x2());
if (type == ContinuousFunction::PlotType::Cartesian) {
assert_cartesian_cache_stays_valid_while_panning(function, &globalContext, &graphRange, &cursor, &functionStore, 2.f);
assert_cartesian_cache_stays_valid_while_panning(function, &globalContext, &graphRange, &cursor, &functionStore, -0.4f);
} else {
assert_check_polar_cache_against_function(function, &globalContext, &graphRange, &functionStore);
}
functionStore.removeAll();
}
QUIZ_CASE(graph_caching) {
assert_cache_stays_valid(ContinuousFunction::PlotType::Cartesian, "x");
assert_cache_stays_valid(ContinuousFunction::PlotType::Cartesian, "x^2");
assert_cache_stays_valid(ContinuousFunction::PlotType::Cartesian, "sin(x)");
assert_cache_stays_valid(ContinuousFunction::PlotType::Cartesian, "sin(x)", -1e6f, 2e8f);
assert_cache_stays_valid(ContinuousFunction::PlotType::Cartesian, "sin(x^2)");
assert_cache_stays_valid(ContinuousFunction::PlotType::Cartesian, "1/x");
assert_cache_stays_valid(ContinuousFunction::PlotType::Cartesian, "1/x", -5e-5f, 5e-5f);
assert_cache_stays_valid(ContinuousFunction::PlotType::Cartesian, "-^x");
assert_cache_stays_valid(ContinuousFunction::PlotType::Polar, "1", 0.f, 360.f);
assert_cache_stays_valid(ContinuousFunction::PlotType::Polar, "θ", 0.f, 360.f);
assert_cache_stays_valid(ContinuousFunction::PlotType::Polar, "sin(θ)", 0.f, 360.f);
assert_cache_stays_valid(ContinuousFunction::PlotType::Polar, "sin(θ)", 2e-4f, 1e-3f);
assert_cache_stays_valid(ContinuousFunction::PlotType::Polar, "cos(5θ)", 0.f, 360.f);
assert_cache_stays_valid(ContinuousFunction::PlotType::Polar, "cos(5θ)", -1e8f, 1e8f);
}
}

View File

@@ -1,5 +1,7 @@
app_shared_test_src = $(addprefix apps/shared/,\
continuous_function.cpp\
continuous_function_cache.cpp \
curve_view_cursor.cpp \
curve_view_range.cpp \
curve_view.cpp \
dots.cpp \
@@ -24,9 +26,7 @@ app_shared_src = $(addprefix apps/shared/,\
buffer_function_title_cell.cpp \
buffer_text_view_with_text_field.cpp \
button_with_separator.cpp \
continuous_function_cache.cpp \
cursor_view.cpp \
curve_view_cursor.cpp \
editable_cell_table_view_controller.cpp \
expression_field_delegate_app.cpp \
expression_model_list_controller.cpp \