1 /*
2  * This file is part of Licq, an instant messaging client for UNIX.
3  * Copyright (C) 2010-2013 Licq Developers <licq-dev@googlegroups.com>
4  *
5  * Licq is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * Licq is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with Licq; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  */
19 
20 #include "../plugin.h"
21 #include "../plugininstance.h"
22 
23 #include <licq/plugin/pluginfactory.h>
24 #include <licq/plugin/plugininterface.h>
25 
26 #include <gtest/gtest.h>
27 #include <gmock/gmock.h>
28 
29 // licq.cpp
30 static const char* argv0 = "test";
31 char** global_argv = const_cast<char**>(&argv0);
32 
33 using LicqDaemon::Plugin;
34 using LicqDaemon::PluginInstance;
35 using LicqDaemon::DynamicLibrary;
36 using LicqDaemon::PluginThread;
37 
38 using ::testing::_;
39 using ::testing::InSequence;
40 using ::testing::Invoke;
41 using ::testing::Return;
42 
43 namespace LicqTest
44 {
45 
46 // Dummy interfaces
47 class InternalFactoryInterface { };
48 class InternalInstanceInterface { };
49 class UnImplementedInterface { };
50 
51 class MockPluginFactory : public Licq::PluginFactory,
52                           public InternalFactoryInterface
53 {
54 public:
55   MOCK_CONST_METHOD0(name, std::string());
56   MOCK_CONST_METHOD0(version, std::string());
57   MOCK_METHOD1(destroyPlugin, void(Licq::PluginInterface* plugin));
58 };
59 
60 class MockPlugin : public Licq::PluginInterface,
61                    public InternalInstanceInterface
62 {
63 public:
64   MOCK_METHOD2(init, bool(int argc, char** argv));
65   MOCK_METHOD0(run, int());
66   MOCK_METHOD0(shutdown, void());
67 };
68 
69 class TestPlugin : public Plugin
70 {
71 public:
72   typedef boost::shared_ptr<TestPlugin> Ptr;
73 
TestPlugin(DynamicLibrary::Ptr lib,boost::shared_ptr<Licq::PluginFactory> factory)74   TestPlugin(DynamicLibrary::Ptr lib,
75              boost::shared_ptr<Licq::PluginFactory> factory)
76     : Plugin(lib),
77       myFactory(factory)
78   {
79     // Empty
80   }
81 
82   // From Plugin
factory()83   boost::shared_ptr<Licq::PluginFactory> factory()
84   {
85     return myFactory;
86   }
87 
88 protected:
89   // From Plugin
factory() const90   boost::shared_ptr<const Licq::PluginFactory> factory() const
91   {
92     return myFactory;
93   }
94 
95 private:
96   boost::shared_ptr<Licq::PluginFactory> myFactory;
97 };
98 
99 class TestPluginInstance : public PluginInstance
100 {
101 public:
102   bool myIsCreated;
103 
TestPluginInstance(int id,TestPlugin::Ptr plugin,PluginThread::Ptr thread,boost::shared_ptr<Licq::PluginInterface> interface)104   TestPluginInstance(
105       int id, TestPlugin::Ptr plugin, PluginThread::Ptr thread,
106       boost::shared_ptr<Licq::PluginInterface> interface)
107     : PluginInstance(id, thread),
108       myIsCreated(false),
109       myPlugin(plugin),
110       myInterface(interface)
111   {
112     // Empty
113   }
114 
~TestPluginInstance()115   ~TestPluginInstance()
116   {
117     destroyInstance();
118   }
119 
120   // For test
destroyInstance()121   void destroyInstance()
122   {
123     if (myInterface)
124       myPlugin->factory()->destroyPlugin(myInterface.get());
125     myInterface.reset();
126     myPlugin.reset();
127   }
128 
129 protected:
130   // From PluginInstance
createInterface()131   void createInterface() { myIsCreated = true; }
132 
interface()133   boost::shared_ptr<Licq::PluginInterface> interface()
134   {
135     return myInterface;
136   }
137 
interface() const138   boost::shared_ptr<const Licq::PluginInterface> interface() const
139   {
140     return myInterface;
141   }
142 
143 private:
144   TestPlugin::Ptr myPlugin;
145   boost::shared_ptr<Licq::PluginInterface> myInterface;
146 };
147 
nullDeleter(void *)148 static void nullDeleter(void*) { /* Empty */ }
149 
150 struct PluginFixture : public ::testing::Test
151 {
152   DynamicLibrary::Ptr myLib;
153   PluginThread::Ptr myThread;
154   MockPluginFactory myMockFactory;
155   MockPlugin myMockInterface;
156   TestPlugin plugin;
157   TestPluginInstance instance;
158 
159   pthread_t myPluginThreadId;
160 
PluginFixtureLicqTest::PluginFixture161   PluginFixture() :
162     myLib(new DynamicLibrary("")),
163     myThread(new PluginThread()),
164     plugin(myLib,
165            boost::shared_ptr<MockPluginFactory>(&myMockFactory, &nullDeleter)),
166     instance(1, boost::shared_ptr<TestPlugin>(&plugin, &nullDeleter),
167              myThread,
168              boost::shared_ptr<MockPlugin>(&myMockInterface, &nullDeleter)),
169     myPluginThreadId(0)
170   {
171     EXPECT_CALL(myMockFactory, destroyPlugin(&myMockInterface));
172   }
173 
~PluginFixtureLicqTest::PluginFixture174   ~PluginFixture()
175   {
176     myThread->cancel();
177   }
178 
compareThreadLicqTest::PluginFixture179   int compareThread()
180   {
181     myPluginThreadId = ::pthread_self();
182     return myThread->isThread(::pthread_self()) ? 5 : -5;
183   }
184 };
185 
TEST_F(PluginFixture,callApiFunctions)186 TEST_F(PluginFixture, callApiFunctions)
187 {
188   InSequence dummy;
189   EXPECT_CALL(myMockFactory, name());
190   EXPECT_CALL(myMockFactory, version());
191   EXPECT_CALL(myMockInterface, shutdown());
192 
193   // Verify that the calls are forwarded to the interface
194   plugin.name();
195   plugin.version();
196   instance.shutdown();
197 }
198 
TEST_F(PluginFixture,castToInternalFactoryInterface)199 TEST_F(PluginFixture, castToInternalFactoryInterface)
200 {
201   Plugin::Ptr ptr(&plugin, &nullDeleter);
202 
203   EXPECT_FALSE(plugin_internal_cast<UnImplementedInterface>(ptr));
204   EXPECT_FALSE(plugin_internal_cast<InternalInstanceInterface>(ptr));
205   EXPECT_EQ(plugin_internal_cast<InternalFactoryInterface>(ptr).get(),
206             &myMockFactory);
207 }
208 
TEST_F(PluginFixture,castToInternalInstanceInterface)209 TEST_F(PluginFixture, castToInternalInstanceInterface)
210 {
211   PluginInstance::Ptr ptr(&instance, &nullDeleter);
212 
213   EXPECT_FALSE(plugin_internal_cast<UnImplementedInterface>(ptr));
214   EXPECT_FALSE(plugin_internal_cast<InternalFactoryInterface>(ptr));
215   EXPECT_EQ(plugin_internal_cast<InternalInstanceInterface>(ptr).get(),
216             &myMockInterface);
217 }
218 
219 static int DeleteCount = 0;
countingNullDeleter(void *)220 void countingNullDeleter(void*)
221 {
222   DeleteCount += 1;
223 }
224 
TEST_F(PluginFixture,lifeTimeOfCastedFactory)225 TEST_F(PluginFixture, lifeTimeOfCastedFactory)
226 {
227   DeleteCount = 0;
228 
229   // Make the instance drop the plugin shared_ptr
230   instance.destroyInstance();
231 
232   // The plugin should not be "deleted" until both the plugin and the interface
233   // has gone out of scope.
234 
235   {
236     boost::shared_ptr<InternalFactoryInterface> factory;
237     {
238       Plugin::Ptr ptr(&plugin, &countingNullDeleter);
239       factory = plugin_internal_cast<InternalFactoryInterface>(ptr);
240       EXPECT_EQ(ptr.use_count(), factory.use_count());
241     }
242     EXPECT_EQ(0, DeleteCount);
243   }
244 
245   EXPECT_EQ(1, DeleteCount);
246 }
247 
TEST_F(PluginFixture,lifeTimeOfCastedInstance)248 TEST_F(PluginFixture, lifeTimeOfCastedInstance)
249 {
250   DeleteCount = 0;
251 
252   // The instance should not be "deleted" until both the instance and the
253   // interface has gone out of scope.
254 
255   {
256     boost::shared_ptr<InternalInstanceInterface> interface;
257     {
258       PluginInstance::Ptr ptr(&instance, &countingNullDeleter);
259       interface = plugin_internal_cast<InternalInstanceInterface>(ptr);
260       EXPECT_EQ(ptr.use_count(), interface.use_count());
261     }
262     EXPECT_EQ(0, DeleteCount);
263   }
264 
265   EXPECT_EQ(1, DeleteCount);
266 }
267 
268 struct CallbackData
269 {
270   bool myCalled;
271   const PluginInstance* myInstance;
272   pthread_t myThreadId;
273 
CallbackDataLicqTest::CallbackData274   CallbackData() : myCalled(false), myInstance(NULL), myThreadId(0) { }
275 };
276 
277 static CallbackData CreateCallbackData;
createCallback(const PluginInstance & instance)278 static void createCallback(const PluginInstance& instance)
279 {
280   CreateCallbackData.myCalled = true;
281   CreateCallbackData.myInstance = &instance;
282   CreateCallbackData.myThreadId = ::pthread_self();
283 }
284 
TEST_F(PluginFixture,create)285 TEST_F(PluginFixture, create)
286 {
287   CreateCallbackData = CallbackData();
288   EXPECT_FALSE(instance.myIsCreated);
289 
290   EXPECT_TRUE(instance.create(&createCallback));
291   EXPECT_TRUE(instance.myIsCreated);
292 
293   EXPECT_TRUE(CreateCallbackData.myCalled);
294   EXPECT_EQ(&instance, CreateCallbackData.myInstance);
295   EXPECT_TRUE(myThread->isThread(CreateCallbackData.myThreadId));
296 }
297 
TEST_F(PluginFixture,init)298 TEST_F(PluginFixture, init)
299 {
300   EXPECT_CALL(myMockInterface, init(1, _));
301   instance.init(0, NULL);
302 }
303 
304 static CallbackData StartCallbackData;
startCallback(const PluginInstance & instance)305 static void startCallback(const PluginInstance& instance)
306 {
307   StartCallbackData.myCalled = true;
308   StartCallbackData.myInstance = &instance;
309   StartCallbackData.myThreadId = ::pthread_self();
310 }
311 
312 static CallbackData ExitCallbackData;
exitCallback(const PluginInstance & instance)313 static void exitCallback(const PluginInstance& instance)
314 {
315   ExitCallbackData.myCalled = true;
316   ExitCallbackData.myInstance = &instance;
317   ExitCallbackData.myThreadId = ::pthread_self();
318 }
319 
TEST_F(PluginFixture,run)320 TEST_F(PluginFixture, run)
321 {
322   EXPECT_CALL(myMockInterface, run())
323       .WillOnce(Invoke(this, &PluginFixture::compareThread));
324   myPluginThreadId = 0;
325   StartCallbackData = CallbackData();
326   ExitCallbackData = CallbackData();
327 
328   instance.run(&startCallback, &exitCallback);
329   EXPECT_EQ(5, instance.joinThread());
330 
331   EXPECT_TRUE(StartCallbackData.myCalled);
332   EXPECT_EQ(&instance, StartCallbackData.myInstance);
333   EXPECT_TRUE(::pthread_equal(myPluginThreadId, StartCallbackData.myThreadId));
334 
335   EXPECT_TRUE(ExitCallbackData.myCalled);
336   EXPECT_EQ(&instance, ExitCallbackData.myInstance);
337   EXPECT_TRUE(::pthread_equal(myPluginThreadId, ExitCallbackData.myThreadId));
338 }
339 
340 } // namespace LicqTest
341