I recently wrote a post about
"According to rzelulattam or ilsevadochny odongo unlegisokgo univertisetta, do not have a lot of time, in cocoa cake there is a boiled bakwy in solv ..." ?
I was very interested in whether it was true or not, and then I also wanted to
gTest to study and practice
Programming through testing at the same time, so I wrote a
program that distorts words in this way.
And indeed, this rule works fine - the texts that have been distorted by my program are quite readable.
To whom it is interesting to play, you can download the program here:
ScrambleStrings.rar - there you can paste any text into the top EditBox and click on "Create". The resulting text can be copied to the clipboard with the "Copy" button and paste wherever you want.
gTest turned out to be a terribly convenient library. It was almost not necessary to spend time on connecting it - only a couple of linking errors were also due to conflicts with MFC. Using gTest is only 2 lines to start tests, and they can also be controlled via the command line.
The tests themselves are also very easy to create (at the end of the post there will be a code - you can check it out)
I decided to write this utility according to the rule
“First test - then code” and did not regret it.
I came up with several test cases at first, wrote them - it helped me think through the function interface for turning the line, which I first wanted to do in the class, but it turned out that it was much more convenient to do just 1 function.
Oddly enough, but all those tests that were eventually written were able to write right away on the first attempt. There was no need to invent new tests - there were enough of these.
When the tests are ready - you stupidly write the implementation, compile, run the tests, look at the errors and fix them.
When everything worked in tests - only then I launched the program with GUI, it also started working right away :)
It took about 2 hours, of which about an hour was spent on disassembling from MFC and SetClipboardData.
There were no tasks to do optimally in terms of speed - I just did it quickly and in order to work correctly.
')
I must say that I had previously used CppUnit in several projects and was pleased with it. But gTest seemed to me more simple and convenient. In addition, it is open source and will be constantly being finalized. My choice now is gTest.
Actually test code:
#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 [] = {"Inverted", "Coup", "War", "Always", "Bad", "Independently", "Goals"};
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 = "which comes their way";
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 = "exaggeration, because Georgia is not part of Europe";
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]);
}
The code of the line shuffle function, if anyone needs it (I repeat once again that there was no task to do it in the most optimal and beautiful way. But if there are comments and suggestions, I will read it with pleasure):
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;
}