Merge pull request #133 from jmossberg/master

Refactor C++ version of the Gilded Rose kata
This commit is contained in:
Emily Bache 2019-12-09 09:00:25 +01:00 committed by GitHub
commit 984e38a3a8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 20786 additions and 287 deletions

2
cpp/.gitignore vendored
View File

@ -1,4 +1,4 @@
.idea
.vs
cmake-build-debug
gtest
build

View File

@ -1,67 +1,34 @@
cmake_minimum_required(VERSION 2.8.4)
project(cpp)
cmake_minimum_required(VERSION 3.13)
project(gilded-rose-refactoring-kata-cpp)
# CMake FindThreads is broken until 3.1
#find_package(Threads REQUIRED)
set(CMAKE_THREAD_LIBS_INIT pthread)
# Load FetchContent module.
include(FetchContent)
enable_testing()
find_package(GTest)
include(ExternalProject)
if(NOT ${GTEST_FOUND}) # Download gtest if not installed
message("No system gtest use external project")
ExternalProject_Add(googletest
# Declare GoogleTest as the content to fetch
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG master
SOURCE_DIR "${CMAKE_BINARY_DIR}/googletest-src"
BINARY_DIR "${CMAKE_BINARY_DIR}/googletest-build"
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
TEST_COMMAND ""
)
add_subdirectory(${CMAKE_BINARY_DIR}/googletest-src
${CMAKE_BINARY_DIR}/googletest-build
EXCLUDE_FROM_ALL)
set(GTEST_BOTH_LIBRARIES gtest gtest_main)
endif()
ExternalProject_Add(aprovaltest
PREFIX ${CMAKE_BINARY_DIR}/aprovaltest
URL https://github.com/approvals/ApprovalTests.cpp/releases/download/v.2.0.0/ApprovalTests.v.2.0.0.hpp
DOWNLOAD_NO_EXTRACT 1
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
GIT_TAG release-1.8.1
)
include_directories(${GTEST_INCLUDE_DIRS})
include_directories(${CMAKE_BINARY_DIR}/aprovaltest/src)
# Fetch GoogleTest amd make build scripts available
if (NOT googletest_POPULATED)
FetchContent_Populate(googletest)
endif()
add_executable(GildedRose
GildedRose.cc
GildedRose.h
GildedRoseUnitTests.cc
)
target_link_libraries(GildedRose ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
add_test(NAME GildedRose COMMAND GildedRose)
#set(gtest_force_shared_crt OFF CACHE BOOL "" FORCE)
# Force Google Test to link the C/C++ runtimes dynamically when
# building on Visual Studio
# Link:
# * https://github.com/google/googletest/tree/release-1.8.1/googletest#visual-studio-dynamic-vs-static-runtimes
if (MSVC)
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
endif()
add_executable(GildedRoseTextTests
GildedRose.cc
GildedRose.h
GildedRoseTextTests.cc
)
target_link_libraries(GildedRoseTextTests ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
add_test(NAME GildedRoseTextTests COMMAND GildedRoseTextTests)
# Bring the populated content into the build
add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR})
add_executable(GildedRoseApprovalTests
GildedRose.cc
GildedRose.h
GildedRoseApprovalTests.cc
GildedRoseApprovalMain.cc
)
target_link_libraries(GildedRoseApprovalTests ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
set_property(TARGET GildedRoseApprovalTests PROPERTY CXX_STANDARD 14)
add_dependencies(GildedRoseApprovalTests aprovaltest)
add_test(NAME GildedRoseApprovalTests COMMAND GildedRoseApprovalTests)
enable_testing()
add_subdirectory(src)
add_subdirectory(lib)
add_subdirectory(test)

View File

@ -1,2 +0,0 @@
#define APPROVALS_GOOGLETEST
#include "ApprovalTests.v.2.0.0.hpp"

View File

@ -1,28 +0,0 @@
#include "ApprovalTests.v.2.0.0.hpp"
#include <gtest/gtest.h>
#include "GildedRose.h"
std::ostream& operator<<(std::ostream& os, const Item& obj)
{
return os
<< "name: " << obj.name
<< ", sellIn: " << obj.sellIn
<< ", quality: " << obj.quality;
}
TEST(GildedRoseApprovalTests, VerifyCombinations)
{
std::vector<string> names { "Foo" };
std::vector<int> sellIns { 1 };
std::vector<int> qualities { 1 };
CombinationApprovals::verifyAllCombinations<
std::vector<string>, std::vector<int>, std::vector<int>, Item>(
[](string name, int sellIn, int quality) {
vector<Item> items = {Item(name, sellIn, quality)};
GildedRose app(items);
app.updateQuality();
return items[0];
},
names, sellIns, qualities);
}

View File

@ -1,42 +0,0 @@
#include "GildedRose.h"
#include <iostream>
using namespace std;
ostream& operator<<(ostream& s, Item& item)
{
s << item.name << ", " << item.sellIn << ", " << item.quality;
return s;
}
int main()
{
vector<Item> items;
items.push_back(Item("+5 Dexterity Vest", 10, 20));
items.push_back(Item("Aged Brie", 2, 0));
items.push_back(Item("Elixir of the Mongoose", 5, 7));
items.push_back(Item("Sulfuras, Hand of Ragnaros", 0, 80));
items.push_back(Item("Sulfuras, Hand of Ragnaros", -1, 80));
items.push_back(Item("Backstage passes to a TAFKAL80ETC concert", 15, 20));
items.push_back(Item("Backstage passes to a TAFKAL80ETC concert", 10, 49));
items.push_back(Item("Backstage passes to a TAFKAL80ETC concert", 5, 49));
// this Conjured item doesn't yet work properly
items.push_back(Item("Conjured Mana Cake", 3, 6));
GildedRose app(items);
cout << "OMGHAI!" << endl;
for (int day = 0; day <= 30; day++)
{
cout << "-------- day " << day << " --------" << endl;
cout << "name, sellIn, quality" << endl;
for (vector<Item>::iterator i = items.begin(); i != items.end(); i++)
{
cout << *i << endl;
}
cout << endl;
app.updateQuality();
}
}

View File

@ -1,24 +0,0 @@
#include <gtest/gtest.h>
#include "GildedRose.h"
TEST(GildedRoseTest, Foo) {
vector<Item> items;
items.push_back(Item("Foo", 0, 0));
GildedRose app(items);
app.updateQuality();
EXPECT_EQ("fixme", app.items[0].name);
}
void example()
{
vector<Item> items;
items.push_back(Item("+5 Dexterity Vest", 10, 20));
items.push_back(Item("Aged Brie", 2, 0));
items.push_back(Item("Elixir of the Mongoose", 5, 7));
items.push_back(Item("Sulfuras, Hand of Ragnaros", 0, 80));
items.push_back(Item("Backstage passes to a TAFKAL80ETC concert", 15, 20));
items.push_back(Item("Conjured Mana Cake", 3, 6));
GildedRose app(items);
app.updateQuality();
}

View File

@ -1,85 +0,0 @@
# Makefile for building the kata file with the Google Testing Framework
#
# SYNOPSIS:
#
# make [all] - makes everything.
# make TARGET - makes the given target.
# make clean - removes all files generated by make.
# Please tweak the following variable definitions as needed by your
# project, except GTEST_HEADERS, which you can use in your own targets
# but shouldn't modify.
# Points to the root of Google Test, relative to where this file is.
# Remember to tweak this if you move this file.
GTEST_DIR = gtest
# Where to find user code.
USER_DIR = .
# Flags passed to the preprocessor.
CPPFLAGS += -I$(GTEST_DIR)/include
# Flags passed to the C++ compiler.
CXXFLAGS += -g -Wall -Wextra
# All tests produced by this Makefile. Remember to add new tests you
# created to the list.
TESTS = GildedRose
TEXTTESTS = GildedRoseTextTests
# All Google Test headers. Usually you shouldn't change this
# definition.
GTEST_HEADERS = $(GTEST_DIR)/include/gtest/*.h \
$(GTEST_DIR)/include/gtest/internal/*.h
# House-keeping build targets.
all : $(TESTS) $(TEXTTESTS)
clean :
rm -f $(TESTS) $(TEXTTESTS) gtest.a gtest_main.a *.o
# Builds gtest.a and gtest_main.a.
# Usually you shouldn't tweak such internal variables, indicated by a
# trailing _.
GTEST_SRCS_ = $(GTEST_DIR)/src/*.cc $(GTEST_DIR)/src/*.h $(GTEST_HEADERS)
# For simplicity and to avoid depending on Google Test's
# implementation details, the dependencies specified below are
# conservative and not optimized. This is fine as Google Test
# compiles fast and for ordinary users its source rarely changes.
gtest-all.o : $(GTEST_SRCS_)
$(CXX) $(CPPFLAGS) -I$(GTEST_DIR) $(CXXFLAGS) -c \
$(GTEST_DIR)/src/gtest-all.cc
gtest_main.o : $(GTEST_SRCS_)
$(CXX) $(CPPFLAGS) -I$(GTEST_DIR) $(CXXFLAGS) -c \
$(GTEST_DIR)/src/gtest_main.cc
gtest.a : gtest-all.o
$(AR) $(ARFLAGS) $@ $^
gtest_main.a : gtest-all.o gtest_main.o
$(AR) $(ARFLAGS) $@ $^
# Builds a sample test. A test should link with gtest.a, and also
# gtest_main.a if it doesn't define its own main() function.
GildedRose.o : $(USER_DIR)/GildedRose.cc
$(CXX) -c $^
GildedRoseUnitTests.o : $(USER_DIR)/GildedRoseUnitTests.cc \
$(GTEST_HEADERS)
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/GildedRoseUnitTests.cc
GildedRose : GildedRoseUnitTests.o GildedRose.o gtest.a gtest_main.a
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -pthread $^ -o $@
GildedRoseTextTests.o : $(USER_DIR)/GildedRoseTextTests.cc
$(CXX) -c $^
GildedRoseTextTests : GildedRoseTextTests.o GildedRose.o
$(CXX) -pthread $^ -o $@

View File

@ -1,38 +1,68 @@
TL;DR;
-------
run-once.sh runs your tests once
# C++ version of Gilded Rose refactoring kata
Before this will work you will need:
- make and a C++ compiler (like gcc) is installed on your system and is in the PATH
- The GTest framework in the directory gtest.
- If your IDE does the compilation and linking, you should remove the first 3 lines
in the run-once.sh file.
## Introduction
The C++ version of the Gilded Rose refactoring kata is available in four variants using different test frameworks:
More Verbose Instructions
-------------------------
* Catch2 test framework
1. Traditional unit test with the [Catch2](https://github.com/catchorg/Catch2) test framework in the `test/cpp_catch2_unittest` folder.
2. [Approval tests](https://github.com/approvals/ApprovalTests.cpp) with the [Catch2](https://github.com/catchorg/Catch2) test framework in the `test/cpp_catch2_approvaltest` folder.
* Google Test framework
1. Traditional unit test with the [Googletest](https://github.com/google/googletest) test framework in the `test/cpp_googletest_unittest` folder.
2. [Approval tests](https://github.com/approvals/ApprovalTests.cpp) with the [Googletest](https://github.com/google/googletest) test framework in the `test/cpp_googletest_approvaltest` folder.
Create a clone of both GildedRose-Refactoring-Kata and googletest in a directory we'll call ${ROOT_INSTALL_DIR}:
The `GildedRose.cc` file, i.e. the code under test, is identical in all four variants.
cd ${ROOT_INSTALL_DIR}
git clone https://github.com/emilybache/GildedRose-Refactoring-Kata
git clone https://github.com/google/googletest
## Prerequisites
Make googletest by running make in subfolder googletest/googletest/make:
* CMake version >= 3.13
* C++ compiler that support C++17
cd googletest/googletest/make
make
## How to build and run tests in a terminal
Create a softlink in the GildedRose-Refactoring-Kata clone pointing at the googletest code:
### Build tests
cd ${ROOT_INSTALL_DIR}/GildedRose-Refactoring-Kata/cpp
ln -s ${ROOT_INSTALL_DIR}/googletest/googletest gtest
$ cd ${GIT_FOLDER}/GildedRose-Refactoring-Kata/cpp
$ mkdir build
$ cd build
$ cmake ..
$ cmake --build .
Make the GildedRose-Refactoring-Kata:
### Show available tests
make
$ cd ${GIT_FOLDER}/GildedRose-Refactoring-Kata/cpp/build
$ ctest -N
Test project ${GIT_FOLDER}/GildedRose-Refactoring-Kata/cpp/build
Test #1: GildedRoseCatch2ApprovalTests
Test #2: GildedRoseCatch2UnitTests
Test #3: GildedRoseGoogletestApprovalTests
Test #4: GildedRoseGoogletestUnitTests
Then you should be able to run the tests:
### Run all tests
./run_once.sh
$ ctest
If you have been successful, then you should see a failing test, "GildedRoseTest.Foo".
### Run all tests with verbose output
$ ctest -VV
### Run a specific test with verbose output
$ ctest -VV --tests-regex Catch2Approval
## How to build and run tests using the [CLion IDE](https://www.jetbrains.com/clion/)
1. Start CLion
2. Select menu `File - Open...`
3. Select folder `${GIT_FOLDER}/GildedRose-Refactoring-Kata/cpp`
4. Select menu `Build - Build Project`
4. Select menu `Run - Run...`
4. Select what test variant to run, e.g. `GildedRoseCatch2ApprovalTests`.
## How to build and run tests using Visual Studio 2019
1. Start Visual Studio 2019
2. Select `Open a local folder`
3. Select folder `${GIT_FOLDER}/GildedRose-Refactoring-Kata/cpp`
4. Wait for message `CMake generation finished.` in the CMake output window at the bottom
5. Select what test variant to run in the drop down menu for Startup Items, e.g. `GildedRoseCatch2ApprovalTests.exe`.
6. Select menu `Debug - Start`

View File

@ -0,0 +1,4 @@
#ifndef APPROVAL_TEST_1_APPROVALTESTS_HPP
#define APPROVAL_TEST_1_APPROVALTESTS_HPP
#include "ApprovalTests.v.6.0.0.hpp"
#endif

File diff suppressed because it is too large Load Diff

3
cpp/lib/CMakeLists.txt Normal file
View File

@ -0,0 +1,3 @@
set(LIB_NAME lib)
add_library(${LIB_NAME} INTERFACE)
target_include_directories(${LIB_NAME} INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})

17597
cpp/lib/Catch.hpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +0,0 @@
#!/bin/bash
if [[ ! -d build ]]; then
mkdir -p build
fi
cd build
cmake .. -DCMAKE_BUILD_TYPE=DEBUG && cmake --build . && ctest --output-on-failure -R GildedRoseApprovalTests

View File

@ -1,8 +0,0 @@
#!/bin/bash
if [[ ! -d build ]]; then
mkdir -p build
fi
cd build
cmake .. -DCMAKE_BUILD_TYPE=DEBUG && cmake --build . && ctest --output-on-failure

View File

@ -1,4 +0,0 @@
rm GildedRose
rm GildedRose.o
make
./GildedRose

4
cpp/src/CMakeLists.txt Normal file
View File

@ -0,0 +1,4 @@
set(SRC_LIB_NAME src)
add_library(${SRC_LIB_NAME})
target_sources(${SRC_LIB_NAME} PRIVATE GildedRose.cc)
target_include_directories(${SRC_LIB_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

4
cpp/test/CMakeLists.txt Normal file
View File

@ -0,0 +1,4 @@
add_subdirectory(cpp_catch2_approvaltest)
add_subdirectory(cpp_catch2_unittest)
add_subdirectory(cpp_googletest_approvaltest)
add_subdirectory(cpp_googletest_unittest)

View File

@ -0,0 +1,15 @@
set(TEST_NAME GildedRoseCatch2ApprovalTests)
add_executable(${TEST_NAME})
target_sources(${TEST_NAME} PRIVATE GildedRoseCatch2ApprovalTests.cc)
target_link_libraries(${TEST_NAME} lib src)
set_property(TARGET ${TEST_NAME} PROPERTY CXX_STANDARD 17)
add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME})
# Set compiler option /FC for Visual Studio to to make the __FILE__ macro expand to full path.
# The __FILE__ macro is used by Catch2 to get the path to current test file.
# Links:
# * https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=vs-2019
# * https://docs.microsoft.com/en-us/cpp/build/reference/fc-full-path-of-source-code-file-in-diagnostics?view=vs-2019
if (MSVC)
target_compile_options(${TEST_NAME} PRIVATE "/FC")
endif()

View File

@ -0,0 +1,29 @@
#define APPROVALS_CATCH
#include "ApprovalTests.hpp"
#include "GildedRose.h"
std::ostream& operator<<(std::ostream& os, const Item& obj)
{
return os
<< "name: " << obj.name
<< ", sellIn: " << obj.sellIn
<< ", quality: " << obj.quality;
}
TEST_CASE("GildedRoseApprovalTests", "VerifyCombinations")
{
std::vector<string> names { "Foo" };
std::vector<int> sellIns { 1 };
std::vector<int> qualities { 1 };
auto f = [](string name, int sellIn, int quality) {
vector<Item> items = {Item(name, sellIn, quality)};
GildedRose app(items);
app.updateQuality();
return items[0];
};
ApprovalTests::CombinationApprovals::verifyAllCombinations(
f,
names, sellIns, qualities);
}

View File

@ -0,0 +1,15 @@
set(TEST_NAME GildedRoseCatch2UnitTests)
add_executable(${TEST_NAME})
target_sources(${TEST_NAME} PRIVATE GildedRoseCatch2UnitTests.cc)
target_link_libraries(${TEST_NAME} lib src)
set_property(TARGET ${TEST_NAME} PROPERTY CXX_STANDARD 17)
add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME})
# Set compiler option /FC for Visual Studio to to make the __FILE__ macro expand to full path.
# The __FILE__ macro is used by Catch2 to get the path to current test file.
# Links:
# * https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=vs-2019
# * https://docs.microsoft.com/en-us/cpp/build/reference/fc-full-path-of-source-code-file-in-diagnostics?view=vs-2019
if (MSVC)
target_compile_options(${TEST_NAME} PRIVATE "/FC")
endif()

View File

@ -0,0 +1,12 @@
#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file
#include "Catch.hpp"
#include "GildedRose.h"
TEST_CASE("GildedRoseUnitTest", "Foo")
{
vector<Item> items;
items.push_back(Item("Foo", 0, 0));
GildedRose app(items);
app.updateQuality();
REQUIRE("fixme" == app.items[0].name);
}

View File

@ -0,0 +1,15 @@
set(TEST_NAME GildedRoseGoogletestApprovalTests)
add_executable(${TEST_NAME})
target_sources(${TEST_NAME} PRIVATE googletest_approval_main.cc GildedRoseGoogletestApprovalTests.cc)
target_link_libraries(${TEST_NAME} lib src gtest)
set_property(TARGET ${TEST_NAME} PROPERTY CXX_STANDARD 17)
add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME})
# Set compiler option /FC for Visual Studio to to make the __FILE__ macro expand to full path.
# The __FILE__ macro can be used to get the path to current test file.
# Links:
# * https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=vs-2019
# * https://docs.microsoft.com/en-us/cpp/build/reference/fc-full-path-of-source-code-file-in-diagnostics?view=vs-2019
if (MSVC)
target_compile_options(${TEST_NAME} PRIVATE "/FC")
endif()

View File

@ -0,0 +1,2 @@
(Foo, 1, 1) => name: Foo, sellIn: 0, quality: 0

View File

@ -0,0 +1,33 @@
// Include header files for test frameworks
#include <gtest/gtest.h>
#include <ApprovalTests.hpp>
// Include code to be tested
#include "GildedRose.h"
std::ostream& operator<<(std::ostream& os, const Item& obj)
{
return os
<< "name: " << obj.name
<< ", sellIn: " << obj.sellIn
<< ", quality: " << obj.quality;
}
TEST(GildedRoseApprovalTests, VerifyCombinations) {
std::vector<string> names { "Foo" };
std::vector<int> sellIns { 1 };
std::vector<int> qualities { 1 };
auto f = [](string name, int sellIn, int quality) {
vector<Item> items = {Item(name, sellIn, quality)};
GildedRose app(items);
app.updateQuality();
return items[0];
};
ApprovalTests::CombinationApprovals::verifyAllCombinations(
f,
names, sellIns, qualities);
}

View File

@ -0,0 +1,2 @@
#define APPROVALS_GOOGLETEST // This tells Approval Tests to provide a main() - only do this in one cpp file
#include "ApprovalTests.hpp"

View File

@ -0,0 +1,15 @@
set(TEST_NAME GildedRoseGoogletestUnitTests)
add_executable(${TEST_NAME})
target_sources(${TEST_NAME} PRIVATE GildedRoseGoogletestUnitTests.cc)
target_link_libraries(${TEST_NAME} src gtest gtest_main)
set_property(TARGET ${TEST_NAME} PROPERTY CXX_STANDARD 17)
add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME})
# Set compiler option /FC for Visual Studio to to make the __FILE__ macro expand to full path.
# The __FILE__ macro can be used to get the path to current test file.
# Links:
# * https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=vs-2019
# * https://docs.microsoft.com/en-us/cpp/build/reference/fc-full-path-of-source-code-file-in-diagnostics?view=vs-2019
if (MSVC)
target_compile_options(${TEST_NAME} PRIVATE "/FC")
endif()

View File

@ -0,0 +1,10 @@
#include <gtest/gtest.h>
#include "GildedRose.h"
TEST(GildedRoseTest, Foo) {
vector<Item> items;
items.push_back(Item("Foo", 0, 0));
GildedRose app(items);
app.updateQuality();
EXPECT_EQ("fixme", app.items[0].name);
}