mirror of https://github.com/hmatuschek/libsdr
Added missing examples and unit tests.
parent
aeec3586bc
commit
7934a72b24
@ -0,0 +1,14 @@
|
||||
IF(SDR_WITH_QT5 AND SDR_WITH_FFTW AND SDR_WITH_PORTAUDIO)
|
||||
add_executable(sdr_spec sdr_spec.cc)
|
||||
target_link_libraries(sdr_spec ${LIBS} ${QT_LIBRARIES} libsdr libsdr-gui )
|
||||
ENDIF(SDR_WITH_QT5 AND SDR_WITH_FFTW AND SDR_WITH_PORTAUDIO)
|
||||
|
||||
IF(SDR_WITH_PORTAUDIO)
|
||||
add_executable(sdr_wavplay sdr_wavplay.cc)
|
||||
target_link_libraries(sdr_wavplay ${LIBS} libsdr)
|
||||
ENDIF(SDR_WITH_PORTAUDIO)
|
||||
|
||||
IF(SDR_WITH_QT5 AND SDR_WITH_FFTW AND SDR_WITH_PORTAUDIO)
|
||||
add_executable(sdr_rds sdr_rds.cc)
|
||||
target_link_libraries(sdr_rds ${LIBS} ${QT_LIBRARIES} libsdr libsdr-gui)
|
||||
ENDIF(SDR_WITH_QT5 AND SDR_WITH_FFTW AND SDR_WITH_PORTAUDIO)
|
||||
@ -0,0 +1,82 @@
|
||||
#include "sdr.hh"
|
||||
#include "rtlsource.hh"
|
||||
#include "baseband.hh"
|
||||
#include "autocast.hh"
|
||||
#include "gui/gui.hh"
|
||||
#include "logger.hh"
|
||||
#include <signal.h>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QMainWindow>
|
||||
#include <QThread>
|
||||
|
||||
using namespace sdr;
|
||||
|
||||
static void __sigint_handler(int signo) {
|
||||
// On SIGINT -> stop queue properly
|
||||
Queue::get().stop();
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (argc < 2) {
|
||||
std::cerr << "USAGE: sdr_rds FREQUENCY" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
double freq = atof(argv[1]);
|
||||
|
||||
PortAudio::init();
|
||||
Queue &queue = Queue::get();
|
||||
|
||||
// Register handler:
|
||||
signal(SIGINT, __sigint_handler);
|
||||
|
||||
QApplication app(argc, argv);
|
||||
|
||||
QMainWindow *win = new QMainWindow();
|
||||
gui::Spectrum *spec = new gui::Spectrum(2, 1024, 5);
|
||||
gui::SpectrumView *spec_view = new gui::SpectrumView(spec);
|
||||
spec_view->setMindB(-200);
|
||||
win->setCentralWidget(spec_view);
|
||||
win->setMinimumSize(640, 240);
|
||||
|
||||
win->show();
|
||||
|
||||
sdr::Logger::get().addHandler(new sdr::StreamLogHandler(std::cerr, sdr::LOG_DEBUG));
|
||||
|
||||
// Assemble processing chain
|
||||
//RTLSource src(freq, 1e6);
|
||||
WavSource src(argv[1]);
|
||||
AutoCast< std::complex<int16_t> > cast;
|
||||
IQBaseBand<int16_t> baseband(0, 200e3, 16, 5);
|
||||
FMDemod<int16_t, int16_t> demod;
|
||||
BaseBand<int16_t> mono(0, 15e3, 16, 6);
|
||||
BaseBand<int16_t> pilot(19e3, 5e2, 16, 84);
|
||||
BaseBand<int16_t> rds(57e3, 3e3, 16, 84);
|
||||
PortSink sink;
|
||||
|
||||
src.connect(&cast, true);
|
||||
cast.connect(&baseband, true);
|
||||
//src.connect(&baseband, true);
|
||||
baseband.connect(&demod, true);
|
||||
demod.connect(&mono);
|
||||
demod.connect(&pilot);
|
||||
demod.connect(&rds);
|
||||
|
||||
mono.connect(&sink);
|
||||
mono.connect(spec);
|
||||
|
||||
//queue.addStart(&src, &RTLSource::start);
|
||||
//queue.addStop(&src, &RTLSource::stop);
|
||||
queue.addIdle(&src, &WavSource::next);
|
||||
|
||||
queue.start();
|
||||
app.exec();
|
||||
queue.stop();
|
||||
queue.wait();
|
||||
|
||||
PortAudio::terminate();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -0,0 +1,64 @@
|
||||
#include "sdr.hh"
|
||||
#include "rtlsource.hh"
|
||||
#include "baseband.hh"
|
||||
#include "utils.hh"
|
||||
#include "gui/gui.hh"
|
||||
#include <signal.h>
|
||||
#include "portaudio.hh"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QMainWindow>
|
||||
#include <QThread>
|
||||
|
||||
using namespace sdr;
|
||||
|
||||
static void __sigint_handler(int signo) {
|
||||
// On SIGINT -> stop queue properly
|
||||
Queue::get().stop();
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
Queue &queue = Queue::get();
|
||||
|
||||
// Register handler:
|
||||
signal(SIGINT, __sigint_handler);
|
||||
|
||||
PortAudio::init();
|
||||
|
||||
QApplication app(argc, argv);
|
||||
|
||||
QMainWindow *win = new QMainWindow();
|
||||
gui::Spectrum *spec = new gui::Spectrum(2, 1024, 5);
|
||||
gui::WaterFallView *spec_view = new gui::WaterFallView(spec);
|
||||
win->setCentralWidget(spec_view);
|
||||
win->setMinimumSize(640, 240);
|
||||
|
||||
win->show();
|
||||
|
||||
// Assemble processing chain
|
||||
PortSource< int16_t > src(44100.0, 2048);
|
||||
AGC<int16_t> agc;
|
||||
//IQBaseBand<int16_t> baseband(0, 500e3, 8, 5);
|
||||
//AMDemod<int16_t> demod;
|
||||
PortSink sink;
|
||||
|
||||
src.connect(&agc, true);
|
||||
agc.connect(&sink, true);
|
||||
//baseband.connect(&demod);
|
||||
//demod.connect(&sink, true);
|
||||
src.connect(spec);
|
||||
|
||||
queue.addIdle(&src, &PortSource< int16_t >::next);
|
||||
|
||||
queue.start();
|
||||
app.exec();
|
||||
|
||||
queue.stop();
|
||||
queue.wait();
|
||||
|
||||
PortAudio::terminate();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
#include "sdr.hh"
|
||||
#include <iostream>
|
||||
|
||||
using namespace sdr;
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (argc < 2) {
|
||||
std::cerr << "USAGE: sdr_wavplay FILENAME" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
Queue &queue = Queue::get();
|
||||
|
||||
PortAudio::init();
|
||||
|
||||
WavSource src(argv[1]);
|
||||
if (! src.isOpen() ) {
|
||||
std::cerr << "Can not open file " << argv[1] << std::endl;
|
||||
return -1;
|
||||
}
|
||||
queue.addIdle(&src, &WavSource::next);
|
||||
|
||||
RealPart<int16_t> to_real;
|
||||
PortSink sink;
|
||||
|
||||
if (src.isReal()) {
|
||||
src.connect(&sink, true);
|
||||
} else {
|
||||
src.connect(&to_real, true);
|
||||
to_real.connect(&sink, true);
|
||||
}
|
||||
|
||||
// run...
|
||||
queue.start();
|
||||
queue.wait();
|
||||
|
||||
PortAudio::terminate();
|
||||
return 0;
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
set(test_SOURCES main.cc
|
||||
cputime.cc unittest.cc buffertest.cc coreutilstest.cc coretest.cc)
|
||||
set(test_HEADERS
|
||||
cputime.hh unittest.hh buffertest.hh coreutilstest.hh coretest.hh)
|
||||
|
||||
add_executable(sdr_test ${test_SOURCES})
|
||||
target_link_libraries(sdr_test ${LIBS} libsdr)
|
||||
@ -0,0 +1,136 @@
|
||||
#include "buffertest.hh"
|
||||
#include <iostream>
|
||||
using namespace sdr;
|
||||
using namespace UnitTest;
|
||||
|
||||
BufferTest::~BufferTest() { }
|
||||
|
||||
|
||||
void
|
||||
BufferTest::testRefcount() {
|
||||
Buffer<int8_t> a(3);
|
||||
|
||||
// Test Direct reference counting
|
||||
UT_ASSERT_EQUAL(a.refCount(), 1);
|
||||
UT_ASSERT(a.isUnused());
|
||||
|
||||
{
|
||||
Buffer<int8_t> b(a);
|
||||
UT_ASSERT_EQUAL(a.refCount(), 1);
|
||||
UT_ASSERT_EQUAL(b.refCount(), 1);
|
||||
UT_ASSERT(a.isUnused());
|
||||
UT_ASSERT(b.isUnused());
|
||||
}
|
||||
|
||||
{
|
||||
Buffer<int8_t> b(a);
|
||||
b.ref();
|
||||
UT_ASSERT_EQUAL(a.refCount(), 2);
|
||||
UT_ASSERT_EQUAL(b.refCount(), 2);
|
||||
UT_ASSERT(!a.isUnused());
|
||||
UT_ASSERT(!b.isUnused());
|
||||
b.unref();
|
||||
}
|
||||
|
||||
UT_ASSERT_EQUAL(a.refCount(), 1);
|
||||
UT_ASSERT(a.isUnused());
|
||||
|
||||
// Test indirect reference counting
|
||||
std::list<RawBuffer> buffers;
|
||||
buffers.push_back(a);
|
||||
|
||||
UT_ASSERT_EQUAL(a.refCount(), 1);
|
||||
UT_ASSERT(a.isUnused());
|
||||
|
||||
// check direct referenceing
|
||||
UT_ASSERT_EQUAL(buffers.back().refCount(), 1);
|
||||
UT_ASSERT(buffers.back().isUnused());
|
||||
|
||||
buffers.pop_back();
|
||||
UT_ASSERT_EQUAL(a.refCount(), 1);
|
||||
UT_ASSERT(a.isUnused());
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
BufferTest::testReinterprete() {
|
||||
// Check handle interleaved numbers as real & imag part
|
||||
Buffer<int8_t> real_buffer(4);
|
||||
|
||||
real_buffer[0] = 1;
|
||||
real_buffer[1] = 2;
|
||||
real_buffer[2] = 3;
|
||||
real_buffer[3] = 4;
|
||||
|
||||
// Cast to complex char
|
||||
Buffer< std::complex<int8_t> > cmplx_buffer(real_buffer);
|
||||
// Check size
|
||||
UT_ASSERT_EQUAL(real_buffer.size()/2, cmplx_buffer.size());
|
||||
// Check content
|
||||
UT_ASSERT_EQUAL(cmplx_buffer[0], std::complex<int8_t>(1,2));
|
||||
UT_ASSERT_EQUAL(cmplx_buffer[1], std::complex<int8_t>(3,4));
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
BufferTest::testRawRingBuffer() {
|
||||
RawBuffer a(3), b(3);
|
||||
RawRingBuffer ring(3);
|
||||
|
||||
memcpy(a.data(), "abc", 3);
|
||||
|
||||
// Check if ring is empty
|
||||
UT_ASSERT_EQUAL(ring.bytesLen(), size_t(0));
|
||||
UT_ASSERT_EQUAL(ring.bytesFree(), size_t(3));
|
||||
|
||||
// Put a byte
|
||||
UT_ASSERT(ring.put(RawBuffer(a, 0, 1)));
|
||||
UT_ASSERT_EQUAL(ring.bytesLen(), size_t(1));
|
||||
UT_ASSERT_EQUAL(ring.bytesFree(), size_t(2));
|
||||
|
||||
// Put two more bytes
|
||||
UT_ASSERT(ring.put(RawBuffer(a, 1, 2)));
|
||||
UT_ASSERT_EQUAL(ring.bytesLen(), size_t(3));
|
||||
UT_ASSERT_EQUAL(ring.bytesFree(), size_t(0));
|
||||
|
||||
// Now, the ring is full, any further put should fail
|
||||
UT_ASSERT(!ring.put(a));
|
||||
|
||||
// Take a byte from ring
|
||||
UT_ASSERT(ring.take(b, 1));
|
||||
UT_ASSERT_EQUAL(ring.bytesLen(), size_t(2));
|
||||
UT_ASSERT_EQUAL(ring.bytesFree(), size_t(1));
|
||||
UT_ASSERT_EQUAL(*(b.data()), 'a');
|
||||
|
||||
// Take another byte
|
||||
UT_ASSERT(ring.take(b, 1));
|
||||
UT_ASSERT_EQUAL(ring.bytesLen(), size_t(1));
|
||||
UT_ASSERT_EQUAL(ring.bytesFree(), size_t(2));
|
||||
UT_ASSERT_EQUAL(*(b.data()), 'b');
|
||||
|
||||
// Put two more back
|
||||
UT_ASSERT(ring.put(RawBuffer(a, 0, 2)));
|
||||
UT_ASSERT_EQUAL(ring.bytesLen(), size_t(3));
|
||||
UT_ASSERT_EQUAL(ring.bytesFree(), size_t(0));
|
||||
|
||||
// Take all
|
||||
UT_ASSERT(ring.take(b, 3));
|
||||
UT_ASSERT_EQUAL(ring.bytesLen(), size_t(0));
|
||||
UT_ASSERT_EQUAL(ring.bytesFree(), size_t(3));
|
||||
UT_ASSERT(0 == memcmp(b.data(), "cab", 3));
|
||||
|
||||
}
|
||||
|
||||
|
||||
TestSuite *
|
||||
BufferTest::suite() {
|
||||
TestSuite *suite = new TestSuite("Buffer Tests");
|
||||
|
||||
suite->addTest(new TestCaller<BufferTest>(
|
||||
"reference counter", &BufferTest::testRefcount));
|
||||
suite->addTest(new TestCaller<BufferTest>(
|
||||
"re-interprete case", &BufferTest::testReinterprete));
|
||||
suite->addTest(new TestCaller<BufferTest>(
|
||||
"raw ring buffer", &BufferTest::testRawRingBuffer));
|
||||
return suite;
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
#ifndef __SDR_TEST_BUFFERTEST_HH__
|
||||
#define __SDR_TEST_BUFFERTEST_HH__
|
||||
|
||||
#include "buffer.hh"
|
||||
#include "unittest.hh"
|
||||
|
||||
|
||||
class BufferTest : public UnitTest::TestCase
|
||||
{
|
||||
public:
|
||||
virtual ~BufferTest();
|
||||
|
||||
void testRefcount();
|
||||
void testReinterprete();
|
||||
void testRawRingBuffer();
|
||||
|
||||
|
||||
public:
|
||||
static UnitTest::TestSuite *suite();
|
||||
};
|
||||
|
||||
#endif // BUFFERTEST_HH
|
||||
@ -0,0 +1,37 @@
|
||||
#include "coretest.hh"
|
||||
#include "sdr.hh"
|
||||
|
||||
using namespace sdr;
|
||||
|
||||
|
||||
CoreTest::~CoreTest() { /* pass... */ }
|
||||
|
||||
|
||||
void
|
||||
CoreTest::testShiftOperators() {
|
||||
// Test if shift can be used as multiplication or division by a power of two
|
||||
// (even on negative integers)
|
||||
int a=128, b=-128;
|
||||
// On positive integers (should work always)
|
||||
UT_ASSERT_EQUAL(a>>1, 64);
|
||||
UT_ASSERT_EQUAL(a<<1, 256);
|
||||
UT_ASSERT_EQUAL(a>>0, 128);
|
||||
UT_ASSERT_EQUAL(a<<0, 128);
|
||||
|
||||
UT_ASSERT_EQUAL(b>>1, -64);
|
||||
UT_ASSERT_EQUAL(b<<1, -256);
|
||||
UT_ASSERT_EQUAL(b>>0, -128);
|
||||
UT_ASSERT_EQUAL(b<<0, -128);
|
||||
}
|
||||
|
||||
|
||||
|
||||
UnitTest::TestSuite *
|
||||
CoreTest::suite() {
|
||||
UnitTest::TestSuite *suite = new UnitTest::TestSuite("Core operations");
|
||||
|
||||
suite->addTest(new UnitTest::TestCaller<CoreTest>(
|
||||
"shift operators", &CoreTest::testShiftOperators));
|
||||
|
||||
return suite;
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
#ifndef __SDT_TEST_CORETEST_HH__
|
||||
#define __SDT_TEST_CORETEST_HH__
|
||||
|
||||
#include "unittest.hh"
|
||||
|
||||
class CoreTest : public UnitTest::TestCase
|
||||
{
|
||||
public:
|
||||
virtual ~CoreTest();
|
||||
|
||||
void testShiftOperators();
|
||||
|
||||
|
||||
public:
|
||||
static UnitTest::TestSuite *suite();
|
||||
};
|
||||
|
||||
#endif // __SDT_TEST_CORETEST_HH__
|
||||
@ -0,0 +1,90 @@
|
||||
#include "coreutilstest.hh"
|
||||
#include "config.hh"
|
||||
#include "utils.hh"
|
||||
#include "combine.hh"
|
||||
|
||||
using namespace sdr;
|
||||
using namespace UnitTest;
|
||||
|
||||
CoreUtilsTest::~CoreUtilsTest() { }
|
||||
|
||||
void
|
||||
CoreUtilsTest::testUChar2Char() {
|
||||
Buffer<uint8_t> uchar_buffer(3);
|
||||
uchar_buffer[0] = 0u;
|
||||
uchar_buffer[1] = 128u;
|
||||
uchar_buffer[2] = 255u;
|
||||
|
||||
// Assemble cast instance and configure it
|
||||
UnsignedToSigned cast;
|
||||
cast.config(Config(Config::Type_u8, 1, 3, 1));
|
||||
// Perform in-place operation
|
||||
cast.handleBuffer(uchar_buffer, true);
|
||||
|
||||
// Reinterprete uchar buffer as char buffer
|
||||
Buffer<int8_t> char_buffer(uchar_buffer);
|
||||
// Check values
|
||||
UT_ASSERT_EQUAL(char_buffer[0], (signed char)-128);
|
||||
UT_ASSERT_EQUAL(char_buffer[1], (signed char)0);
|
||||
UT_ASSERT_EQUAL(char_buffer[2], (signed char)127);
|
||||
}
|
||||
|
||||
void
|
||||
CoreUtilsTest::testUShort2Short() {
|
||||
Buffer<uint16_t> uchar_buffer(3);
|
||||
uchar_buffer[0] = 0u;
|
||||
uchar_buffer[1] = 128u;
|
||||
uchar_buffer[2] = 255u;
|
||||
|
||||
// Assemble cast instance and configure it
|
||||
UnsignedToSigned cast;
|
||||
cast.config(Config(Config::Type_u16, 1, 3, 1));
|
||||
// Perform in-place operation
|
||||
cast.handleBuffer(uchar_buffer, true);
|
||||
|
||||
// Reinterprete uchar buffer as char buffer
|
||||
Buffer<int16_t> char_buffer(uchar_buffer);
|
||||
// Check values
|
||||
UT_ASSERT_EQUAL(char_buffer[0], (int16_t)-128);
|
||||
UT_ASSERT_EQUAL(char_buffer[1], (int16_t)0);
|
||||
UT_ASSERT_EQUAL(char_buffer[2], (int16_t)127);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CoreUtilsTest::testInterleave() {
|
||||
Interleave<int16_t> interl(2);
|
||||
DebugStore<int16_t> sink;
|
||||
Buffer<int16_t> a(3);
|
||||
|
||||
interl.sink(0)->config(Config(Config::Type_s16, 1, a.size(), 1));
|
||||
interl.sink(1)->config(Config(Config::Type_s16, 1, a.size(), 1));
|
||||
interl.connect(&sink, true);
|
||||
|
||||
// Send some data
|
||||
a[0] = 1; a[1] = 2; a[2] = 3; interl.sink(0)->process(a, false);
|
||||
a[0] = 4; a[1] = 5; a[2] = 6; interl.sink(1)->process(a, false);
|
||||
|
||||
// Check content of sink
|
||||
UT_ASSERT_EQUAL(sink.buffer()[0], (int16_t)1);
|
||||
UT_ASSERT_EQUAL(sink.buffer()[1], (int16_t)4);
|
||||
UT_ASSERT_EQUAL(sink.buffer()[2], (int16_t)2);
|
||||
UT_ASSERT_EQUAL(sink.buffer()[3], (int16_t)5);
|
||||
UT_ASSERT_EQUAL(sink.buffer()[4], (int16_t)3);
|
||||
UT_ASSERT_EQUAL(sink.buffer()[5], (int16_t)6);
|
||||
}
|
||||
|
||||
|
||||
TestSuite *
|
||||
CoreUtilsTest::suite() {
|
||||
TestSuite *suite = new TestSuite("Core Utils");
|
||||
|
||||
suite->addTest(new TestCaller<CoreUtilsTest>(
|
||||
"cast uint8_t -> int8_t", &CoreUtilsTest::testUChar2Char));
|
||||
suite->addTest(new TestCaller<CoreUtilsTest>(
|
||||
"cast uint16_t -> int16_t", &CoreUtilsTest::testUChar2Char));
|
||||
suite->addTest(new TestCaller<CoreUtilsTest>(
|
||||
"Interleave", &CoreUtilsTest::testInterleave));
|
||||
|
||||
return suite;
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
#ifndef __SDR_TEST_COREUTILSTEST_HH__
|
||||
#define __SDR_TEST_COREUTILSTEST_HH__
|
||||
|
||||
#include "unittest.hh"
|
||||
|
||||
class CoreUtilsTest : public UnitTest::TestCase
|
||||
{
|
||||
public:
|
||||
virtual ~CoreUtilsTest();
|
||||
|
||||
void testUChar2Char();
|
||||
void testUShort2Short();
|
||||
void testInterleave();
|
||||
|
||||
public:
|
||||
static UnitTest::TestSuite *suite();
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,46 @@
|
||||
#include "cputime.hh"
|
||||
|
||||
using namespace UnitTest;
|
||||
|
||||
|
||||
CpuTime::CpuTime()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CpuTime::start()
|
||||
{
|
||||
this->_clocks.push_back(clock());
|
||||
}
|
||||
|
||||
|
||||
double
|
||||
CpuTime::stop()
|
||||
{
|
||||
// measure time.
|
||||
clock_t end = clock();
|
||||
|
||||
// Get time-diff since start:
|
||||
double dt = end-this->_clocks.back();
|
||||
dt /= CLOCKS_PER_SEC;
|
||||
|
||||
// Remove start time from stack:
|
||||
this->_clocks.pop_back();
|
||||
|
||||
// Return delta t:
|
||||
return dt;
|
||||
}
|
||||
|
||||
|
||||
double
|
||||
CpuTime::getTime()
|
||||
{
|
||||
clock_t end = clock();
|
||||
|
||||
// get diff:
|
||||
double dt = end - this->_clocks.back();
|
||||
dt /= CLOCKS_PER_SEC;
|
||||
|
||||
return dt;
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
#ifndef __SDR_CPUTIME_HH__
|
||||
#define __SDR_CPUTIME_HH__
|
||||
|
||||
#include <time.h>
|
||||
#include <list>
|
||||
|
||||
|
||||
namespace UnitTest {
|
||||
|
||||
/** A utility class to measure the CPU time used by some algorithms. */
|
||||
class CpuTime
|
||||
{
|
||||
public:
|
||||
/** Constructs a new CPU time clock. */
|
||||
CpuTime();
|
||||
|
||||
/** Start the clock. */
|
||||
void start();
|
||||
/** Stops the clock and returns the time in seconds. */
|
||||
double stop();
|
||||
/** Retruns the current time of the current clock. */
|
||||
double getTime();
|
||||
|
||||
protected:
|
||||
/** The stack of start times. */
|
||||
std::list< clock_t > _clocks;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // CPUTIME_HH
|
||||
@ -0,0 +1,21 @@
|
||||
#include "coretest.hh"
|
||||
#include "coreutilstest.hh"
|
||||
#include "unittest.hh"
|
||||
#include "buffertest.hh"
|
||||
#include <iostream>
|
||||
|
||||
using namespace sdr;
|
||||
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
UnitTest::TestRunner runner(std::cout);
|
||||
|
||||
runner.addSuite(CoreTest::suite());
|
||||
runner.addSuite(BufferTest::suite());
|
||||
runner.addSuite(CoreUtilsTest::suite());
|
||||
|
||||
runner();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -0,0 +1,192 @@
|
||||
#include "unittest.hh"
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <limits>
|
||||
#include <cmath>
|
||||
#include "cputime.hh"
|
||||
|
||||
using namespace UnitTest;
|
||||
|
||||
|
||||
|
||||
/* ********************************************************************************************* *
|
||||
* Implementation of TestFailure
|
||||
* ********************************************************************************************* */
|
||||
TestFailure::TestFailure(const std::string &message) throw()
|
||||
: message(message)
|
||||
{
|
||||
// pass...
|
||||
}
|
||||
|
||||
TestFailure::~TestFailure() throw()
|
||||
{
|
||||
// Pass...
|
||||
}
|
||||
|
||||
const char *
|
||||
TestFailure::what() const throw ()
|
||||
{
|
||||
return this->message.c_str();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* ********************************************************************************************* *
|
||||
* Implementation of TestCase
|
||||
* ********************************************************************************************* */
|
||||
void
|
||||
TestCase::setUp()
|
||||
{
|
||||
// Pass...
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TestCase::tearDown()
|
||||
{
|
||||
// Pass...
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TestCase::assertTrue(bool test, const std::string &file, size_t line)
|
||||
{
|
||||
if (! test)
|
||||
{
|
||||
std::stringstream str;
|
||||
str << "Assert failed in " << file << " in line " << line;
|
||||
throw TestFailure(str.str());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ********************************************************************************************* *
|
||||
* Implementation of TestSuite
|
||||
* ********************************************************************************************* */
|
||||
TestSuite::TestSuite(const std::string &desc)
|
||||
: description(desc)
|
||||
{
|
||||
// pass...
|
||||
}
|
||||
|
||||
|
||||
TestSuite::~TestSuite()
|
||||
{
|
||||
// Free callers:
|
||||
for (std::list<TestCallerInterface *>::iterator caller=this->tests.begin();
|
||||
caller != this->tests.end(); caller++) {
|
||||
delete *caller;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TestSuite::addTest(TestCallerInterface *test)
|
||||
{
|
||||
this->tests.push_back(test);
|
||||
}
|
||||
|
||||
|
||||
const std::string &
|
||||
TestSuite::getDescription()
|
||||
{
|
||||
return this->description;
|
||||
}
|
||||
|
||||
|
||||
TestSuite::iterator
|
||||
TestSuite::begin()
|
||||
{
|
||||
return this->tests.begin();
|
||||
}
|
||||
|
||||
|
||||
TestSuite::iterator
|
||||
TestSuite::end()
|
||||
{
|
||||
return this->tests.end();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* ********************************************************************************************* *
|
||||
* Implementation of TestSuite
|
||||
* ********************************************************************************************* */
|
||||
TestRunner::TestRunner(std::ostream &stream)
|
||||
: stream(stream)
|
||||
{
|
||||
// Pass...
|
||||
}
|
||||
|
||||
|
||||
TestRunner::~TestRunner()
|
||||
{
|
||||
// Free suites:
|
||||
for (std::list<TestSuite *>::iterator suite = this->suites.begin();
|
||||
suite != this->suites.end(); suite++)
|
||||
{
|
||||
delete *suite;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TestRunner::addSuite(TestSuite *suite)
|
||||
{
|
||||
this->suites.push_back(suite);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TestRunner::operator ()()
|
||||
{
|
||||
size_t tests_run = 0;
|
||||
size_t tests_failed = 0;
|
||||
size_t tests_error = 0;
|
||||
|
||||
for (std::list<TestSuite *>::iterator suite = this->suites.begin();
|
||||
suite != this->suites.end(); suite++)
|
||||
{
|
||||
// Dump Suite description
|
||||
this->stream << "Suite: " << (*suite)->getDescription() << std::endl;
|
||||
|
||||
// For each test in suite:
|
||||
for (TestSuite::iterator test = (*suite)->begin(); test != (*suite)->end(); test++)
|
||||
{
|
||||
this->stream << " test: " << (*test)->getDescription() << ": ";
|
||||
|
||||
try
|
||||
{
|
||||
tests_run++;
|
||||
CpuTime clock; clock.start();
|
||||
// Run test
|
||||
(**test)();
|
||||
this->stream << " ok (" << clock.stop() << "s)" << std::endl;
|
||||
}
|
||||
catch (TestFailure &fail)
|
||||
{
|
||||
this->stream << " fail" << std::endl;
|
||||
this->stream << " reason: " << fail.what() << std::endl;
|
||||
tests_failed++;
|
||||
}
|
||||
catch (std::exception &err)
|
||||
{
|
||||
this->stream << " exception" << std::endl;
|
||||
this->stream << " what(): " << err.what() << std::endl;
|
||||
tests_error++;
|
||||
}
|
||||
}
|
||||
|
||||
this->stream << std::endl;
|
||||
}
|
||||
|
||||
this->stream << "Summary: " << tests_failed << " tests failed out of "
|
||||
<< tests_run - tests_error
|
||||
<< " (" << 100. * float((tests_run-tests_failed-tests_error))/(tests_run-tests_error)
|
||||
<< "% passed)." << std::endl << " Where "
|
||||
<< tests_error << " tests produced errors." << std::endl;
|
||||
}
|
||||
|
||||
@ -0,0 +1,159 @@
|
||||
#ifndef __SDR_UNITTEST_HH__
|
||||
#define __SDR_UNITTEST_HH__
|
||||
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <cmath>
|
||||
|
||||
|
||||
namespace UnitTest {
|
||||
|
||||
class TestFailure : public std::exception
|
||||
{
|
||||
protected:
|
||||
std::string message;
|
||||
|
||||
public:
|
||||
TestFailure(const std::string &message) throw();
|
||||
virtual ~TestFailure() throw();
|
||||
|
||||
const char *what() const throw();
|
||||
};
|
||||
|
||||
|
||||
|
||||
class TestCase
|
||||
{
|
||||
public:
|
||||
virtual void setUp();
|
||||
virtual void tearDown();
|
||||
|
||||
void assertTrue(bool test, const std::string &file, size_t line);
|
||||
|
||||
template <class Scalar>
|
||||
void assertEqual(Scalar t, Scalar e, const std::string &file, size_t line) {
|
||||
if (e != t) {
|
||||
std::stringstream str;
|
||||
str << "Expected: " << +e << " but got: " << +t
|
||||
<< " in file "<< file << " in line " << line;
|
||||
throw TestFailure(str.str());
|
||||
}
|
||||
}
|
||||
|
||||
template <class Scalar>
|
||||
void assertNear(Scalar t, Scalar e, const std::string &file, size_t line,
|
||||
Scalar err_abs=Scalar(1e-8), Scalar err_rel=Scalar(1e-6))
|
||||
{
|
||||
if (std::abs(e-t) > (err_abs + err_rel*std::abs(e))) {
|
||||
std::stringstream str;
|
||||
str << "Expected: " << +e << " but got: " << +t
|
||||
<< " in file "<< file << " in line " << line;
|
||||
throw TestFailure(str.str());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
class TestCallerInterface
|
||||
{
|
||||
protected:
|
||||
std::string description;
|
||||
|
||||
public:
|
||||
TestCallerInterface(const std::string &desc)
|
||||
: description(desc)
|
||||
{
|
||||
// Pass...
|
||||
}
|
||||
|
||||
virtual ~TestCallerInterface() { /* pass... */ }
|
||||
|
||||
virtual const std::string &getDescription()
|
||||
{
|
||||
return this->description;
|
||||
}
|
||||
|
||||
virtual void operator() () = 0;
|
||||
};
|
||||
|
||||
|
||||
template <class T>
|
||||
class TestCaller : public TestCallerInterface
|
||||
{
|
||||
protected:
|
||||
void (T::*function)(void);
|
||||
|
||||
public:
|
||||
TestCaller(const std::string &desc, void (T::*func)(void))
|
||||
: TestCallerInterface(desc), function(func)
|
||||
{
|
||||
// Pass...
|
||||
}
|
||||
|
||||
virtual ~TestCaller() { /* pass... */ }
|
||||
|
||||
virtual void operator() ()
|
||||
{
|
||||
// Create new test:
|
||||
T *instance = new T();
|
||||
|
||||
// Call test
|
||||
instance->setUp();
|
||||
(instance->*function)();
|
||||
instance->tearDown();
|
||||
|
||||
// free instance:
|
||||
delete instance;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class TestSuite
|
||||
{
|
||||
public:
|
||||
typedef std::list<TestCallerInterface *>::iterator iterator;
|
||||
|
||||
protected:
|
||||
std::string description;
|
||||
std::list<TestCallerInterface *> tests;
|
||||
|
||||
public:
|
||||
TestSuite(const std::string &desc);
|
||||
virtual ~TestSuite();
|
||||
|
||||
void addTest(TestCallerInterface *test);
|
||||
|
||||
const std::string &getDescription();
|
||||
|
||||
iterator begin();
|
||||
iterator end();
|
||||
};
|
||||
|
||||
|
||||
class TestRunner
|
||||
{
|
||||
protected:
|
||||
std::ostream &stream;
|
||||
std::list<TestSuite *> suites;
|
||||
|
||||
public:
|
||||
TestRunner(std::ostream &stream);
|
||||
virtual ~TestRunner();
|
||||
|
||||
void addSuite(TestSuite *suite);
|
||||
|
||||
void operator() ();
|
||||
};
|
||||
|
||||
|
||||
#define UT_ASSERT(t) this->assertTrue(t, __FILE__, __LINE__)
|
||||
#define UT_ASSERT_EQUAL(t, e) this->assertEqual(t, e, __FILE__, __LINE__)
|
||||
#define UT_ASSERT_NEAR(t, e) this->assertNear(t, e, __FILE__, __LINE__)
|
||||
#define UT_ASSERT_THROW(t, e) \
|
||||
try { t; throw UnitTest::TestFailure("No exception thrown!"); } catch (e &err) {}
|
||||
}
|
||||
|
||||
|
||||
#endif // UNITTEST_HH
|
||||
Loading…
Reference in New Issue