// basictests.cpp : basic unit tests
//
/**
* Copyright (C) 2009 10gen Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the GNU Affero General Public License in all respects
* for all of the code used other than as permitted herein. If you modify
* file(s) with this exception, you may extend this exception to your
* version of the file(s), but you are not obligated to do so. If you do not
* wish to do so, delete this exception statement from your version. If you
* delete this exception statement from all source files in the program,
* then also delete it in the license file.
*/
#include "mongo/platform/basic.h"
#include
#include "mongo/db/client.h"
#include "mongo/db/storage/mmap_v1/compress.h"
#include "mongo/db/storage/mmap_v1/paths.h"
#include "mongo/dbtests/dbtests.h"
#include "mongo/util/base64.h"
#include "mongo/util/queue.h"
#include "mongo/util/stringutils.h"
#include "mongo/util/text.h"
#include "mongo/util/thread_safe_string.h"
#include "mongo/util/time_support.h"
#include "mongo/util/timer.h"
namespace BasicTests {
using std::unique_ptr;
using std::shared_ptr;
using std::cout;
using std::dec;
using std::endl;
using std::hex;
using std::string;
using std::stringstream;
using std::vector;
class Rarely {
public:
void run() {
int first = 0;
int second = 0;
for (int i = 0; i < 128; ++i) {
incRarely(first);
incRarely2(second);
}
ASSERT_EQUALS(1, first);
ASSERT_EQUALS(1, second);
}
private:
void incRarely(int& c) {
RARELY++ c;
}
void incRarely2(int& c) {
RARELY++ c;
}
};
class Base64Tests {
public:
void roundTrip(string s) {
ASSERT_EQUALS(s, base64::decode(base64::encode(s)));
}
void roundTrip(const unsigned char* _data, int len) {
const char* data = (const char*)_data;
string s = base64::encode(data, len);
string out = base64::decode(s);
ASSERT_EQUALS(out.size(), static_cast(len));
bool broke = false;
for (int i = 0; i < len; i++) {
if (data[i] != out[i])
broke = true;
}
if (!broke)
return;
cout << s << endl;
for (int i = 0; i < len; i++)
cout << hex << (data[i] & 0xFF) << dec << " ";
cout << endl;
for (int i = 0; i < len; i++)
cout << hex << (out[i] & 0xFF) << dec << " ";
cout << endl;
ASSERT(0);
}
void run() {
ASSERT_EQUALS("ZWxp", base64::encode("eli", 3));
ASSERT_EQUALS("ZWxpb3Rz", base64::encode("eliots", 6));
ASSERT_EQUALS("ZWxpb3Rz", base64::encode("eliots"));
ASSERT_EQUALS("ZQ==", base64::encode("e", 1));
ASSERT_EQUALS("ZWw=", base64::encode("el", 2));
roundTrip("e");
roundTrip("el");
roundTrip("eli");
roundTrip("elio");
roundTrip("eliot");
roundTrip("eliots");
roundTrip("eliotsz");
unsigned char z[] = {0x1, 0x2, 0x3, 0x4};
roundTrip(z, 4);
unsigned char y[] = {0x01, 0x10, 0x83, 0x10, 0x51, 0x87, 0x20, 0x92, 0x8B, 0x30,
0xD3, 0x8F, 0x41, 0x14, 0x93, 0x51, 0x55, 0x97, 0x61, 0x96,
0x9B, 0x71, 0xD7, 0x9F, 0x82, 0x18, 0xA3, 0x92, 0x59, 0xA7,
0xA2, 0x9A, 0xAB, 0xB2, 0xDB, 0xAF, 0xC3, 0x1C, 0xB3, 0xD3,
0x5D, 0xB7, 0xE3, 0x9E, 0xBB, 0xF3, 0xDF, 0xBF};
roundTrip(y, 4);
roundTrip(y, 40);
}
};
namespace stringbuildertests {
#define SBTGB(x) \
ss << (x); \
sb << (x);
class Base {
virtual void pop() = 0;
public:
Base() {}
virtual ~Base() {}
void run() {
pop();
ASSERT_EQUALS(ss.str(), sb.str());
}
stringstream ss;
StringBuilder sb;
};
class simple1 : public Base {
void pop() {
SBTGB(1);
SBTGB("yo");
SBTGB(2);
}
};
class simple2 : public Base {
void pop() {
SBTGB(1);
SBTGB("yo");
SBTGB(2);
SBTGB(12123123123LL);
SBTGB("xxx");
SBTGB(5.4);
SBTGB(5.4312);
SBTGB("yyy");
SBTGB((short)5);
SBTGB((short)(1231231231231LL));
}
};
class reset1 {
public:
void run() {
StringBuilder sb;
sb << "1"
<< "abc"
<< "5.17";
ASSERT_EQUALS("1abc5.17", sb.str());
ASSERT_EQUALS("1abc5.17", sb.str());
sb.reset();
ASSERT_EQUALS("", sb.str());
sb << "999";
ASSERT_EQUALS("999", sb.str());
}
};
class reset2 {
public:
void run() {
StringBuilder sb;
sb << "1"
<< "abc"
<< "5.17";
ASSERT_EQUALS("1abc5.17", sb.str());
ASSERT_EQUALS("1abc5.17", sb.str());
sb.reset(1);
ASSERT_EQUALS("", sb.str());
sb << "999";
ASSERT_EQUALS("999", sb.str());
}
};
} // namespace stringbuildertests
class sleeptest {
public:
void run() {
Timer t;
int matches = 0;
for (int p = 0; p < 3; p++) {
sleepsecs(1);
int sec = (t.millis() + 2) / 1000;
if (sec == 1)
matches++;
else
mongo::unittest::log() << "temp millis: " << t.millis() << endl;
ASSERT(sec >= 0 && sec <= 2);
t.reset();
}
if (matches < 2)
mongo::unittest::log() << "matches:" << matches << endl;
ASSERT(matches >= 2);
sleepmicros(1527123);
ASSERT(t.micros() > 1000000);
ASSERT(t.micros() < 2000000);
t.reset();
sleepmillis(1727);
ASSERT(t.millis() >= 1000);
ASSERT(t.millis() <= 2500);
{
int total = 1200;
int ms = 2;
t.reset();
for (int i = 0; i < (total / ms); i++) {
sleepmillis(ms);
}
{
int x = t.millis();
if (x < 1000 || x > 2500) {
cout << "sleeptest finds sleep accuracy to be not great. x: " << x << endl;
ASSERT(x >= 1000);
ASSERT(x <= 20000);
}
}
}
#ifdef __linux__
{
int total = 1200;
int micros = 100;
t.reset();
int numSleeps = 1000 * (total / micros);
for (int i = 0; i < numSleeps; i++) {
sleepmicros(micros);
}
{
int y = t.millis();
if (y < 1000 || y > 2500) {
cout << "sleeptest y: " << y << endl;
ASSERT(y >= 1000);
/* ASSERT( y <= 100000 ); */
}
}
}
#endif
}
};
class SleepBackoffTest {
public:
void run() {
int maxSleepTimeMillis = 1000;
Backoff backoff(maxSleepTimeMillis, maxSleepTimeMillis * 2);
// Double previous sleep duration
ASSERT_EQUALS(backoff.getNextSleepMillis(0, 0, 0), 1);
ASSERT_EQUALS(backoff.getNextSleepMillis(2, 0, 0), 4);
ASSERT_EQUALS(backoff.getNextSleepMillis(256, 0, 0), 512);
// Make sure our backoff increases to the maximum value
ASSERT_EQUALS(backoff.getNextSleepMillis(maxSleepTimeMillis - 200, 0, 0),
maxSleepTimeMillis);
ASSERT_EQUALS(backoff.getNextSleepMillis(maxSleepTimeMillis * 2, 0, 0), maxSleepTimeMillis);
// Make sure that our backoff gets reset if we wait much longer than the maximum wait
unsigned long long resetAfterMillis = maxSleepTimeMillis + maxSleepTimeMillis * 2;
ASSERT_EQUALS(backoff.getNextSleepMillis(20, resetAfterMillis, 0), 40); // no reset here
ASSERT_EQUALS(backoff.getNextSleepMillis(20, resetAfterMillis + 1, 0),
1); // reset expected
}
};
class AssertTests {
public:
int x;
AssertTests() {
x = 0;
}
string foo() {
x++;
return "";
}
void run() {
uassert(-1, foo(), 1);
if (x != 0) {
ASSERT_EQUALS(0, x);
}
try {
uassert(-1, foo(), 0);
} catch (...) {
}
ASSERT_EQUALS(1, x);
}
};
class ThreadSafeStringTest {
public:
void run() {
ThreadSafeString s;
s = "eliot";
ASSERT_EQUALS(s.toString(), "eliot");
ASSERT(s.toString() != "eliot2");
ThreadSafeString s2;
s2 = s.toString().c_str();
ASSERT_EQUALS(s2.toString(), "eliot");
{
string foo;
{
ThreadSafeString bar;
bar = "eliot2";
foo = bar.toString();
}
ASSERT_EQUALS("eliot2", foo);
}
}
};
struct StringSplitterTest {
void test(string s) {
vector v = StringSplitter::split(s, ",");
ASSERT_EQUALS(s, StringSplitter::join(v, ","));
}
void run() {
test("a");
test("a,b");
test("a,b,c");
vector x = StringSplitter::split("axbxc", "x");
ASSERT_EQUALS(3, (int)x.size());
ASSERT_EQUALS("a", x[0]);
ASSERT_EQUALS("b", x[1]);
ASSERT_EQUALS("c", x[2]);
x = StringSplitter::split("axxbxxc", "xx");
ASSERT_EQUALS(3, (int)x.size());
ASSERT_EQUALS("a", x[0]);
ASSERT_EQUALS("b", x[1]);
ASSERT_EQUALS("c", x[2]);
}
};
struct IsValidUTF8Test {
// macros used to get valid line numbers
#define good(s) ASSERT(isValidUTF8(s));
#define bad(s) ASSERT(!isValidUTF8(s));
void run() {
good("A");
good("\xC2\xA2"); // cent: ¢
good("\xE2\x82\xAC"); // euro: €
good("\xF0\x9D\x90\x80"); // Blackboard A: 𝐀
// abrupt end
bad("\xC2");
bad("\xE2\x82");
bad("\xF0\x9D\x90");
bad("\xC2 ");
bad("\xE2\x82 ");
bad("\xF0\x9D\x90 ");
// too long
bad("\xF8\x80\x80\x80\x80");
bad("\xFC\x80\x80\x80\x80\x80");
bad("\xFE\x80\x80\x80\x80\x80\x80");
bad("\xFF\x80\x80\x80\x80\x80\x80\x80");
bad("\xF5\x80\x80\x80"); // U+140000 > U+10FFFF
bad("\x80"); // cant start with continuation byte
bad("\xC0\x80"); // 2-byte version of ASCII NUL
#undef good
#undef bad
}
};
class QueueTest {
public:
void run() {
BlockingQueue q;
Timer t;
int x;
ASSERT(!q.blockingPop(x, 5));
ASSERT(t.seconds() > 3 && t.seconds() < 9);
}
};
class StrTests {
public:
void run() {
ASSERT_EQUALS(1u, str::count("abc", 'b'));
ASSERT_EQUALS(3u, str::count("babab", 'b'));
}
};
class HostAndPortTests {
public:
void run() {
HostAndPort a("x1", 1000);
HostAndPort b("x1", 1000);
HostAndPort c("x1", 1001);
HostAndPort d("x2", 1000);
ASSERT(a == b);
ASSERT(a != c);
ASSERT(a != d);
}
};
class RelativePathTest {
public:
void run() {
RelativePath a = RelativePath::fromRelativePath("a");
RelativePath b = RelativePath::fromRelativePath("a");
RelativePath c = RelativePath::fromRelativePath("b");
RelativePath d = RelativePath::fromRelativePath("a/b");
ASSERT(a == b);
ASSERT(a != c);
ASSERT(a != d);
ASSERT(c != d);
}
};
struct CompressionTest1 {
void run() {
const char* c = "this is a test";
std::string s;
size_t len = compress(c, strlen(c) + 1, &s);
verify(len > 0);
std::string out;
bool ok = uncompress(s.c_str(), s.size(), &out);
verify(ok);
verify(strcmp(out.c_str(), c) == 0);
}
} ctest1;
class All : public Suite {
public:
All() : Suite("basic") {}
void setupTests() {
add();
add();
add();
add();
add();
add();
add();
add();
add();
add();
add();
add();
add();
add();
add();
add();
}
};
SuiteInstance myall;
} // namespace BasicTests