Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 8 additions & 9 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@ project(chapterFirstExample)
cmake_minimum_required(VERSION 2.6)

include_directories($ENV{GMOCK_HOME}/include $ENV{GMOCK_HOME}/gtest/include)
link_directories($ENV{GMOCK_HOME}/mybuild $ENV{GMOCK_HOME}/gtest/mybuild)
add_definitions(-std=c++0x)
link_directories($ENV{GMOCK_HOME}/lib/.libs $ENV{GMOCK_HOME}/gtest/lib/.libs)
add_definitions(-std=c++11)
set(CMAKE_CXX_FLAGS "${CMAXE_CXX_FLAGS} -Wall")

set(sources
main.cpp
set(sources
main.cpp
SoundexTest.cpp)
add_executable(test ${sources})
target_link_libraries(test pthread)
target_link_libraries(test gmock)
target_link_libraries(test gtest)

add_executable(unittests ${sources})
target_link_libraries(unittests pthread)
target_link_libraries(unittests gmock)
target_link_libraries(unittests gtest)
1 change: 0 additions & 1 deletion ExpectToFailCompile

This file was deleted.

93 changes: 93 additions & 0 deletions Soundex.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#ifndef Soundex_h
#define Soundex_h
#include <string>
#include <unordered_map>

class Soundex {
public:
static const size_t MaxCodeLength{4};

std::string encode(const std::string& word) const {
return zeroPad(upperFront(head(word)) + tail(encodedDigits(word)));
}

std::string encodedDigit(char letter) const {
const std::unordered_map<char, std::string> encodings {
{'b', "1"}, {'f', "1"}, {'p', "1"}, {'v', "1"},
{'c', "2"}, {'g', "2"}, {'j', "2"}, {'k', "2"}, {'q', "2"},
{'s', "2"}, {'x', "2"}, {'z', "2"},
{'d', "3"}, {'t', "3"},
{'l', "4"},
{'m', "5"}, {'n', "5"},
{'r', "6"}
};
auto it = encodings.find(lower(letter));
return it == encodings.end() ? NotADigit : it->second;
}

private:
char lower(char c) const {
return std::tolower(static_cast<unsigned char>(c));
}

std::string head(const std::string& word) const {
return word.substr(0, 1);
}

std::string tail(const std::string& word) const {
return word.substr(1);
}

const std::string NotADigit{"*"};

std::string encodedDigits(const std::string& word) const {
std::string encoding;
encodeHead(encoding, word);
encodeTail(encoding, word);
return encoding;
}

void encodeHead(std::string& encoding, const std::string& word) const {
encoding += encodedDigit(word.front());
}

void encodeTail(std::string& encoding, const std::string& word) const {
for (auto i = 1u; i < word.length(); i++) {
if (!isComplete(encoding))
encodeLetter(encoding, word[i], word[i-1]);
}
}

void encodeLetter(std::string& encoding, char letter, char lastLetter) const {
auto digit = encodedDigit(letter);
if (digit != NotADigit &&
(digit != lastDigit(encoding) || isVowel(lastLetter)))
encoding += digit;
}

bool isVowel(char letter) const {
return
std::string("aeiouy").find(lower(letter)) != std::string::npos;
}

bool isComplete (const std::string& encoding) const {
return encoding.length() == MaxCodeLength;
}

std::string zeroPad(const std::string& word) const {
auto zerosNeeded = MaxCodeLength - word.length();
return word + std::string(zerosNeeded, '0');
}

std::string lastDigit(const std::string& encoding) const {
if (encoding.empty()) return NotADigit;
return std::string(1, encoding.back());
}

std::string upperFront(const std::string& string) const {
return std::string(1,
std::toupper(static_cast<unsigned char>(string.front())));
}
};

#endif
61 changes: 58 additions & 3 deletions SoundexTest.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,61 @@
#include "gmock/gmock.h" //<label id="code.include"/>
#include "gmock/gmock.h"
#include "Soundex.h"

TEST(SoundexEncoding, RetainsSoleLetterOfOneLetterWord) { //<label id="code.test"/>
Soundex soundex; //<label id="code.construct"/>
using namespace testing;

class SoundexEncoding: public Test {
public:
Soundex soundex;
};

TEST_F(SoundexEncoding, RetainsSoleLetterOfOneLetterWord) {
ASSERT_THAT(soundex.encode("A"), Eq("A000"));
}

TEST_F(SoundexEncoding, PadsWithZerosToEnsureThreeDigits) {
ASSERT_THAT(soundex.encode("I"), Eq("I000"));
}

TEST_F(SoundexEncoding, ReplacesConsonantsWithAppropriateDigits) {
EXPECT_THAT(soundex.encode("Ax"), Eq("A200"));
}

TEST_F(SoundexEncoding, IgnoresNonAlphabetics) {
ASSERT_THAT(soundex.encode("A#"), Eq("A000"));
}

TEST_F(SoundexEncoding, ReplacesMultipleConsonantsWithDigits) {
ASSERT_THAT(soundex.encode("Acdl"), Eq("A234"));
}

TEST_F(SoundexEncoding, LimitsLengthToFourCharacters) {
ASSERT_THAT(soundex.encode("Dcdlb").length(), Eq(4u));
}

TEST_F(SoundexEncoding, IgnoresVowelLikeLetters) {
ASSERT_THAT(soundex.encode("BaAeEiIoOuUhHyYcdl"), Eq("B234"));
}

TEST_F(SoundexEncoding, CombinesDublicateEncodings) {
ASSERT_THAT(soundex.encodedDigit('b'), Eq(soundex.encodedDigit('f')));
ASSERT_THAT(soundex.encodedDigit('c'), Eq(soundex.encodedDigit('g')));
ASSERT_THAT(soundex.encodedDigit('d'), Eq(soundex.encodedDigit('t')));

ASSERT_THAT(soundex.encode("Abfcgdt"), Eq("A123"));
}

TEST_F(SoundexEncoding, UppercasesFirstLetter) {
ASSERT_THAT(soundex.encode("abcd"), StartsWith("A"));
}

TEST_F(SoundexEncoding, IgnoresCaseWhenEncodingConsonants) {
ASSERT_THAT(soundex.encode("BCDL"), Eq(soundex.encode("Bcdl")));
}

TEST_F(SoundexEncoding, CombinesDuplicateCodesWhen2ndLetterDuplicates1st) {
ASSERT_THAT(soundex.encode("Bbcd"), Eq("B230"));
}

TEST_F(SoundexEncoding, DoesNotCombineDuplicateEncodingsSeparatedByVowels) {
ASSERT_THAT(soundex.encode("Jbob"), Eq("J110"));
}