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