michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=2 et sw=2 tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: // Original author: ekr@rtfm.com michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include "sigslot.h" michael@0: michael@0: #include "logging.h" michael@0: #include "nspr.h" michael@0: #include "nss.h" michael@0: #include "ssl.h" michael@0: michael@0: #include "nsThreadUtils.h" michael@0: #include "nsXPCOM.h" michael@0: michael@0: #include "dtlsidentity.h" michael@0: #include "nricectx.h" michael@0: #include "nricemediastream.h" michael@0: #include "transportflow.h" michael@0: #include "transportlayer.h" michael@0: #include "transportlayerdtls.h" michael@0: #include "transportlayerice.h" michael@0: #include "transportlayerlog.h" michael@0: #include "transportlayerloopback.h" michael@0: michael@0: #include "mtransport_test_utils.h" michael@0: #include "runnable_utils.h" michael@0: michael@0: #define GTEST_HAS_RTTI 0 michael@0: #include "gtest/gtest.h" michael@0: #include "gtest_utils.h" michael@0: michael@0: using namespace mozilla; michael@0: MOZ_MTLOG_MODULE("mtransport") michael@0: michael@0: MtransportTestUtils *test_utils; michael@0: michael@0: // Layer class which can't be initialized. michael@0: class TransportLayerDummy : public TransportLayer { michael@0: public: michael@0: TransportLayerDummy(bool allow_init, bool *destroyed) michael@0: : allow_init_(allow_init), michael@0: destroyed_(destroyed) { michael@0: *destroyed_ = false; michael@0: } michael@0: michael@0: virtual ~TransportLayerDummy() { michael@0: *destroyed_ = true; michael@0: } michael@0: michael@0: virtual nsresult InitInternal() { michael@0: return allow_init_ ? NS_OK : NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: virtual TransportResult SendPacket(const unsigned char *data, size_t len) { michael@0: MOZ_CRASH(); // Should never be called. michael@0: return 0; michael@0: } michael@0: michael@0: TRANSPORT_LAYER_ID("lossy") michael@0: michael@0: private: michael@0: bool allow_init_; michael@0: bool *destroyed_; michael@0: }; michael@0: michael@0: // Class to simulate various kinds of network lossage michael@0: class TransportLayerLossy : public TransportLayer { michael@0: public: michael@0: TransportLayerLossy() : loss_mask_(0), packet_(0) {} michael@0: ~TransportLayerLossy () {} michael@0: michael@0: virtual TransportResult SendPacket(const unsigned char *data, size_t len) { michael@0: MOZ_MTLOG(ML_NOTICE, LAYER_INFO << "SendPacket(" << len << ")"); michael@0: michael@0: if (loss_mask_ & (1 << (packet_ % 32))) { michael@0: MOZ_MTLOG(ML_NOTICE, "Dropping packet"); michael@0: ++packet_; michael@0: return len; michael@0: } michael@0: michael@0: ++packet_; michael@0: michael@0: return downward_->SendPacket(data, len); michael@0: } michael@0: michael@0: void SetLoss(uint32_t packet) { michael@0: loss_mask_ |= (1 << (packet & 32)); michael@0: } michael@0: michael@0: void StateChange(TransportLayer *layer, State state) { michael@0: TL_SET_STATE(state); michael@0: } michael@0: michael@0: void PacketReceived(TransportLayer *layer, const unsigned char *data, michael@0: size_t len) { michael@0: SignalPacketReceived(this, data, len); michael@0: } michael@0: michael@0: TRANSPORT_LAYER_ID("lossy") michael@0: michael@0: protected: michael@0: virtual void WasInserted() { michael@0: downward_->SignalPacketReceived. michael@0: connect(this, michael@0: &TransportLayerLossy::PacketReceived); michael@0: downward_->SignalStateChange. michael@0: connect(this, michael@0: &TransportLayerLossy::StateChange); michael@0: michael@0: TL_SET_STATE(downward_->state()); michael@0: } michael@0: michael@0: private: michael@0: uint32_t loss_mask_; michael@0: uint32_t packet_; michael@0: }; michael@0: michael@0: namespace { michael@0: class TransportTestPeer : public sigslot::has_slots<> { michael@0: public: michael@0: TransportTestPeer(nsCOMPtr target, std::string name) michael@0: : name_(name), target_(target), michael@0: received_(0), flow_(new TransportFlow(name)), michael@0: loopback_(new TransportLayerLoopback()), michael@0: logging_(new TransportLayerLogging()), michael@0: lossy_(new TransportLayerLossy()), michael@0: dtls_(new TransportLayerDtls()), michael@0: identity_(DtlsIdentity::Generate()), michael@0: ice_ctx_(NrIceCtx::Create(name, michael@0: name == "P2" ? michael@0: TransportLayerDtls::CLIENT : michael@0: TransportLayerDtls::SERVER)), michael@0: streams_(), candidates_(), michael@0: peer_(nullptr), michael@0: gathering_complete_(false) michael@0: { michael@0: std::vector stun_servers; michael@0: ScopedDeletePtr server(NrIceStunServer::Create( michael@0: std::string((char *)"216.93.246.14"), 3478)); michael@0: stun_servers.push_back(*server); michael@0: EXPECT_TRUE(NS_SUCCEEDED(ice_ctx_->SetStunServers(stun_servers))); michael@0: michael@0: dtls_->SetIdentity(identity_); michael@0: dtls_->SetRole(name == "P2" ? michael@0: TransportLayerDtls::CLIENT : michael@0: TransportLayerDtls::SERVER); michael@0: michael@0: nsresult res = identity_->ComputeFingerprint("sha-1", michael@0: fingerprint_, michael@0: sizeof(fingerprint_), michael@0: &fingerprint_len_); michael@0: EXPECT_TRUE(NS_SUCCEEDED(res)); michael@0: EXPECT_EQ(20u, fingerprint_len_); michael@0: } michael@0: michael@0: ~TransportTestPeer() { michael@0: test_utils->sts_target()->Dispatch( michael@0: WrapRunnable(this, &TransportTestPeer::DestroyFlow), michael@0: NS_DISPATCH_SYNC); michael@0: } michael@0: michael@0: michael@0: void DestroyFlow() { michael@0: if (flow_) { michael@0: loopback_->Disconnect(); michael@0: flow_ = nullptr; michael@0: } michael@0: ice_ctx_ = nullptr; michael@0: } michael@0: michael@0: void DisconnectDestroyFlow() { michael@0: loopback_->Disconnect(); michael@0: disconnect_all(); // Disconnect from the signals; michael@0: flow_ = nullptr; michael@0: } michael@0: michael@0: void SetDtlsAllowAll() { michael@0: nsresult res = dtls_->SetVerificationAllowAll(); michael@0: ASSERT_TRUE(NS_SUCCEEDED(res)); michael@0: } michael@0: michael@0: void SetDtlsPeer(TransportTestPeer *peer, int digests, unsigned int damage) { michael@0: unsigned int mask = 1; michael@0: michael@0: for (int i=0; ifingerprint_, michael@0: peer->fingerprint_len_); michael@0: if (damage & mask) michael@0: fingerprint_to_set[0]++; michael@0: michael@0: nsresult res = dtls_->SetVerificationDigest( michael@0: "sha-1", michael@0: fingerprint_to_set, michael@0: peer->fingerprint_len_); michael@0: michael@0: ASSERT_TRUE(NS_SUCCEEDED(res)); michael@0: michael@0: mask <<= 1; michael@0: } michael@0: } michael@0: michael@0: michael@0: void ConnectSocket_s(TransportTestPeer *peer) { michael@0: nsresult res; michael@0: res = loopback_->Init(); michael@0: ASSERT_EQ((nsresult)NS_OK, res); michael@0: michael@0: loopback_->Connect(peer->loopback_); michael@0: michael@0: ASSERT_EQ((nsresult)NS_OK, flow_->PushLayer(loopback_)); michael@0: ASSERT_EQ((nsresult)NS_OK, flow_->PushLayer(logging_)); michael@0: ASSERT_EQ((nsresult)NS_OK, flow_->PushLayer(lossy_)); michael@0: ASSERT_EQ((nsresult)NS_OK, flow_->PushLayer(dtls_)); michael@0: michael@0: flow_->SignalPacketReceived.connect(this, &TransportTestPeer::PacketReceived); michael@0: } michael@0: michael@0: void ConnectSocket(TransportTestPeer *peer) { michael@0: RUN_ON_THREAD(test_utils->sts_target(), michael@0: WrapRunnable(this, & TransportTestPeer::ConnectSocket_s, michael@0: peer), michael@0: NS_DISPATCH_SYNC); michael@0: } michael@0: michael@0: void InitIce() { michael@0: nsresult res; michael@0: michael@0: // Attach our slots michael@0: ice_ctx_->SignalGatheringStateChange. michael@0: connect(this, &TransportTestPeer::GatheringStateChange); michael@0: michael@0: char name[100]; michael@0: snprintf(name, sizeof(name), "%s:stream%d", name_.c_str(), michael@0: (int)streams_.size()); michael@0: michael@0: // Create the media stream michael@0: mozilla::RefPtr stream = michael@0: ice_ctx_->CreateStream(static_cast(name), 1); michael@0: ASSERT_TRUE(stream != nullptr); michael@0: streams_.push_back(stream); michael@0: michael@0: // Listen for candidates michael@0: stream->SignalCandidate. michael@0: connect(this, &TransportTestPeer::GotCandidate); michael@0: michael@0: // Create the transport layer michael@0: ice_ = new TransportLayerIce(name, ice_ctx_, stream, 1); michael@0: michael@0: // Assemble the stack michael@0: nsAutoPtr > layers( michael@0: new std::queue); michael@0: layers->push(ice_); michael@0: layers->push(dtls_); michael@0: michael@0: test_utils->sts_target()->Dispatch( michael@0: WrapRunnableRet(flow_, &TransportFlow::PushLayers, layers, &res), michael@0: NS_DISPATCH_SYNC); michael@0: michael@0: ASSERT_EQ((nsresult)NS_OK, res); michael@0: michael@0: // Listen for media events michael@0: flow_->SignalPacketReceived.connect(this, &TransportTestPeer::PacketReceived); michael@0: flow_->SignalStateChange.connect(this, &TransportTestPeer::StateChanged); michael@0: michael@0: // Start gathering michael@0: test_utils->sts_target()->Dispatch( michael@0: WrapRunnableRet(ice_ctx_, &NrIceCtx::StartGathering, &res), michael@0: NS_DISPATCH_SYNC); michael@0: ASSERT_TRUE(NS_SUCCEEDED(res)); michael@0: } michael@0: michael@0: void ConnectIce(TransportTestPeer *peer) { michael@0: peer_ = peer; michael@0: michael@0: // If gathering is already complete, push the candidates over michael@0: if (gathering_complete_) michael@0: GatheringComplete(); michael@0: } michael@0: michael@0: // New candidate michael@0: void GotCandidate(NrIceMediaStream *stream, const std::string &candidate) { michael@0: std::cerr << "Got candidate " << candidate << std::endl; michael@0: candidates_[stream->name()].push_back(candidate); michael@0: } michael@0: michael@0: void GatheringStateChange(NrIceCtx* ctx, michael@0: NrIceCtx::GatheringState state) { michael@0: (void)ctx; michael@0: if (state == NrIceCtx::ICE_CTX_GATHER_COMPLETE) { michael@0: GatheringComplete(); michael@0: } michael@0: } michael@0: michael@0: // Gathering complete, so send our candidates and start michael@0: // connecting on the other peer. michael@0: void GatheringComplete() { michael@0: nsresult res; michael@0: michael@0: // Don't send to the other side michael@0: if (!peer_) { michael@0: gathering_complete_ = true; michael@0: return; michael@0: } michael@0: michael@0: // First send attributes michael@0: test_utils->sts_target()->Dispatch( michael@0: WrapRunnableRet(peer_->ice_ctx_, michael@0: &NrIceCtx::ParseGlobalAttributes, michael@0: ice_ctx_->GetGlobalAttributes(), &res), michael@0: NS_DISPATCH_SYNC); michael@0: ASSERT_TRUE(NS_SUCCEEDED(res)); michael@0: michael@0: for (size_t i=0; ists_target()->Dispatch( michael@0: WrapRunnableRet(peer_->streams_[i], &NrIceMediaStream::ParseAttributes, michael@0: candidates_[streams_[i]->name()], &res), NS_DISPATCH_SYNC); michael@0: michael@0: ASSERT_TRUE(NS_SUCCEEDED(res)); michael@0: } michael@0: michael@0: // Start checks on the other peer. michael@0: test_utils->sts_target()->Dispatch( michael@0: WrapRunnableRet(peer_->ice_ctx_, &NrIceCtx::StartChecks, &res), michael@0: NS_DISPATCH_SYNC); michael@0: ASSERT_TRUE(NS_SUCCEEDED(res)); michael@0: } michael@0: michael@0: TransportResult SendPacket(const unsigned char* data, size_t len) { michael@0: TransportResult ret; michael@0: test_utils->sts_target()->Dispatch( michael@0: WrapRunnableRet(flow_, &TransportFlow::SendPacket, data, len, &ret), michael@0: NS_DISPATCH_SYNC); michael@0: michael@0: return ret; michael@0: } michael@0: michael@0: michael@0: void StateChanged(TransportFlow *flow, TransportLayer::State state) { michael@0: if (state == TransportLayer::TS_OPEN) { michael@0: std::cerr << "Now connected" << std::endl; michael@0: } michael@0: } michael@0: michael@0: void PacketReceived(TransportFlow * flow, const unsigned char* data, michael@0: size_t len) { michael@0: std::cerr << "Received " << len << " bytes" << std::endl; michael@0: ++received_; michael@0: } michael@0: michael@0: void SetLoss(uint32_t loss) { michael@0: lossy_->SetLoss(loss); michael@0: } michael@0: michael@0: TransportLayer::State state() { michael@0: TransportLayer::State tstate; michael@0: michael@0: RUN_ON_THREAD(test_utils->sts_target(), michael@0: WrapRunnableRet(flow_, &TransportFlow::state, &tstate), michael@0: NS_DISPATCH_SYNC); michael@0: michael@0: return tstate; michael@0: } michael@0: michael@0: bool connected() { michael@0: return state() == TransportLayer::TS_OPEN; michael@0: } michael@0: michael@0: bool failed() { michael@0: return state() == TransportLayer::TS_ERROR; michael@0: } michael@0: michael@0: size_t received() { return received_; } michael@0: michael@0: private: michael@0: std::string name_; michael@0: nsCOMPtr target_; michael@0: size_t received_; michael@0: mozilla::RefPtr flow_; michael@0: TransportLayerLoopback *loopback_; michael@0: TransportLayerLogging *logging_; michael@0: TransportLayerLossy *lossy_; michael@0: TransportLayerDtls *dtls_; michael@0: TransportLayerIce *ice_; michael@0: mozilla::RefPtr identity_; michael@0: mozilla::RefPtr ice_ctx_; michael@0: std::vector > streams_; michael@0: std::map > candidates_; michael@0: TransportTestPeer *peer_; michael@0: bool gathering_complete_; michael@0: unsigned char fingerprint_[TransportLayerDtls::kMaxDigestLength]; michael@0: size_t fingerprint_len_; michael@0: }; michael@0: michael@0: michael@0: class TransportTest : public ::testing::Test { michael@0: public: michael@0: TransportTest() { michael@0: fds_[0] = nullptr; michael@0: fds_[1] = nullptr; michael@0: } michael@0: michael@0: ~TransportTest() { michael@0: delete p1_; michael@0: delete p2_; michael@0: michael@0: // Can't detach these michael@0: // PR_Close(fds_[0]); michael@0: // PR_Close(fds_[1]); michael@0: } michael@0: michael@0: void DestroyPeerFlows() { michael@0: p1_->DisconnectDestroyFlow(); michael@0: p2_->DisconnectDestroyFlow(); michael@0: } michael@0: michael@0: void SetUp() { michael@0: nsresult rv; michael@0: target_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); michael@0: ASSERT_TRUE(NS_SUCCEEDED(rv)); michael@0: michael@0: p1_ = new TransportTestPeer(target_, "P1"); michael@0: p2_ = new TransportTestPeer(target_, "P2"); michael@0: } michael@0: michael@0: void SetDtlsPeer(int digests = 1, unsigned int damage = 0) { michael@0: p1_->SetDtlsPeer(p2_, digests, damage); michael@0: p2_->SetDtlsPeer(p1_, digests, damage); michael@0: } michael@0: michael@0: void SetDtlsAllowAll() { michael@0: p1_->SetDtlsAllowAll(); michael@0: p2_->SetDtlsAllowAll(); michael@0: } michael@0: michael@0: void ConnectSocket() { michael@0: test_utils->sts_target()->Dispatch( michael@0: WrapRunnable(p1_, &TransportTestPeer::ConnectSocket, p2_), michael@0: NS_DISPATCH_SYNC); michael@0: test_utils->sts_target()->Dispatch( michael@0: WrapRunnable(p2_, &TransportTestPeer::ConnectSocket, p1_), michael@0: NS_DISPATCH_SYNC); michael@0: michael@0: ASSERT_TRUE_WAIT(p1_->connected(), 10000); michael@0: ASSERT_TRUE_WAIT(p2_->connected(), 10000); michael@0: } michael@0: michael@0: void ConnectSocketExpectFail() { michael@0: test_utils->sts_target()->Dispatch( michael@0: WrapRunnable(p1_, &TransportTestPeer::ConnectSocket, p2_), michael@0: NS_DISPATCH_SYNC); michael@0: test_utils->sts_target()->Dispatch( michael@0: WrapRunnable(p2_, &TransportTestPeer::ConnectSocket, p1_), michael@0: NS_DISPATCH_SYNC); michael@0: ASSERT_TRUE_WAIT(p1_->failed(), 10000); michael@0: ASSERT_TRUE_WAIT(p2_->failed(), 10000); michael@0: } michael@0: michael@0: void InitIce() { michael@0: p1_->InitIce(); michael@0: p2_->InitIce(); michael@0: } michael@0: michael@0: void ConnectIce() { michael@0: p1_->InitIce(); michael@0: p2_->InitIce(); michael@0: p1_->ConnectIce(p2_); michael@0: p2_->ConnectIce(p1_); michael@0: ASSERT_TRUE_WAIT(p1_->connected(), 10000); michael@0: ASSERT_TRUE_WAIT(p2_->connected(), 10000); michael@0: } michael@0: michael@0: void TransferTest(size_t count) { michael@0: unsigned char buf[1000]; michael@0: michael@0: for (size_t i= 0; iSendPacket(buf, sizeof(buf)); michael@0: ASSERT_TRUE(rv > 0); michael@0: } michael@0: michael@0: std::cerr << "Received == " << p2_->received() << std::endl; michael@0: ASSERT_TRUE_WAIT(count == p2_->received(), 10000); michael@0: } michael@0: michael@0: protected: michael@0: PRFileDesc *fds_[2]; michael@0: TransportTestPeer *p1_; michael@0: TransportTestPeer *p2_; michael@0: nsCOMPtr target_; michael@0: }; michael@0: michael@0: michael@0: TEST_F(TransportTest, TestNoDtlsVerificationSettings) { michael@0: ConnectSocketExpectFail(); michael@0: } michael@0: michael@0: TEST_F(TransportTest, TestConnect) { michael@0: SetDtlsPeer(); michael@0: ConnectSocket(); michael@0: } michael@0: michael@0: TEST_F(TransportTest, TestConnectDestroyFlowsMainThread) { michael@0: SetDtlsPeer(); michael@0: ConnectSocket(); michael@0: DestroyPeerFlows(); michael@0: } michael@0: michael@0: TEST_F(TransportTest, TestConnectAllowAll) { michael@0: SetDtlsAllowAll(); michael@0: ConnectSocket(); michael@0: } michael@0: michael@0: TEST_F(TransportTest, TestConnectBadDigest) { michael@0: SetDtlsPeer(1, 1); michael@0: michael@0: ConnectSocketExpectFail(); michael@0: } michael@0: michael@0: TEST_F(TransportTest, TestConnectTwoDigests) { michael@0: SetDtlsPeer(2, 0); michael@0: michael@0: ConnectSocket(); michael@0: } michael@0: michael@0: TEST_F(TransportTest, TestConnectTwoDigestsFirstBad) { michael@0: SetDtlsPeer(2, 1); michael@0: michael@0: ConnectSocketExpectFail(); michael@0: } michael@0: michael@0: TEST_F(TransportTest, TestConnectTwoDigestsSecondBad) { michael@0: SetDtlsPeer(2, 2); michael@0: michael@0: ConnectSocketExpectFail(); michael@0: } michael@0: michael@0: TEST_F(TransportTest, TestConnectTwoDigestsBothBad) { michael@0: SetDtlsPeer(2, 3); michael@0: michael@0: ConnectSocketExpectFail(); michael@0: } michael@0: michael@0: TEST_F(TransportTest, TestTransfer) { michael@0: SetDtlsPeer(); michael@0: ConnectSocket(); michael@0: TransferTest(1); michael@0: } michael@0: michael@0: TEST_F(TransportTest, TestConnectLoseFirst) { michael@0: SetDtlsPeer(); michael@0: p1_->SetLoss(0); michael@0: ConnectSocket(); michael@0: TransferTest(1); michael@0: } michael@0: michael@0: TEST_F(TransportTest, TestConnectIce) { michael@0: SetDtlsPeer(); michael@0: ConnectIce(); michael@0: } michael@0: michael@0: TEST_F(TransportTest, TestTransferIce) { michael@0: SetDtlsPeer(); michael@0: ConnectIce(); michael@0: TransferTest(1); michael@0: } michael@0: michael@0: TEST(PushTests, LayerFail) { michael@0: TransportFlow flow; michael@0: nsresult rv; michael@0: bool destroyed1, destroyed2; michael@0: michael@0: rv = flow.PushLayer(new TransportLayerDummy(true, &destroyed1)); michael@0: ASSERT_TRUE(NS_SUCCEEDED(rv)); michael@0: michael@0: rv = flow.PushLayer(new TransportLayerDummy(false, &destroyed2)); michael@0: ASSERT_TRUE(NS_FAILED(rv)); michael@0: michael@0: ASSERT_EQ(TransportLayer::TS_ERROR, flow.state()); michael@0: ASSERT_EQ(true, destroyed1); michael@0: ASSERT_EQ(true, destroyed2); michael@0: michael@0: rv = flow.PushLayer(new TransportLayerDummy(true, &destroyed1)); michael@0: ASSERT_TRUE(NS_FAILED(rv)); michael@0: ASSERT_EQ(true, destroyed1); michael@0: } michael@0: michael@0: michael@0: TEST(PushTests, LayersFail) { michael@0: TransportFlow flow; michael@0: nsresult rv; michael@0: bool destroyed1, destroyed2, destroyed3; michael@0: michael@0: rv = flow.PushLayer(new TransportLayerDummy(true, &destroyed1)); michael@0: ASSERT_TRUE(NS_SUCCEEDED(rv)); michael@0: michael@0: nsAutoPtr > layers( michael@0: new std::queue()); michael@0: michael@0: layers->push(new TransportLayerDummy(true, &destroyed2)); michael@0: layers->push(new TransportLayerDummy(false, &destroyed3)); michael@0: michael@0: rv = flow.PushLayers(layers); michael@0: ASSERT_TRUE(NS_FAILED(rv)); michael@0: michael@0: ASSERT_EQ(TransportLayer::TS_ERROR, flow.state()); michael@0: ASSERT_EQ(true, destroyed1); michael@0: ASSERT_EQ(true, destroyed2); michael@0: ASSERT_EQ(true, destroyed3); michael@0: michael@0: layers = new std::queue(); michael@0: layers->push(new TransportLayerDummy(true, &destroyed2)); michael@0: layers->push(new TransportLayerDummy(true, &destroyed3)); michael@0: rv = flow.PushLayers(layers); michael@0: michael@0: ASSERT_TRUE(NS_FAILED(rv)); michael@0: ASSERT_EQ(true, destroyed2); michael@0: ASSERT_EQ(true, destroyed3); michael@0: } michael@0: michael@0: } // end namespace michael@0: michael@0: int main(int argc, char **argv) michael@0: { michael@0: test_utils = new MtransportTestUtils(); michael@0: michael@0: NSS_NoDB_Init(nullptr); michael@0: NSS_SetDomesticPolicy(); michael@0: // Start the tests michael@0: ::testing::InitGoogleTest(&argc, argv); michael@0: michael@0: int rv = RUN_ALL_TESTS(); michael@0: delete test_utils; michael@0: return rv; michael@0: }