From 8eb4f2233b58dd11d276de2db44a8841224e15f5 Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Fri, 12 Oct 2012 00:35:18 +0200 Subject: [PATCH] Start on unit testing (adds a dependency on Google Test). Right now, we have a single trivial test. --- .gitignore | 3 +- Makefile | 41 ++++++++++++---- main.cpp => demo.cpp | 0 effect_chain_test.cpp | 109 ++++++++++++++++++++++++++++++++++++++++++ gtest_sdl_main.cpp | 15 ++++++ 5 files changed, 158 insertions(+), 10 deletions(-) rename main.cpp => demo.cpp (100%) create mode 100644 effect_chain_test.cpp create mode 100644 gtest_sdl_main.cpp diff --git a/.gitignore b/.gitignore index 0b4a5ba..6dffcb1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,9 @@ *.o *.a *.d -test *.jpg *.png perf.data *.dot +demo +effect_chain_test diff --git a/Makefile b/Makefile index ff76843..b575bb0 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,13 @@ +GTEST_DIR = /usr/src/gtest + CC=gcc CXX=g++ -CXXFLAGS=-Wall -g $(shell pkg-config --cflags eigen3 ) -LDFLAGS=-lSDL -lSDL_image -lGL -lrt +CXXFLAGS=-Wall -g -I$(GTEST_DIR)/include $(shell pkg-config --cflags eigen3 ) +LDFLAGS=-lSDL -lSDL_image -lGL -lrt -lpthread RANLIB=ranlib -TEST_OBJS=main.o +DEMO_OBJS=demo.o +TESTS=effect_chain_test # Core. LIB_OBJS=util.o widgets.o effect.o effect_chain.o @@ -31,11 +34,26 @@ LIB_OBJS += resize_effect.o LIB_OBJS += deconvolution_sharpen_effect.o LIB_OBJS += sandbox_effect.o -OBJS=$(TEST_OBJS) $(LIB_OBJS) +# Default target: +all: $(TESTS) demo + +# Google Test. +GDEMO_OBJS = gtest-all.o gtest_sdl_main.o + +gtest-all.o: $(GTEST_DIR)/src/gtest-all.cc + $(CXX) -MMD $(CPPFLAGS) -I$(GTEST_DIR) $(CXXFLAGS) -c $< -o $@ +gtest_sdl_main.o: gtest_sdl_main.cpp + $(CXX) -MMD $(CPPFLAGS) -I$(GTEST_DIR) $(CXXFLAGS) -c $< -o $@ -# A small test program (not a unit test). -test: libmovit.a $(TEST_OBJS) - $(CXX) -o test $(TEST_OBJS) libmovit.a $(LDFLAGS) +# Unit tests. +effect_chain_test: effect_chain_test.o $(GDEMO_OBJS) libmovit.a + $(CXX) -o $@ effect_chain_test.o $(GDEMO_OBJS) libmovit.a $(LDFLAGS) + +OBJS=$(DEMO_OBJS) $(LIB_OBJS) $(GDEMO_OBJS) + +# A small demo program. +demo: libmovit.a $(DEMO_OBJS) + $(CXX) -o demo $(DEMO_OBJS) libmovit.a $(LDFLAGS) # The library itself. libmovit.a: $(LIB_OBJS) @@ -49,6 +67,11 @@ DEPS=$(OBJS:.o=.d) -include $(DEPS) clean: - $(RM) test libmovit.a $(OBJS) $(DEPS) + $(RM) demo $(TESTS) libmovit.a $(OBJS) $(DEPS) + +check: $(TESTS) + for TEST in $(TESTS); do \ + ./$$TEST; \ + done -.PHONY: clean +.PHONY: clean check all diff --git a/main.cpp b/demo.cpp similarity index 100% rename from main.cpp rename to demo.cpp diff --git a/effect_chain_test.cpp b/effect_chain_test.cpp new file mode 100644 index 0000000..c8fd946 --- /dev/null +++ b/effect_chain_test.cpp @@ -0,0 +1,109 @@ +// Unit tests for EffectChain. + +#include "effect_chain.h" +#include "flat_input.h" +#include "sandbox_effect.h" +#include "opengl.h" +#include "gtest/gtest.h" + +#include +#include + +#include + +class EffectChainTester { +public: + EffectChainTester(const float *data, unsigned width, unsigned height, ColorSpace color_space, GammaCurve gamma_curve) + : chain(width, height), width(width), height(height) + { + ImageFormat format; + format.color_space = color_space; + format.gamma_curve = gamma_curve; + + FlatInput *input = new FlatInput(format, FORMAT_GRAYSCALE, GL_FLOAT, width, height); + input->set_pixel_data(data); + chain.add_input(input); + } + + EffectChain *get_chain() { return &chain; } + + void run(float *out_data, ColorSpace color_space, GammaCurve gamma_curve) + { + ImageFormat format; + format.color_space = color_space; + format.gamma_curve = gamma_curve; + chain.add_output(format); + chain.finalize(); + + glViewport(0, 0, width, height); + chain.render_to_screen(); + + glReadPixels(0, 0, width, height, GL_RED, GL_FLOAT, out_data); + + // Flip upside-down to compensate for different origin. + for (unsigned y = 0; y < height / 2; ++y) { + unsigned flip_y = height - y - 1; + for (unsigned x = 0; x < width; ++x) { + std::swap(out_data[y * width + x], out_data[flip_y * width + x]); + } + } + } + +private: + EffectChain chain; + unsigned width, height; +}; + +void expect_equal(const float *ref, const float *result, unsigned width, unsigned height) +{ + float largest_difference = -1.0f; + float squared_difference = 0.0f; + + for (unsigned y = 0; y < height; ++y) { + for (unsigned x = 0; x < width; ++x) { + float diff = ref[y * width + x] - result[y * width + x]; + largest_difference = std::max(largest_difference, fabsf(diff)); + squared_difference += diff * diff; + } + } + + const float largest_difference_limit = 1.5 / 255.0; + const float rms_limit = 0.5 / 255.0; + + EXPECT_LT(largest_difference, largest_difference_limit); + + float rms = sqrt(squared_difference) / (width * height); + EXPECT_LT(rms, rms_limit); + + if (largest_difference >= largest_difference_limit || rms >= rms_limit) { + fprintf(stderr, "Dumping matrices for easier debugging, since at least one test failed.\n"); + + fprintf(stderr, "Reference:\n"); + for (unsigned y = 0; y < height; ++y) { + for (unsigned x = 0; x < width; ++x) { + fprintf(stderr, "%7.4f ", ref[y * width + x]); + } + fprintf(stderr, "\n"); + } + + fprintf(stderr, "\nResult:\n"); + for (unsigned y = 0; y < height; ++y) { + for (unsigned x = 0; x < width; ++x) { + fprintf(stderr, "%7.4f ", result[y * width + x]); + } + fprintf(stderr, "\n"); + } + } +} + +TEST(EffectChainTest, Identity) { + float data[] = { + 0.0f, 0.25f, 0.3f, + 0.75f, 1.0f, 1.0f, + }; + float out_data[6]; + EffectChainTester tester(data, 3, 2, COLORSPACE_sRGB, GAMMA_LINEAR); + tester.run(out_data, COLORSPACE_sRGB, GAMMA_LINEAR); + + expect_equal(data, out_data, 3, 2); +} diff --git a/gtest_sdl_main.cpp b/gtest_sdl_main.cpp new file mode 100644 index 0000000..01eedf2 --- /dev/null +++ b/gtest_sdl_main.cpp @@ -0,0 +1,15 @@ +#include +#include "gtest/gtest.h" + +int main(int argc, char **argv) { + // Set up an OpenGL context using SDL. + SDL_Init(SDL_INIT_VIDEO); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 0); + SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 0); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + SDL_SetVideoMode(1280, 720, 0, SDL_OPENGL); + SDL_WM_SetCaption("OpenGL window for unit test", NULL); + + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} -- 2.39.2