Commit bf498040 authored by Camilo Viecco's avatar Camilo Viecco

Bug 1057584 - Add gtest framework and initial tests optionally (3/4). Testing Code. r=wtc

parent a8047093
GTest-based Unit Tests
This directory contains GTest-based unit tests for NSS.
Currently, these are only loopback-type tests of libsssl,
but could be expanded to other types of tests. To make these
work do:
- Set NSS_BUILD_GTESTS=1 before starting your build
- cd tests/
- Set NSS_TESTS=ssl_gtests and NSS_CYCLES=standard
- run ./all.sh
This will run the certutil tests (generating a test db) and
will finalize with a call to the ssl_gtest
You should be able to run the unit tests manually as:
ssl_gtest -d ${SSLGTESTDIR}
Where $SSLGTESTDIR the directory created by ./all.sh or a manually
created directory with a database containing a certificate called
server (with its private keys)
There is a very trivial set of tests that demonstrate some
of the features.
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef databuffer_h__
#define databuffer_h__
class DataBuffer {
public:
DataBuffer() : data_(nullptr), len_(0) {}
DataBuffer(const uint8_t *data, size_t len) : data_(nullptr), len_(0) {
Assign(data, len);
}
~DataBuffer() { delete[] data_; }
void Assign(const uint8_t *data, size_t len) {
Allocate(len);
memcpy(static_cast<void *>(data_), static_cast<const void *>(data), len);
}
void Allocate(size_t len) {
delete[] data_;
data_ = new unsigned char[len ? len : 1]; // Don't depend on new [0].
len_ = len;
}
const uint8_t *data() const { return data_; }
uint8_t *data() { return data_; }
size_t len() const { return len_; }
const bool empty() const { return len_ != 0; }
private:
uint8_t *data_;
size_t len_;
};
#endif
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef gtest_utils_h__
#define gtest_utils_h__
namespace nss_test {
// Gtest utilities
class Timeout : public PollTarget {
public:
Timeout(int32_t timer_ms) : handle_(nullptr), timed_out_(false) {
Poller::Instance()->SetTimer(timer_ms, this, &Timeout::ExpiredCallback,
&handle_);
}
static void ExpiredCallback(PollTarget* target, Event event) {
Timeout* timeout = static_cast<Timeout*>(target);
timeout->timed_out_ = true;
}
void Cancel() { handle_->Cancel(); }
bool timed_out() const { return timed_out_; }
private:
Poller::Timer* handle_;
bool timed_out_;
};
} // namespace nss_test
#define WAIT_(expression, timeout) \
do { \
Timeout tm(timeout); \
while (!(expression)) { \
Poller::Instance()->Poll(); \
if (tm.timed_out()) break; \
} \
} while (0)
#define ASSERT_TRUE_WAIT(expression, timeout) \
do { \
WAIT_(expression, timeout); \
ASSERT_TRUE(expression); \
} while (0)
#endif
#include "nspr.h"
#include "nss.h"
#include "ssl.h"
#include "test_io.h"
#define GTEST_HAS_RTTI 0
#include "gtest/gtest.h"
int main(int argc, char **argv) {
// Start the tests
::testing::InitGoogleTest(&argc, argv);
std::string path = ".";
for (int i = 0; i < argc; i++) {
if (!strcmp(argv[i], "-d")) {
path = argv[i + 1];
++i;
}
}
NSS_Initialize(path.c_str(), "", "", SECMOD_DB, NSS_INIT_READONLY);
NSS_SetDomesticPolicy();
int rv = RUN_ALL_TESTS();
NSS_Shutdown();
nss_test::Poller::Shutdown();
return rv;
}
This diff is collapsed.
This diff is collapsed.
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef test_io_h_
#define test_io_h_
#include <string.h>
#include <map>
#include <memory>
#include <queue>
#include <string>
namespace nss_test {
struct Packet;
class DummyPrSocket; // Fwd decl.
// Allow us to inspect a packet before it is written.
class Inspector {
public:
virtual ~Inspector() {}
virtual void Inspect(DummyPrSocket* adapter, const void* data,
size_t len) = 0;
};
enum Mode { STREAM, DGRAM };
class DummyPrSocket {
public:
~DummyPrSocket() { delete inspector_; }
static PRFileDesc* CreateFD(const std::string& name,
Mode mode); // Returns an FD.
static DummyPrSocket* GetAdapter(PRFileDesc* fd);
void SetPeer(DummyPrSocket* peer) { peer_ = peer; }
void SetInspector(Inspector* inspector) { inspector_ = inspector; }
void PacketReceived(const void* data, int32_t len);
int32_t Read(void* data, int32_t len);
int32_t Recv(void* buf, int32_t buflen);
int32_t Write(const void* buf, int32_t length);
int32_t WriteDirect(const void* buf, int32_t length);
Mode mode() const { return mode_; }
bool readable() { return !input_.empty(); }
bool writable() { return true; }
private:
DummyPrSocket(const std::string& name, Mode mode)
: name_(name),
mode_(mode),
peer_(nullptr),
input_(),
inspector_(nullptr) {}
const std::string name_;
Mode mode_;
DummyPrSocket* peer_;
std::queue<Packet*> input_;
Inspector* inspector_;
};
// Marker interface.
class PollTarget {};
enum Event { READABLE_EVENT, TIMER_EVENT /* Must be last */ };
typedef void (*PollCallback)(PollTarget*, Event);
class Poller {
public:
static Poller* Instance(); // Get a singleton.
static void Shutdown(); // Shut it down.
class Timer {
public:
Timer(PRTime deadline, PollTarget* target, PollCallback callback)
: deadline_(deadline), target_(target), callback_(callback) {}
void Cancel() { callback_ = nullptr; }
PRTime deadline_;
PollTarget* target_;
PollCallback callback_;
};
void Wait(Event event, DummyPrSocket* adapter, PollTarget* target,
PollCallback cb);
void SetTimer(uint32_t timer_ms, PollTarget* target, PollCallback cb,
Timer** handle);
bool Poll();
private:
Poller() : waiters_(), timers_() {}
class Waiter {
public:
Waiter(DummyPrSocket* io) : io_(io) {
memset(&callbacks_[0], 0, sizeof(callbacks_));
}
void WaitFor(Event event, PollCallback callback);
DummyPrSocket* io_;
PollTarget* targets_[TIMER_EVENT];
PollCallback callbacks_[TIMER_EVENT];
};
class TimerComparator {
public:
bool operator()(const Timer* lhs, const Timer* rhs) {
return lhs->deadline_ > rhs->deadline_;
}
};
static Poller* instance;
std::map<DummyPrSocket*, Waiter*> waiters_;
std::priority_queue<Timer*, std::vector<Timer*>, TimerComparator> timers_;
};
} // end of namespace
#endif
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "tls_parser.h"
// Process DTLS Records
#define CHECK_LENGTH(expected) \
do { \
if (remaining() < expected) return false; \
} while (0)
bool TlsParser::Read(unsigned char* val) {
if (remaining() < 1) {
return false;
}
*val = *ptr();
consume(1);
return true;
}
bool TlsParser::Read(unsigned char* val, size_t len) {
if (remaining() < len) {
return false;
}
if (val) {
memcpy(val, ptr(), len);
}
consume(len);
return true;
}
bool TlsRecordParser::NextRecord(uint8_t* ct,
std::auto_ptr<DataBuffer>* buffer) {
if (!remaining()) return false;
CHECK_LENGTH(5U);
const uint8_t* ctp = reinterpret_cast<const uint8_t*>(ptr());
consume(3); // ct + version
const uint16_t* tmp = reinterpret_cast<const uint16_t*>(ptr());
size_t length = ntohs(*tmp);
consume(2);
CHECK_LENGTH(length);
DataBuffer* db = new DataBuffer(ptr(), length);
consume(length);
*ct = *ctp;
buffer->reset(db);
return true;
}
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef tls_parser_h_
#define tls_parser_h_
#include <memory>
#include <stdint.h>
#include <string.h>
#include <arpa/inet.h>
#include "databuffer.h"
const uint8_t kTlsChangeCipherSpecType = 0x14;
const uint8_t kTlsHandshakeType = 0x16;
const uint8_t kTlsHandshakeCertificate = 0x0b;
const uint8_t kTlsHandshakeServerKeyExchange = 0x0c;
const uint8_t kTlsFakeChangeCipherSpec[] = {
kTlsChangeCipherSpecType, // Type
0xfe, 0xff, // Version
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x10, // Fictitious sequence #
0x00, 0x01, // Length
0x01 // Value
};
class TlsParser {
public:
TlsParser(const unsigned char *data, size_t len)
: buffer_(data, len), offset_(0) {}
bool Read(unsigned char *val);
// Read an integral type of specified width.
bool Read(uint32_t *val, size_t len) {
if (len > sizeof(uint32_t)) return false;
*val = 0;
for (size_t i = 0; i < len; ++i) {
unsigned char tmp;
(*val) <<= 8;
if (!Read(&tmp)) return false;
*val += tmp;
}
return true;
}
bool Read(unsigned char *val, size_t len);
size_t remaining() const { return buffer_.len() - offset_; }
private:
void consume(size_t len) { offset_ += len; }
const uint8_t *ptr() const { return buffer_.data() + offset_; }
DataBuffer buffer_;
size_t offset_;
};
class TlsRecordParser {
public:
TlsRecordParser(const unsigned char *data, size_t len)
: buffer_(data, len), offset_(0) {}
bool NextRecord(uint8_t *ct, std::auto_ptr<DataBuffer> *buffer);
private:
size_t remaining() const { return buffer_.len() - offset_; }
const uint8_t *ptr() const { return buffer_.data() + offset_; }
void consume(size_t len) { offset_ += len; }
DataBuffer buffer_;
size_t offset_;
};
#endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment