diff options
author | Daniel Burrows <Daniel Burrows Daniel_Burrows@alumni.brown.edu> | 2010-05-24 09:36:33 -0700 |
---|---|---|
committer | Daniel Burrows <Daniel Burrows Daniel_Burrows@alumni.brown.edu> | 2010-05-24 09:36:33 -0700 |
commit | 8b81c0c1e773819f1d531b4d44388193bb93fd9e (patch) | |
tree | 605bc7357c5cbb7aa870ec47d8de1fcd359926f2 | |
parent | 88e3dc1c2841098538d30c13fb2ce5d164ced183 (diff) | |
download | aptitude-8b81c0c1e773819f1d531b4d44388193bb93fd9e.tar.gz |
Start using google-mock to test the search input controller logic.
I use google-mock to create a mock implementation of the view, then
test that the logic interacts with it as expected. Right now, it
doesn't.
The ability to do this was part of why I wanted to separate the logic
from the view.
I haven't added google-mock to configure.ac yet; need to do that later.
-rw-r--r-- | site_scons/aptitude_configure.py | 3 | ||||
-rw-r--r-- | site_scons/aptitude_configure_checks.py | 27 | ||||
-rw-r--r-- | src/gtk/views/mocks/search_input.h | 70 | ||||
-rw-r--r-- | tests/Makefile.am | 7 | ||||
-rw-r--r-- | tests/SConscript | 28 | ||||
-rw-r--r-- | tests/boost_test_main.cc | 11 | ||||
-rw-r--r-- | tests/test_search_input_controller.cc | 131 |
7 files changed, 276 insertions, 1 deletions
diff --git a/site_scons/aptitude_configure.py b/site_scons/aptitude_configure.py index f46a31b5..46b4c184 100644 --- a/site_scons/aptitude_configure.py +++ b/site_scons/aptitude_configure.py @@ -132,6 +132,9 @@ the Boost unit tests need.''' TryLibrary('boost_unit_test_framework') ]), "Can't find Boost.Test") + RequireCheck(conf.CheckForGoogleMock(), + "Can't find google-mock") + conf.Finish() def DoConfigureCppunitTests(env): diff --git a/site_scons/aptitude_configure_checks.py b/site_scons/aptitude_configure_checks.py index 75eee9cc..fd6006f9 100644 --- a/site_scons/aptitude_configure_checks.py +++ b/site_scons/aptitude_configure_checks.py @@ -236,6 +236,33 @@ int main(int argc, char **argv) boost::iostreams::filtering_ostream compressed_devnull(boost::iostreams::zlib_compressor(9) | devnull); }''', context.env['CXXFILESUFFIX']) +@ConfigureCheck("Checking for google-mock") +def CheckForGoogleMock(context): + """Look for gmock (headers and library). + +Brings gtest along for the ride, because otherwise gmock won't link.""" + + context.env.Append(LIBS = [ 'gmock', 'gtest' ]) + + return context.TryLink(''' +#include <gmock/gmock.h> + +class FooMock +{ +public: + MOCK_METHOD1(foo, void(int)); +}; + +int main(int argc, char **argv) +{ + ::testing::GTEST_FLAG(throw_on_failure) = true; + ::testing::InitGoogleMock(&argc, argv); + + FooMock mock; + mock.foo(4); +}''', context.env['CXXFILESUFFIX']) + + @ConfigureCheck("Checking for Boost.Test") def CheckForBoostTest(context): """Look for Boost.Test.""" diff --git a/src/gtk/views/mocks/search_input.h b/src/gtk/views/mocks/search_input.h new file mode 100644 index 00000000..f70a0b5e --- /dev/null +++ b/src/gtk/views/mocks/search_input.h @@ -0,0 +1,70 @@ +/** \file search_input.h */ // -*-c++-*- + +// Copyright (C) 2010 Daniel Burrows +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, write to +// the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +// Boston, MA 02111-1307, USA. + +#ifndef APTITUDE_GTK_VIEWS_MOCKS_SEARCH_INPUT_H +#define APTITUDE_GTK_VIEWS_MOCKS_SEARCH_INPUT_H + +#include <gmock/gmock.h> +#include <gtk/views/search_input.h> + +namespace gui +{ + namespace views + { + namespace mocks + { + /** \brief Mock implementation of search_input for use in unit + * tests. + * + * Provides signals which, by default, are connected by the + * connect_() methods. + */ + class search_input : public views::search_input + { + public: + MOCK_METHOD0(get_search_text, Glib::ustring()); + MOCK_METHOD1(set_search_text, void(const Glib::ustring &)); + MOCK_METHOD1(set_error_message, void(const Glib::ustring &)); + MOCK_METHOD1(set_input_validity, void(bool)); + MOCK_METHOD1(set_find_sensitivity, void(bool)); + MOCK_METHOD1(connect_search_text_changed, sigc::connection(const sigc::slot<void> &)); + MOCK_METHOD1(connect_search, sigc::connection(const sigc::slot<void> &)); + + sigc::signal<void> signal_search_text_changed; + sigc::signal<void> signal; + + search_input() + { + using testing::_; + using testing::Invoke; + + ON_CALL(*this, connect_search_text_changed(_)) + .WillByDefault(Invoke(&signal_search_text_changed, + &sigc::signal<void>::connect)); + + ON_CALL(*this, connect_search(_)) + .WillByDefault(Invoke(&signal, + &sigc::signal<void>::connect)); + } + }; + } + } +} + +#endif // APTITUDE_GTK_VIEWS_MOCKS_SEARCH_INPUT_H diff --git a/tests/Makefile.am b/tests/Makefile.am index 9a630f0d..755c632d 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -2,13 +2,17 @@ MAINTAINERCLEANFILES = Makefile.in INCLUDES = -I$(top_builddir) -I$(top_srcdir) -I$(top_srcdir)/src -I$(srcdir) BOOST_TEST_LDFLAGS = @BOOST_UNIT_TEST_LIBS@ +GMOCK_LDFLAGS = -lgmock -lgtest AM_CPPFLAGS = -DBOOST_TEST_DYN_LINK -DSRCDIR=\"$(srcdir)\" LDADD = $(top_builddir)/src/loggers.o \ +$(top_builddir)/src/gtk/controllers/libgtk-controllers.a \ +$(top_builddir)/src/gtk/views/libgtk-views.a \ $(top_builddir)/src/generic/apt/matching/libgeneric-matching.a \ $(top_builddir)/src/generic/apt/libgeneric-apt.a \ $(top_builddir)/src/generic/apt/matching/libgeneric-matching.a \ $(top_builddir)/src/generic/problemresolver/libgeneric-problemresolver.a \ -$(top_builddir)/src/generic/util/libgeneric-util.a -lcppunit $(BOOST_TEST_LDFLAGS) +$(top_builddir)/src/generic/util/libgeneric-util.a -lcppunit \ +$(BOOST_TEST_LDFLAGS) $(GMOCK_LDFLAGS) check_PROGRAMS = cppunit_test boost_test @@ -51,4 +55,5 @@ boost_test_SOURCES = \ test_dynamic_set.cc \ test_enumerator.cc \ test_file_cache.cc \ + test_search_input_controller.cc \ test_sqlite.cc diff --git a/tests/SConscript b/tests/SConscript index 658d47c0..b41e3358 100644 --- a/tests/SConscript +++ b/tests/SConscript @@ -67,15 +67,43 @@ boost_test_sources = [ 'test_dynamic_set.cc', 'test_enumerator.cc', 'test_file_cache.cc', + 'test_search_input_controller.cc', 'test_sqlite.cc' ] boost_test_extra_deps = [ '../src/loggers.o', + '../src/generic/apt/apt.o', + '../src/generic/apt/aptcache.o', + '../src/generic/apt/aptitude_resolver.o', + '../src/generic/apt/aptitude_resolver_cost_settings.o', + '../src/generic/apt/aptitude_resolver_cost_syntax.o', + '../src/generic/apt/aptitude_resolver_cost_types.o', + '../src/generic/apt/aptitude_resolver_universe.o', + '../src/generic/apt/aptitudepolicy.o', + '../src/generic/apt/config_signal.o', + '../src/generic/apt/download_queue.o', + '../src/generic/apt/dump_packages.o', + '../src/generic/apt/matching/compare_patterns.o', + '../src/generic/apt/matching/match.o', + '../src/generic/apt/matching/parse.o', + '../src/generic/apt/matching/pattern.o', + '../src/generic/apt/matching/serialize.o', + '../src/generic/apt/pkg_hier.o', + '../src/generic/apt/resolver_manager.o', + '../src/generic/apt/tags.o', + '../src/generic/apt/tasks.o', + '../src/generic/problemresolver/cost.o', + '../src/generic/problemresolver/cost_limits.o', + '../src/generic/problemresolver/incremental_expression.o', '../src/generic/util/file_cache.o', + '../src/generic/util/refcounted_base.o', '../src/generic/util/sqlite.o', '../src/generic/util/temp.o', + '../src/generic/util/undo.o', '../src/generic/util/util.o', + '../src/gtk/controllers/search_input.o', + '../src/gtk/views/search_input.o', ] test_data_files = [ diff --git a/tests/boost_test_main.cc b/tests/boost_test_main.cc index 40bf9eea..a4732d51 100644 --- a/tests/boost_test_main.cc +++ b/tests/boost_test_main.cc @@ -4,6 +4,8 @@ #include <boost/test/unit_test.hpp> +#include <gmock/gmock.h> + #include <log4cxx/basicconfigurator.h> #include <log4cxx/level.h> #include <log4cxx/logger.h> @@ -27,6 +29,15 @@ int main(int argc, char **argv) { argv0 = argv[0]; + // TODO: setting throw_on_failure is a hack; instead, we should + // configure the mock framework to emit Boost.Test errors. + // Unfortunately, I can't access the documentation (for the Google + // Test event listener API) that would tell me how to do this + // without a network connection. I hate libraries without offline + // documentation. + ::testing::GTEST_FLAG(throw_on_failure) = true; + ::testing::InitGoogleMock(&argc, argv); + bool debug = false; for(int i = 1; i < argc; ++i) { diff --git a/tests/test_search_input_controller.cc b/tests/test_search_input_controller.cc new file mode 100644 index 00000000..0eb54ba0 --- /dev/null +++ b/tests/test_search_input_controller.cc @@ -0,0 +1,131 @@ +/** \file test_search_input.cc */ + +// Copyright (C) 2010 Daniel Burrows +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, write to +// the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +// Boston, MA 02111-1307, USA. + +#include <generic/apt/matching/compare_patterns.h> +#include <generic/apt/matching/pattern.h> +#include <gtk/controllers/search_input.h> +#include <gtk/views/mocks/search_input.h> + +#include <boost/make_shared.hpp> +#include <boost/test/unit_test.hpp> + +namespace ctrls = gui::controllers; +namespace views = gui::views; +namespace mocks = gui::views::mocks; + +using aptitude::matching::compare_patterns; +using aptitude::matching::pattern; +using boost::make_shared; +using boost::shared_ptr; +using cwidget::util::ref_ptr; +using testing::InSequence; +using testing::NiceMock; +using testing::StrictMock; + +namespace +{ + MATCHER_P(PatternsEqual, + expected, + "") + { + return compare_patterns(arg, expected) == 0; + } + + // Mock class that lets us capture the controller's outgoing + // signals. + class controller_callbacks + { + public: + MOCK_METHOD2(activated, void(Glib::ustring, cwidget::util::ref_ptr<aptitude::matching::pattern>)); + }; + + struct SearchInputTest + { + boost::shared_ptr<mocks::search_input> view; + boost::shared_ptr<mocks::search_input> viewNice; + boost::shared_ptr<mocks::search_input> viewStrict; + + controller_callbacks callbacks; + controller_callbacks callbacksNice; + controller_callbacks callbacksStrict; + + // Controllers are initialized lazily, to give us a chance to + // register expectations with the views first. + private: + boost::shared_ptr<ctrls::search_input> controller; + boost::shared_ptr<ctrls::search_input> controllerNice; + boost::shared_ptr<ctrls::search_input> controllerStrict; + + public: + boost::shared_ptr<ctrls::search_input> get_controller() + { + if(controller.get() == NULL) + { + controller = ctrls::create_search_input(view); + controller->connect_activated(sigc::mem_fun(callbacks, + &controller_callbacks::activated)); + } + return controller; + } + + boost::shared_ptr<ctrls::search_input> get_controllerNice() + { + if(controllerNice.get() == NULL) + { + controllerNice = ctrls::create_search_input(view); + controllerNice->connect_activated(sigc::mem_fun(callbacksNice, + &controller_callbacks::activated)); + } + return controllerNice; + } + + boost::shared_ptr<ctrls::search_input> get_controllerStrict() + { + if(controllerStrict.get() == NULL) + { + controllerStrict = ctrls::create_search_input(view); + controllerStrict->connect_activated(sigc::mem_fun(callbacksStrict, + &controller_callbacks::activated)); + } + return controllerStrict; + } + + SearchInputTest() + : view(make_shared<mocks::search_input>()), + viewNice(make_shared<NiceMock<mocks::search_input> >()), + viewStrict(make_shared<StrictMock<mocks::search_input> >()) + { + } + }; +} + +BOOST_FIXTURE_TEST_CASE(testEnteringCorrectTextSearches, SearchInputTest) +{ + ref_ptr<pattern> p = pattern::make_installed(); + Glib::ustring p_text = "?installed"; + + { + InSequence dummy; + + EXPECT_CALL(*view, set_search_text(p_text)); + EXPECT_CALL(callbacks, activated(p_text, PatternsEqual(p))); + } + + get_controller()->enter_text(p_text); +} |