blob: fae03878456cf8677aa35519101d7d4b1cd75ebb [file] [log] [blame]
Amna4d2942e2024-05-01 18:27:34 -04001/*
2 * Copyright (C) 2024 Savoir-faire Linux Inc.
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18#include "connectionmanager.h"
19#include "multiplexed_socket.h"
20#include "test_runner.h"
21#include "certstore.h"
22
23#include <opendht/log.h>
24#include <asio/executor_work_guard.hpp>
25#include <asio/io_context.hpp>
26#include <fmt/compile.h>
27
28#include <cppunit/TestAssert.h>
29#include <cppunit/TestFixture.h>
30#include <cppunit/extensions/HelperMacros.h>
31
32#include <condition_variable>
33#include <iostream>
34#include <filesystem>
35
36using namespace std::literals::chrono_literals;
37
38namespace dhtnet {
39namespace test {
40
41struct ConnectionHandler
42{
43 dht::crypto::Identity id;
44 std::shared_ptr<Logger> logger;
45 std::shared_ptr<tls::CertificateStore> certStore;
46 std::shared_ptr<dht::DhtRunner> dht;
47 std::shared_ptr<ConnectionManager> connectionManager;
48 std::shared_ptr<asio::io_context> ioContext;
49 std::shared_ptr<std::thread> ioContextRunner;
50};
51
52class PeerDiscoveryTest : public CppUnit::TestFixture
53{
54public:
55 PeerDiscoveryTest() {
56 pj_log_set_level(0);
57 pj_log_set_log_func([](int level, const char* data, int /*len*/) {});
58 testDir_ = std::filesystem::current_path() / "tmp_tests_PeerDiscoveryTest";
59 }
60 ~PeerDiscoveryTest() {}
61 static std::string name() { return "PeerDiscoveryTest"; }
62 void setUp();
63 void tearDown();
64
65 dht::crypto::Identity org1Id, org2Id;
66 dht::crypto::Identity aliceId, bobId;
67 dht::crypto::Identity aliceDevice1Id, bobDevice1Id;
68
69 std::unique_ptr<ConnectionHandler> alice;
70 std::unique_ptr<ConnectionHandler> bob;
71
72 std::mutex mtx;
73 std::shared_ptr<asio::io_context> ioContext;
74 std::shared_ptr<std::thread> ioContextRunner;
75 std::shared_ptr<Logger> logger = dht::log::getStdLogger();
76 std::shared_ptr<IceTransportFactory> factory;
77
78private:
79 std::unique_ptr<ConnectionHandler> setupHandler(const dht::crypto::Identity& id);
80 std::filesystem::path testDir_;
81
82 void testConnectDevice();
83 CPPUNIT_TEST_SUITE(PeerDiscoveryTest);
84 CPPUNIT_TEST(testConnectDevice);
85
86 CPPUNIT_TEST_SUITE_END();
87};
88
89CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(PeerDiscoveryTest, PeerDiscoveryTest::name());
90
91std::unique_ptr<ConnectionHandler>
92PeerDiscoveryTest::setupHandler(const dht::crypto::Identity& id)
93{
94 auto h = std::make_unique<ConnectionHandler>();
95 h->id = id;
96 h->logger = logger;
97 h->certStore = std::make_shared<tls::CertificateStore>(testDir_ / id.second->getName(), nullptr/*h->logger*/);
98 h->ioContext = ioContext;
99 h->ioContextRunner = ioContextRunner;
100
101 dht::DhtRunner::Config dhtConfig;
102 dhtConfig.dht_config.id = h->id;
103 dhtConfig.threaded = true;
104 dhtConfig.peer_discovery = true;
105 dhtConfig.peer_publish = true;
106
107 dht::DhtRunner::Context dhtContext;
108
109 dhtContext.certificateStore = [c = h->certStore](const dht::InfoHash& pk_id) {
110 std::vector<std::shared_ptr<dht::crypto::Certificate>> ret;
111 if (auto cert = c->getCertificate(pk_id.toString()))
112 ret.emplace_back(std::move(cert));
113 return ret;
114 };
115 dhtContext.logger = h->logger;
116
117 h->dht = std::make_shared<dht::DhtRunner>();
118 h->dht->run(dhtConfig, std::move(dhtContext));
119 auto config = std::make_shared<ConnectionManager::Config>();
120 config->dht = h->dht;
121 config->id = h->id;
122 config->ioContext = h->ioContext;
123 config->factory = factory;
124 config->certStore = h->certStore;
125 config->cachePath = testDir_ / id.second->getName() / "temp";
126
127 h->connectionManager = std::make_shared<ConnectionManager>(config);
128 h->connectionManager->onICERequest([](const DeviceId&) { return true; });
129 h->connectionManager->onDhtConnected(h->id.first->getPublicKey());
130
131 return h;
132}
133
134void
135PeerDiscoveryTest::setUp()
136{
137 if (not org1Id.first) {
138 org1Id = dht::crypto::generateIdentity("org1");
139 org2Id = dht::crypto::generateIdentity("org2");
140 aliceId = dht::crypto::generateIdentity("alice", org1Id, 2048, true);
141 bobId = dht::crypto::generateIdentity("bob", org2Id, 2048, true);
142 aliceDevice1Id = dht::crypto::generateIdentity("aliceDevice1", aliceId);
143 bobDevice1Id = dht::crypto::generateIdentity("bobDevice1", bobId);
144 }
145
146 ioContext = std::make_shared<asio::io_context>();
147 ioContextRunner = std::make_shared<std::thread>([context = ioContext]() {
148 try {
149 auto work = asio::make_work_guard(*context);
150 context->run();
151 } catch (const std::exception& ex) {
152 fmt::print("Exception in ioContextRunner: {}\n", ex.what());
153 }
154 });
155
156 factory = std::make_unique<IceTransportFactory>(/*logger*/);
157 alice = setupHandler(aliceDevice1Id);
158 bob = setupHandler(bobDevice1Id);
159}
160
161void
162PeerDiscoveryTest::tearDown()
163{
164 ioContext->stop();
165
166 if (ioContextRunner && ioContextRunner->joinable()) {
167 ioContextRunner->join();
168 }
169
170 alice.reset();
171 bob.reset();
172 factory.reset();
173 std::filesystem::remove_all(testDir_);
174}
175
176void PeerDiscoveryTest::testConnectDevice()
177{
178 std::condition_variable bobConVar;
179 bool isBobRecvChanlReq = false;
180 bob->connectionManager->onChannelRequest(
181 [&](const std::shared_ptr<dht::crypto::Certificate>&,
182 const std::string& name) {
183 std::lock_guard lock{mtx};
184 isBobRecvChanlReq = name == "dummyName";
185 bobConVar.notify_one();
186 return true;
187 });
188
189 std::condition_variable alicConVar;
190 bool isAlicConnected = false;
191 alice->connectionManager->connectDevice(bob->id.second, "dummyName", [&](std::shared_ptr<ChannelSocket> socket, const DeviceId&) {
192 std::lock_guard lock{mtx};
193 if (socket) {
194 isAlicConnected = true;
195 }
196 alicConVar.notify_one();
197 });
198
199 std::unique_lock lock{mtx};
200 CPPUNIT_ASSERT(bobConVar.wait_for(lock, 30s, [&] { return isBobRecvChanlReq; }));
201 CPPUNIT_ASSERT(alicConVar.wait_for(lock, 30s, [&] { return isAlicConnected; }));
202}
203
204}
205}
206JAMI_TEST_RUNNER(dhtnet::test::PeerDiscoveryTest::name())