From 120023d8da0aabcb803a87111608ce84c94661f8 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Mon, 25 Jul 2011 17:46:24 +0300 Subject: [PATCH] Add unit tests --- tests/Build | 15 ++++ tests/codec.cpp | 41 +++++++++++ tests/console/console.cpp | 26 +++++++ tests/datetime.cpp | 118 +++++++++++++++++++++++++++++++ tests/file.cpp | 87 +++++++++++++++++++++++ tests/getopt.cpp | 141 ++++++++++++++++++++++++++++++++++++++ tests/maputils.cpp | 40 +++++++++++ tests/memory.cpp | 73 ++++++++++++++++++++ tests/pipe.cpp | 81 ++++++++++++++++++++++ tests/thread.cpp | 129 ++++++++++++++++++++++++++++++++++ tests/timezone.cpp | 48 +++++++++++++ tests/variant.cpp | 113 ++++++++++++++++++++++++++++++ 12 files changed, 912 insertions(+) create mode 100644 tests/Build create mode 100644 tests/codec.cpp create mode 100644 tests/console/console.cpp create mode 100644 tests/datetime.cpp create mode 100644 tests/file.cpp create mode 100644 tests/getopt.cpp create mode 100644 tests/maputils.cpp create mode 100644 tests/memory.cpp create mode 100644 tests/pipe.cpp create mode 100644 tests/thread.cpp create mode 100644 tests/timezone.cpp create mode 100644 tests/variant.cpp diff --git a/tests/Build b/tests/Build new file mode 100644 index 0000000..982059e --- /dev/null +++ b/tests/Build @@ -0,0 +1,15 @@ +package "mspcore-tests" +{ + require "mspcore"; + require "msptest"; + + program "test" + { + source "."; + }; + + program "consoletest" + { + source "console/console.cpp"; + }; +}; diff --git a/tests/codec.cpp b/tests/codec.cpp new file mode 100644 index 0000000..896ce5d --- /dev/null +++ b/tests/codec.cpp @@ -0,0 +1,41 @@ +#include +#include +#include + +using namespace std; +using namespace Msp; + +class CodecTests: public Test::RegisteredTest +{ +public: + CodecTests(); + + static const char *get_name() { return "Codec"; } + +private: + void invalid_character(); + void invalid_sequence(); +}; + + +CodecTests::CodecTests() +{ + add(&CodecTests::invalid_character, "invalid_character").expect_throw(); + add(&CodecTests::invalid_sequence, "invalid_sequence").expect_throw(); +} + +void CodecTests::invalid_character() +{ + StringCodec::unichar ch = 0xE4; + StringCodec::Ascii::Encoder enc; + string buf; + enc.encode_char(ch, buf); +} + +void CodecTests::invalid_sequence() +{ + string str = "\343\201"; + string::const_iterator iter = str.begin(); + StringCodec::Utf8::Decoder dec; + dec.decode_char(str, iter); +} diff --git a/tests/console/console.cpp b/tests/console/console.cpp new file mode 100644 index 0000000..04ad4ac --- /dev/null +++ b/tests/console/console.cpp @@ -0,0 +1,26 @@ +#include +#include +#include + +using namespace Msp; + +int main() +{ + IO::cin.set_local_echo(false); + IO::cin.set_line_buffer(false); + while(1) + { + if(IO::poll(IO::cin, IO::P_INPUT)==IO::P_INPUT) + { + char c = IO::cin.get(); + IO::print("Got character '%c' (%02X)\n", c, c); + } + else + { + IO::print("EOF\n"); + break; + } + } + + return 0; +} diff --git a/tests/datetime.cpp b/tests/datetime.cpp new file mode 100644 index 0000000..45a8699 --- /dev/null +++ b/tests/datetime.cpp @@ -0,0 +1,118 @@ +#include +#include +#include +#include +#include + +using namespace std; +using namespace Msp; + +class DateTimeTests: public Test::RegisteredTest +{ +public: + DateTimeTests(); + + static const char *get_name() { return "DateTime"; } + +private: + void add_days(); + void add_timedelta(); + void leap_year(); + void format(); + void timestamp(); + void timezone(); +}; + + +DateTimeTests::DateTimeTests() +{ + add(&DateTimeTests::add_days, "add_days"); + add(&DateTimeTests::add_timedelta, "+= TimeDelta"); + add(&DateTimeTests::leap_year, "Leap years"); + add(&DateTimeTests::format, "Formatting"); + add(&DateTimeTests::timestamp, "Timestamp conversion"); + add(&DateTimeTests::timezone, "Timezone conversion"); +} + +void DateTimeTests::add_days() +{ + Time::DateTime dt(2010, 1, 1); + dt.add_days(1); + EXPECT_EQUAL(dt, Time::DateTime(2010, 1, 2)); + dt.add_days(57); + EXPECT_EQUAL(dt, Time::DateTime(2010, 2, 28)); + dt.add_days(307); + EXPECT_EQUAL(dt, Time::DateTime(2011, 1, 1)); + dt.add_days(-366); + EXPECT_EQUAL(dt, Time::DateTime(2009, 12, 31)); +} + +void DateTimeTests::add_timedelta() +{ + Time::DateTime dt(2010, 1, 1, 23, 50, 0); + dt += 20*Time::min; + EXPECT_EQUAL(dt, Time::DateTime(2010, 1, 2, 0, 10, 0)); + dt += 23*Time::hour+50*Time::min; + EXPECT_EQUAL(dt, Time::DateTime(2010, 1, 3)); + dt += 52*Time::week; + EXPECT_EQUAL(dt, Time::DateTime(2011, 1, 2)); +} + +void DateTimeTests::leap_year() +{ + Time::DateTime dt(1996, 2, 1); + dt.add_days(29); + EXPECT_EQUAL(dt, Time::DateTime(1996, 3, 1)); + + dt = Time::DateTime(2000, 2, 1); + dt.add_days(29); + EXPECT_EQUAL(dt, Time::DateTime(2000, 3, 1)); + + dt = Time::DateTime(2100, 2, 1); + dt.add_days(29); + EXPECT_EQUAL(dt, Time::DateTime(2100, 3, 2)); + + dt = Time::DateTime(1996, 2, 1); + dt.add_days(8*365); + EXPECT_EQUAL(dt, Time::DateTime(2004, 1, 30)); + + dt = Time::DateTime(2096, 2, 1); + dt.add_days(8*365); + EXPECT_EQUAL(dt, Time::DateTime(2104, 1, 31)); + + dt = Time::DateTime(2010, 2, 1); + dt.add_days(400*365+96); + EXPECT_EQUAL(dt, Time::DateTime(2410, 1, 31)); +} + +void DateTimeTests::format() +{ + Time::DateTime dt(2010, 4, 27, 13, 57, 12); + EXPECT_EQUAL(dt.format("%Y-%m-%d"), "2010-04-27"); + EXPECT_EQUAL(dt.format("%d%m%y"), "270410"); + EXPECT_EQUAL(dt.format("%H:%M:%S"), "13:57:12"); + EXPECT_EQUAL(dt.format("%I:%M %p"), "01:57 PM"); + EXPECT_EQUAL(Time::DateTime(2010, 4, 27, 12, 0, 0).format("%p"), "PM"); + EXPECT_EQUAL(dt.format_rfc3339(), "2010-04-27T13:57:12Z"); + dt.set_timezone(Time::TimeZone(120)); + EXPECT_EQUAL(dt.format_rfc3339(), "2010-04-27T13:57:12+02:00"); +} + +void DateTimeTests::timestamp() +{ + Time::DateTime dt(2010, 1, 1); + Time::TimeStamp ts = dt.get_timestamp(); + Time::DateTime dt2(ts); + EXPECT_EQUAL(dt, dt2); +} + +void DateTimeTests::timezone() +{ + Time::DateTime dt(2010, 1, 1, 2, 0, 0); + dt.convert_timezone(Time::TimeZone(120)); + EXPECT_EQUAL(dt, Time::DateTime(2010, 1, 1, 4, 0, 0)); + dt.convert_timezone(Time::TimeZone(-300)); + EXPECT_EQUAL(dt, Time::DateTime(2009, 12, 31, 21, 0, 0)); + dt.convert_timezone(Time::TimeZone::utc()); + EXPECT_EQUAL(dt, Time::DateTime(2010, 1, 1, 2, 0, 0)); +} diff --git a/tests/file.cpp b/tests/file.cpp new file mode 100644 index 0000000..ccd2eb5 --- /dev/null +++ b/tests/file.cpp @@ -0,0 +1,87 @@ +#include +#include +#include + +using namespace std; +using namespace Msp; + +class FileTests: public Test::RegisteredTest +{ +public: + FileTests(); + + static const char *get_name() { return "File"; } + +private: + void write(); + void read(); + void seek(); + void file_not_found(); + void invalid_access(); +}; + + +FileTests::FileTests() +{ + add(&FileTests::write, "Writing"); + add(&FileTests::read, "Reading"); + add(&FileTests::seek, "Seeking"); + add(&FileTests::file_not_found, "file_not_found").expect_throw(); + add(&FileTests::invalid_access, "invalid_access").expect_throw(); +} + +void FileTests::write() +{ + IO::File out("test.txt", IO::M_WRITE); + out.write("foobar\n"); + out.write("quux\n"); +} + +void FileTests::read() +{ + IO::File in("test.txt"); + + string line; + EXPECT(in.getline(line)); + EXPECT_EQUAL(line, "foobar"); + + char buf[128]; + unsigned len = in.read(buf, sizeof(buf)); + EXPECT_EQUAL(len, 5U); + EXPECT_EQUAL(string(buf, len), "quux\n"); + + len = in.read(buf, sizeof(buf)); + EXPECT_EQUAL(len, 0U); + EXPECT(in.eof()); +} + +void FileTests::seek() +{ + IO::File in("test.txt"); + + IO::SeekOffset pos = in.seek(3, IO::S_BEG); + EXPECT_EQUAL(pos, 3); + char c = in.get(); + EXPECT_EQUAL(c, 'b'); + + pos = in.seek(1, IO::S_CUR); + EXPECT_EQUAL(pos, 5); + c = in.get(); + EXPECT_EQUAL(c, 'r'); + + pos = in.seek(-2, IO::S_END); + EXPECT_EQUAL(pos, 10); + c = in.get(); + EXPECT_EQUAL(c, 'x'); +} + +void FileTests::file_not_found() +{ + IO::File in("does.not.exist"); +} + +void FileTests::invalid_access() +{ + IO::File in("test.txt"); + in.write("foo\n"); +} diff --git a/tests/getopt.cpp b/tests/getopt.cpp new file mode 100644 index 0000000..cb5df62 --- /dev/null +++ b/tests/getopt.cpp @@ -0,0 +1,141 @@ +#include +#include + +using namespace std; +using namespace Msp; + +class GetOptTests: public Test::RegisteredTest +{ +public: + GetOptTests(); + + static const char *get_name() { return "GetOpt"; } + +private: + void short_options(); + void long_options(); + void arguments(); + void non_options(); + void help(); + void invalid_option(); + void invalid_arg(); + void missing_arg(); +}; + +GetOptTests::GetOptTests() +{ + add(&GetOptTests::short_options, "Short options"); + add(&GetOptTests::long_options, "Long options"); + add(&GetOptTests::arguments, "Option arguments"); + add(&GetOptTests::non_options, "Non-options"); + add(&GetOptTests::help, "Help").expect_throw(); + add(&GetOptTests::invalid_option, "Invalid option").expect_throw(); + add(&GetOptTests::invalid_arg, "Invalid argument").expect_throw(); + add(&GetOptTests::missing_arg, "Missing argument").expect_throw(); +} + +void GetOptTests::short_options() +{ + static const char *argv[] = { "test", "-a", "-bc", 0 }; + + bool a = false; + bool b = false; + bool c = false; + + GetOpt getopt; + getopt.add_option('a', "a", a, GetOpt::NO_ARG); + getopt.add_option('b', "b", b, GetOpt::NO_ARG); + getopt.add_option('c', "c", c, GetOpt::NO_ARG); + getopt(3, argv); + + EXPECT(a && b && c); +} + +void GetOptTests::long_options() +{ + static const char *argv[] = { "test", "--foo", "--bar", 0 }; + + bool foo = false; + bool bar = false; + + GetOpt getopt; + getopt.add_option("foo", foo, GetOpt::NO_ARG); + getopt.add_option("bar", bar, GetOpt::NO_ARG); + getopt(3, argv); + + EXPECT(foo && bar); +} + +void GetOptTests::arguments() +{ + static const char *argv[] = { "test", "-aabc", "-b", "x y z", "--foo=42", "--bar", "69", 0 }; + + string a; + string b; + int foo = 0; + int bar = 0; + + GetOpt getopt; + getopt.add_option('a', "a", a, GetOpt::REQUIRED_ARG); + getopt.add_option('b', "b", b, GetOpt::REQUIRED_ARG); + getopt.add_option("foo", foo, GetOpt::REQUIRED_ARG); + getopt.add_option("bar", bar, GetOpt::REQUIRED_ARG); + getopt(7, argv); + + EXPECT_EQUAL(a, "abc"); + EXPECT_EQUAL(b, "x y z"); + EXPECT_EQUAL(foo, 42); + EXPECT_EQUAL(bar, 69); +} + +void GetOptTests::non_options() +{ + static const char *argv[] = { "test", "-a", "foo", "-b", "bar", "baz", 0 }; + + bool a = false; + bool b = false; + + GetOpt getopt; + getopt.add_option('a', "a", a, GetOpt::NO_ARG); + getopt.add_option('b', "b", b, GetOpt::NO_ARG); + getopt(6, argv); + + EXPECT(a && b); + EXPECT_EQUAL(getopt.get_args().size(), 3U); +} + +void GetOptTests::help() +{ + static const char *argv[] = { "test", "--help", 0 }; + + GetOpt getopt; + getopt(2, argv); +} + +void GetOptTests::invalid_option() +{ + static const char *argv[] = { "test", "--invalid", 0 }; + + GetOpt getopt; + getopt(2, argv); +} + +void GetOptTests::invalid_arg() +{ + static const char *argv[] = { "test", "--intval=foo", 0 }; + + int value; + GetOpt getopt; + getopt.add_option("intval", value, GetOpt::REQUIRED_ARG); + getopt(2, argv); +} + +void GetOptTests::missing_arg() +{ + static const char *argv[] = { "test", "--value", 0 }; + + string value; + GetOpt getopt; + getopt.add_option("value", value, GetOpt::REQUIRED_ARG); + getopt(2, argv); +} diff --git a/tests/maputils.cpp b/tests/maputils.cpp new file mode 100644 index 0000000..78cdaad --- /dev/null +++ b/tests/maputils.cpp @@ -0,0 +1,40 @@ +#include +#include +#include + +using namespace std; +using namespace Msp; + +class MapUtilTests: public Test::RegisteredTest +{ +public: + MapUtilTests(); + + static const char *get_name() { return "maputils"; } + +private: + void success(); + void error(); +}; + + +MapUtilTests::MapUtilTests() +{ + add(&MapUtilTests::success, "success"); + add(&MapUtilTests::error, "error").expect_throw(); +} + +void MapUtilTests::success() +{ + map m; + m[1] = "foo"; + m[3] = "bar"; + EXPECT_EQUAL(get_item(m, 1), "foo"); + EXPECT_EQUAL(get_item(m, 3), "bar"); +} + +void MapUtilTests::error() +{ + map m; + get_item(m, 0); +} diff --git a/tests/memory.cpp b/tests/memory.cpp new file mode 100644 index 0000000..5c3fa69 --- /dev/null +++ b/tests/memory.cpp @@ -0,0 +1,73 @@ +#include +#include + +using namespace std; +using namespace Msp; + +class MemoryTests: public Test::RegisteredTest +{ +public: + MemoryTests(); + + static const char *get_name() { return "Memory"; } + +private: + void write(); + void read(); + void getline(); + void invalid_access(); +}; + + +MemoryTests::MemoryTests() +{ + add(&MemoryTests::write, "write"); + add(&MemoryTests::read, "read"); + add(&MemoryTests::getline, "getline"); + add(&MemoryTests::invalid_access, "invalid_access").expect_throw(); +} + +void MemoryTests::write() +{ + char buf[64] = { }; + IO::Memory mem(buf, sizeof(buf)); + mem.write("foobar"); + EXPECT(equal(buf, buf+6, "foobar")); + for(unsigned i=6; i +#include +#include +#include +#include + +using namespace Msp; + +class PipeTests: public Test::RegisteredTest +{ +public: + PipeTests(); + + static const char *get_name() { return "Pipe"; } + +private: + void readwrite(); + void poll(); +}; + + +class PipeWriter: public Thread +{ +private: + IO::Pipe &pipe; + +public: + PipeWriter(IO::Pipe &); + +private: + virtual void main(); +}; + + +PipeTests::PipeTests() +{ + add(&PipeTests::readwrite, "Read/write"); + add(&PipeTests::poll, "Poll"); +} + +void PipeTests::readwrite() +{ + IO::Pipe pipe; + PipeWriter writer(pipe); + + for(unsigned i=0; i<256; ++i) + { + unsigned char c = pipe.get(); + if(c!=i) + fail("Invalid data"); + } + + writer.join(); +} + +void PipeTests::poll() +{ + IO::Pipe pipe; + + pipe.put(1); + IO::PollEvent ev = IO::poll(pipe, IO::P_INPUT); + EXPECT_EQUAL(ev, IO::P_INPUT); + + pipe.get(); + + ev = IO::poll(pipe, IO::P_INPUT, 100*Time::msec); + EXPECT_EQUAL(ev, IO::P_NONE); +} + + +PipeWriter::PipeWriter(IO::Pipe &p): + pipe(p) +{ + launch(); +} + +void PipeWriter::main() +{ + for(unsigned i=0; i<256; ++i) + pipe.put(i); +} diff --git a/tests/thread.cpp b/tests/thread.cpp new file mode 100644 index 0000000..9529af8 --- /dev/null +++ b/tests/thread.cpp @@ -0,0 +1,129 @@ +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace Msp; + +class ThreadTests: public Test::RegisteredTest +{ +private: + std::vector data; + +public: + ThreadTests(); + + static const char *get_name() { return "Thread"; } + +private: + void single(); + void multiple(); +}; + + +class TestThread: public Thread +{ +private: + int *start; + unsigned count; + unsigned step; + int value; + bool done; + +public: + TestThread(int *, unsigned, unsigned, int); + +private: + virtual void main(); + +public: + bool is_done() const { return done; } +}; + + +ThreadTests::ThreadTests(): + data(1000000) +{ + add(&ThreadTests::single, "Single thread"); + add(&ThreadTests::multiple, "Multiple threads"); +} + +void ThreadTests::single() +{ + fill(data.begin(), data.end(), -1); + + TestThread thread(&data[0], data.size(), 1, 1); + unsigned wait = 100; + while(wait && !thread.is_done()) + { + Time::sleep(100*Time::msec); + --wait; + } + + if(!wait) + fail("Thread did not finish"); + + thread.join(); + + for(vector::iterator i=data.begin(); i!=data.end(); ++i) + if(*i!=1) + fail("Invalid data"); +} + +void ThreadTests::multiple() +{ + fill(data.begin(), data.end(), -1); + + list threads; + for(unsigned i=0; i<10; ++i) + threads.push_back(new TestThread(&data[i], data.size()/10, 10, i+1)); + unsigned wait = 100; + while(wait && !threads.empty()) + { + Time::sleep(100*Time::msec); + for(list::iterator i=threads.begin(); i!=threads.end();) + { + if((*i)->is_done()) + { + (*i)->join(); + delete *i; + threads.erase(i++); + } + else + ++i; + } + --wait; + } + + if(!wait) + fail("Threads did not finish"); + + for(unsigned i=0; i(i%10+1)) + fail("Invalid data"); +} + + +TestThread::TestThread(int *s, unsigned c, unsigned t, int v): + start(s), + count(c), + step(t), + value(v), + done(false) +{ + launch(); +} + +void TestThread::main() +{ + int *ptr = start; + for(unsigned i=0; i +#include +#include + +using namespace std; +using namespace Msp; + +class TimeZoneTests: public Test::RegisteredTest +{ +public: + TimeZoneTests(); + + static const char *get_name() { return "TimeZone"; } + +private: + void utc(); + void local(); + void custom(); +}; + + +TimeZoneTests::TimeZoneTests() +{ + add(&TimeZoneTests::utc, "UTC"); + add(&TimeZoneTests::local, "Local"); + add(&TimeZoneTests::custom, "Custom"); +} + +void TimeZoneTests::utc() +{ + Time::TimeZone tz = Time::TimeZone::utc(); + EXPECT_EQUAL(tz.get_name(), "UTC"); + EXPECT_EQUAL(tz.get_offset(), Time::zero); +} + +void TimeZoneTests::local() +{ + Time::TimeZone tz = Time::TimeZone::local(); + info(format("'%s' %s", tz.get_name(), tz.get_offset())); +} + +void TimeZoneTests::custom() +{ + Time::TimeZone tz1(120); + EXPECT_EQUAL(tz1.get_name(), "UTC+2"); + Time::TimeZone tz2(345); + EXPECT_EQUAL(tz2.get_name(), "UTC+5:45"); +} diff --git a/tests/variant.cpp b/tests/variant.cpp new file mode 100644 index 0000000..99cacdf --- /dev/null +++ b/tests/variant.cpp @@ -0,0 +1,113 @@ +#include +#include + +using namespace std; +using namespace Msp; + +class VariantTests: public Test::RegisteredTest +{ +public: + VariantTests(); + + static const char *get_name() { return "Variant"; } + +private: + void integer(); + void stdstring(); + void pointer(); + void ref_into(); + void copying(); + void destruction(); + void types(); + void mismatch(); +}; + +class Counter +{ +private: + int &count; + +public: + Counter(int &c): count(c) { ++count; } + Counter(const Counter &c): count(c.count) { ++count; } + ~Counter() { --count; } +}; + +VariantTests::VariantTests() +{ + add(&VariantTests::integer, "Integer"); + add(&VariantTests::stdstring, "std::string"); + add(&VariantTests::pointer, "Pointer"); + add(&VariantTests::ref_into, "Reference into"); + add(&VariantTests::copying, "Copying"); + add(&VariantTests::destruction, "Destruction"); + add(&VariantTests::types, "Type checks"); + add(&VariantTests::mismatch, "Mismatch").expect_throw(); +} + +void VariantTests::integer() +{ + Variant var = 42; + EXPECT_EQUAL(var.value(), 42); +} + +void VariantTests::stdstring() +{ + Variant var = string("foobar"); + EXPECT_EQUAL(var.value(), "foobar"); +} + +void VariantTests::pointer() +{ + int i = 7; + Variant var = &i; + *var.value() = 42; + EXPECT_EQUAL(i, 42); +} + +void VariantTests::ref_into() +{ + Variant var = 7; + int &r = var.value(); + r = 42; + EXPECT_EQUAL(var.value(), 42); +} + +void VariantTests::copying() +{ + Variant var = 42; + Variant var2 = var; + EXPECT_EQUAL(var2.value(), 42); +} + +void VariantTests::destruction() +{ + int count = 0; + { + Variant var = Counter(count); + Variant var2 = var; + EXPECT_EQUAL(count, 2); + Variant var3; + var3 = var; + EXPECT_EQUAL(count, 3); + var2 = Variant(); + EXPECT_EQUAL(count, 2); + } + EXPECT_EQUAL(count, 0); +} + +void VariantTests::types() +{ + Variant var = 42U; + EXPECT(!var.check_type()); + EXPECT(var.check_type()); + EXPECT(!var.check_type()); + EXPECT(!var.check_type()); + EXPECT(!var.check_type()); +} + +void VariantTests::mismatch() +{ + Variant var = 1; + var.value(); +} -- 2.45.2