Pull to refresh

gTest и

Lumber room
Написал недавно пост про " По рзелульаттам илссеовадний одонго анлигйсокго унвиертисета, не иеемт занчнеия, в кокам пряокде рсапожолены бкувы в солве..."?
Очень меня заинтересовало — правда это или нет, а тут еще захотелось gTest поизучать и заодно попрактиковаться в Программировании через тестирование, так что написал я программку, которая таким вот образом коверкает слова.
И действительно, это правило отлично работает — тексты, исковерканные моей программой, достаточно свободно читаются.
Кому интересно самому поиграться, могут скачать программу тут:
ScrambleStrings.rar — там можно вставить любой текст в верхний EditBox и нажать на «Создать». Получившийся текст можно скопировать в буфер обмена кнопкой «Копировать» и вставить куда хочешь.



gTest оказался жутко удобной библиотекой. Почти не пришлось тратить время на его подключение — только пара ошибок линкования была и та из-за конфликтов с MFC. Использование gTest — это всего 2 строки для старта тестов, причем ими еще и через командную строку можно управлять.
Сами тесты тоже создавать предельно просто (в конце поста будет код — можно заценить).
Я решил написать эту утилитку по правилу «Сначала тест — потом код» и не пожалел.
Придумал вначале несколько тестовых случаев, написал их — это помогло продумать интерфейс функции для переворачивания строки, которую я сначала хотел сделать в классе, но оказалось, что гораздо удобнее сделать просто 1 функцию.
Как ни странно, но все те тесты, что в итоге были написаны, удалось написать сразу с первой попытки. Новых тестов придумывать не пришлось — хватило этих.
Когда тесты готовы — тупо пишешь реализацию, компилируешь, запускаешь тесты, смотришь ошибки и исправляешь.
Когда все заработало в тестах — только тогда запустил саму программу с GUI, она тоже уже сразу заработала :)
На все ушло около 2 часов, из которых около часа — на разборки с MFC и SetClipboardData.
Задачи сделать оптимально по быстродействию не стояло — просто делал быстро и чтобы работало правильно.

Надо сказать, что я до этого использовал CppUnit в нескольких проектах и был им доволен. Но gTest показался мне более простым и удобным. К тому же он Open Source и будет постоянно дорабатываться. Мой выбор теперь — gTest.

Собственно код тестов:

#include «stdafx.h»
#include <gtest/gtest.h>

#include «StringScrambler.h»


TEST(StringScramblerTest, TestNoChangeCases)
{
std::string testCases[] = {
"",
" ",
«I»,
«In»,
«Ink»,
" I ",
«I am Ink»,
".I, ;am:-\'\" ?Ink!",
" Are you Ink? ",
" \n\r\n\n\r\r Are you Ink? I use tab and car ret\n\r",
" \n\r\n\n\r\r ",
«Arrrrrrrrrrrrrrrrrrrrrre»
};
for (int i = 0; i < sizeof(testCases)/sizeof(testCases[0]); ++i)
{
EXPECT_STREQ(testCases[i].c_str(), StringScrambler::ScrambleString(testCases[i]).c_str());
}
}


TEST(StringScramblerTest, TestGoodOneWordCases)
{
std::string testCases[] = {«Перевернул», «Переворот», «Война», «Всегда», «Плохо», «Независимо», «Целей»};
std::string ret;
for (int i = 0; i < sizeof(testCases)/sizeof(testCases[0]); ++i)
{
ret = StringScrambler::ScrambleString(testCases[i]);
EXPECT_STRNE(ret.c_str(), testCases[i].c_str());
assert(ret != testCases[i]);
EXPECT_EQ(testCases[i][0], ret[0]);
EXPECT_EQ(testCases[i][testCases[i].length() — 1], ret[ret.length() — 1]);
EXPECT_EQ(testCases[i].length(), ret.length());
}
}

TEST(StringScramblerTest, TestSeveralWords)
{
std::string checkPhraze = «которое попадается им на пути»;
std::string ret = StringScrambler::ScrambleString(checkPhraze);
EXPECT_STRNE(ret.c_str(), checkPhraze.c_str());

EXPECT_EQ(checkPhraze[0], ret[0]);
EXPECT_EQ(checkPhraze[checkPhraze.length() — 1], ret[ret.length() — 1]);
EXPECT_EQ(checkPhraze.length(), ret.length());

EXPECT_EQ(checkPhraze[6], ret[6]);
EXPECT_EQ(checkPhraze[8], ret[8]);
EXPECT_EQ(checkPhraze[17], ret[17]);
EXPECT_EQ(checkPhraze[25], ret[25]);
}

TEST(StringScramblerTest, TestSeveralWords2)
{
std::string checkPhraze = «преувеличение, потому что Грузия не входит в состав Европы»;
std::string ret = StringScrambler::ScrambleString(checkPhraze);
EXPECT_STRNE(ret.c_str(), checkPhraze.c_str());

EXPECT_EQ(checkPhraze[0], ret[0]);
EXPECT_EQ(checkPhraze[checkPhraze.length() — 1], ret[ret.length() — 1]);
EXPECT_EQ(checkPhraze.length(), ret.length());


EXPECT_EQ(checkPhraze[12], ret[12]);
EXPECT_EQ(checkPhraze[15], ret[15]);
EXPECT_EQ(checkPhraze[20], ret[20]);
EXPECT_EQ(checkPhraze[26], ret[26]);
EXPECT_EQ(checkPhraze[31], ret[31]);
EXPECT_EQ(checkPhraze[36], ret[36]);
}


Код функции перемешивания строки, если кому надо (еще раз повторюсь, что не было задачи сделать это наиболее оптимально и красиво. Но если будут коментарии и предложения — с удовольствием почитаю):
bool isItDelimiter(char sym)
{
char delimiters[] = {' ', '\t', ' ', '\r', '\n', '.', ',', '!', '?', ';', ':', '\'', '\"', '-', ')', '(', '[', ']', '{', '}'};
for(int i = 0; i < sizeof(delimiters)/sizeof(delimiters[0]); ++i)
if (sym == delimiters[i])
return true;

return false;
}

void scramble(const char* sourceStart, const char* sourceEnd, char* to)
{
int numSymbols = (int)(sourceEnd — sourceStart);
if (numSymbols < 4)
return;
bool stringFromOneSymbol = true;
for(const char* s = sourceStart + 1; s < sourceEnd — 2; ++s)
if (*s != *(s+1))
{
stringFromOneSymbol = false;
break;
}
if (stringFromOneSymbol)
return;

int numChanges = numSymbols/3 + 2;
while(numChanges-- > 0)
{
int posFrom = rand()%(numSymbols — 2) + 1;
int posTo= rand()%(numSymbols — 2) + 1;
char t = to[posTo];
to[posTo] = to[posFrom];
to[posFrom] = t;
}

if (strncmp(sourceStart, to, numSymbols) == 0)
scramble(sourceStart, sourceEnd, to);
}

const char* findAndScrambleNextString(const char* from, char* &to)
{
if (*from == 0)
return from;
const char* prevNotDelimiter = 0;

while(*from)
{
*to = *from;
if (isItDelimiter(*from))
{
if (prevNotDelimiter != 0)
scramble(prevNotDelimiter, from, to — (from — prevNotDelimiter));

prevNotDelimiter = 0;
}
else
{
if (prevNotDelimiter == 0)
prevNotDelimiter = from;
}
++from;
++to;
}
if (prevNotDelimiter != 0)
scramble(prevNotDelimiter, from, to — (from — prevNotDelimiter));
return from;
}

std::string StringScrambler::ScrambleString(const std::string& source)
{
if (source.length() < 4)
return source;

char* str = new char[source.length() + 1];
char* strStart = str;
strcpy_s(str, source.length() + 1, source.c_str());

const char* from = source.c_str();
while(*from)
{
from = findAndScrambleNextString(from, str);
}

std::string ret = strStart;
delete []strStart;

return ret;
}
Tags:programmingtest driven developmenttestingunit testingтестированиепрограммирование на c
Hubs: Lumber room
Total votes 5: ↑4 and ↓1+3
Views473

Popular right now

Top of the last 24 hours