HW2 Publish

This commit is contained in:
2025-10-08 00:01:57 +08:00
commit fd471a68c4
19 changed files with 2357 additions and 0 deletions

6
.clang-format Normal file
View File

@@ -0,0 +1,6 @@
BasedOnStyle: LLVM
IndentWidth: 4
AccessModifierOffset: -4
AllowShortFunctionsOnASingleLine: Inline
AlwaysBreakTemplateDeclarations: Yes
PackConstructorInitializers: Never

492
.gitignore vendored Normal file
View File

@@ -0,0 +1,492 @@
# .gitignore file for CLion, Visual Studio, and Visual Studio Code
# From JetBrains .gitignore template
# https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# AWS User-specific
.idea/**/aws.xml
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# SonarLint plugin
.idea/sonarlint/
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
# From Visual Studio .gitignore template
# https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.tlog
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio 6 auto-generated project file (contains which files were open etc.)
*.vbp
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
*.dsw
*.dsp
# Visual Studio 6 technical files
*.ncb
*.aps
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# Visual Studio History (VSHistory) files
.vshistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
# VS Code files for those working on multiple tools
# .vscode/*
# !.vscode/settings.json
# !.vscode/tasks.json
# !.vscode/launch.json
# !.vscode/extensions.json
# *.code-workspace
# Local History for Visual Studio Code
.history/
# Windows Installer files from build outputs
*.cab
*.msi
*.msix
*.msm
*.msp
# JetBrains
*.sln.iml
/.idea/
# Custom writen VSCode .gitignore
.vscode/
# Default build directory for CMake extension
build/
*_build/

155
CMakeLists.txt Normal file
View File

@@ -0,0 +1,155 @@
# ! ------------------------------------------------------------------ !
# ! Changing this file incorrectly can cause your program to not build !
# ! If you don't know CMake well DO NOT touch this file !
# ! ------------------------------------------------------------------ !
cmake_minimum_required(VERSION 3.20)
project(OOP VERSION 0.1.0)
enable_testing()
set(CMAKE_CXX_STANDARD 17)
#### GTest
option(FETCH_GOOGLETEST "Download googletest source" ON)
if(FETCH_GOOGLETEST)
# reference: https://google.github.io/googletest/quickstart-cmake.html
include(FetchContent)
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG release-1.12.1
)
# For Windows: Prevent overriding the parent project's compiler/linker settings
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)
else()
message("Using custom googletest installation")
# reference: https://cmake.org/cmake/help/latest/module/FindGTest.html
find_package(GTest REQUIRED)
endif()
include(files.cmake)
set(SRC_DIR ${CMAKE_SOURCE_DIR}/src)
set(INCLUDE_DIR ${CMAKE_SOURCE_DIR}/include)
set(TEST_DIR ${CMAKE_SOURCE_DIR}/test)
list(TRANSFORM SRC_FILES PREPEND ${SRC_DIR}/)
list(TRANSFORM INCLUDE_FILES PREPEND ${INCLUDE_DIR}/)
list(TRANSFORM TEST_FILES PREPEND ${TEST_DIR}/)
find_program(MEMORYCHECK_COMMAND valgrind)
set(MEMORYCHECK_COMMAND_OPTIONS
"--error-exitcode=1 --track-origins=yes --leak-check=full"
)
include(CTest)
option(COVERAGE "Enable coverage" OFF)
if(COVERAGE)
set(CMAKE_C_FLAGS "--coverage -g -O0")
set(CMAKE_CXX_FLAGS "--coverage -g -O0")
include(scripts/CodeCoverage.cmake)
set(CODE_COVERAGE_VERBOSE ON)
set(COVERAGE_EXCLUDE
${CMAKE_BINARY_DIR}/_deps/*
${PROJECT_SOURCE_DIR}/test/*
)
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
set(GCOVR_ADDITIONAL_ARGS
--gcov-executable "llvm-cov gcov"
)
endif()
append_coverage_compiler_flags()
setup_target_for_coverage_gcovr_html(
NAME ut_all_coverage
DEPENDENCIES ut_all
EXCLUDE ${COVERAGE_EXCLUDE}
)
endif()
if (MSVC)
add_compile_options(
/W4
)
else()
add_compile_options(
-Wall -Wextra -Wpedantic
-Werror=return-type
-Werror=uninitialized
-Wfatal-errors
)
endif()
add_executable(ut_all
${SRC_FILES}
${INCLUDE_FILES}
${TEST_FILES}
)
target_link_libraries(ut_all
GTest::gtest_main
)
target_include_directories(ut_all PRIVATE
${INCLUDE_DIR}
)
target_compile_options(ut_all PRIVATE
${TARGET_COMPILER_OPTIONS}
)
add_executable(ut_snack
include/Snack.hpp
src/Snack.cpp
test/ut_snack.cpp
)
target_link_libraries(ut_snack
GTest::gtest_main
)
target_include_directories(ut_snack PRIVATE
${INCLUDE_DIR}
)
target_compile_options(ut_snack PRIVATE
${TARGET_COMPILER_OPTIONS}
)
add_executable(ut_storage
include/Snack.hpp
include/Storage.hpp
src/Snack.cpp
src/Storage.cpp
test/ut_storage.cpp
)
target_link_libraries(ut_storage
GTest::gtest_main
)
target_include_directories(ut_storage PRIVATE
${INCLUDE_DIR}
)
target_compile_options(ut_storage PRIVATE
${TARGET_COMPILER_OPTIONS}
)
add_executable(ut_storage_manager
include/Snack.hpp
include/Storage.hpp
include/StorageManager.hpp
src/Snack.cpp
src/Storage.cpp
src/StorageManager.cpp
test/ut_storage_manager.cpp
)
target_link_libraries(ut_storage_manager
GTest::gtest_main
)
target_include_directories(ut_storage_manager PRIVATE
${INCLUDE_DIR}
)
target_compile_options(ut_storage_manager PRIVATE
${TARGET_COMPILER_OPTIONS}
)
include(GoogleTest)
gtest_discover_tests(ut_all)

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 NOOBDY
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

209
README.md Normal file
View File

@@ -0,0 +1,209 @@
# Homework 02
> 這份作業由物件導向程式設計助教群所製。
> 如有問題,歡迎使用以下方式聯繫助教:
> Email: t111590004@ntut.org.tw、t112820018@ntut.org.tw
> MS Teams: 張意昌、許景喬
> Discord 群組
⚠️ Due: 2025/10/20 23:59 ⚠️
## 說明
### 目標
- [ ] 了解並學會使用C++編寫程式。
- [ ] 學習使用Class。
- [ ] 學習使用Vector。
- [ ] 學習使用sort。
- [ ] 學習使用enum Class。
- [ ] 學會並撰寫Class中的method。
### 檔案架構
請確認提交是否符合以下的檔案架構,否則無法進行評分。
```txt
.
│ .clang-format
│ .gitignore
│ CMakeLists.txt
│ files.cmake
│ LICENSE
│ README.md
├───include
│ Snack.hpp
│ Storage.hpp
│ StorageManager.hpp
├───scripts
│ CodeCoverage.cmake
│ coverage.sh
├───src
│ Snack.cpp
│ Storage.cpp
│ StorageManager.cpp
└───test
ut_snack.cpp
ut_storage.cpp
ut_storage_manager.cpp
```
## 題目評分
- 20%]完成 `Snack`。 (ut_snack.cpp)
- 60%]完成 `Storage`。 (ut_storage.cpp)
- 20%]完成 `StorageManager`。 (ut_storage_manager.cpp)
作業總分為 `100` ,如果你完成 (ut_all) 的話。
## 注意事項
- 你不應該上傳 `/bin` 資料夾至專案上。
- 你的功課不應該出現 `Memory Leak`,否則將會扣作業總分 10 分。
- 你不應該上傳 `/bin` 資料夾至專案庫,編譯結果不應該上傳至專案庫上,若在助教確認功課評分時 `/bin` 資料夾存在在專案庫中,扣除作業總分 5 分。
## 敘述
> 嗨,歡迎參加 OOP 課程。
>
> 想必你已經完成了 Lecture 03 的課程,並瞭解這門課所使用的語言 C++ 的相關語法。
> (若還不了解的話,可以查看 Lecture 03 的簡報)
>
> 在這個任務中,你會了解課堂上所教學的語法將如何使用。
請嘗試完成任務,並在 `OJ` 上拿到綠色的 `Correct` 勾勾。
## 題目敘述
> 在這個任務中,你將描述 Uriah 的糧食庫 `Storage`。
Uriah 住在中山區的一間小雅房,身為一個平日不是在朝九晚六實習,不然就是在努力精煉自己能力的資工大四生,每天凌晨夜深人靜的小確幸,不外乎就是打開糧食庫並拿點東西吃,接著繼續忙。
Uriah 的糧食庫可能有許多點心例如泡麵、餅乾、御飯糰、冰7林、*飛天義大利麵*、*新生高架橋*、*大直河濱公園的河水*、*長春松江路口的公車站*、*捷運行天宮站*等等,這些多樣性的糧食能夠有助於 Uriah 在吃完這些點心後充足了一些精力並繼續忙手邊的事情。
在這份作業中你需要完成一個管理糧食庫的程式。
### 任務零、了解題目架構,製作 `Snack` 的建構子
-`include/Snack.hpp``include/Storage.hpp``include/StorageManager.hpp` 中,是我們這次要撰寫的 `class``method`
- 你必須要在 `src/Snack.cpp``src/Storage.hpp``src/StorageManager.hpp` 完成所有的功能 。
首先請觀察 `include/Snack.hpp` `class Snack` 變數長這樣:
| 變數名稱 | 定義 |
|--------------------|------|
| `std::string name` | 點心名稱 |
| `int amount` | 點心數量 |
- 打開 `src/Snack.cpp` 並在這裡完成你剩下的實作。
- 對於 `class Snack` 的建構子,請根據傳入的點心的名稱、點心數量 (沒有輸入預設為 1 ) 。
- ※備註:在 `include/Snack.hpp` 中看到的建構子不會有 "Snack::"
| 建構子 | 定義 |
|--------------------------------------------------------|---------------------|
| `Snack::Snack(std::string snackName)` | 初始設定點心名稱,點心數量設為 1 。 |
| `Snack::Snack(std::string snackName, int snackAmount)` | 初始設定點心名稱與點心數量。 |
### 任務一、完成 `Snack` 剩下的 method
| method | 定義 |
|------------------------------------------|---------------------------------------------|
| `std::string Snack::getName() const` | 回傳 `Snack` 中的變數 name 。 |
| `int Snack::getAmount() const` | 回傳 `Snack` 中的變數 amount 。 |
| `void Snack::setAmount(int snackAmount)` | 設置 `Snack` 中的變數 amount 為傳入的變數 snackAmount 。 |
### 任務二、觀察 `Storage` 製作 `Storage` 的建構子
| 儲存庫類型 | 變數名稱 |
|-----------|------------------------|
| 放糖果的儲存庫 | `StorageType::CANDY` |
| 放餅乾的儲存庫 | `StorageType::COOKIES` |
| 放蛋糕的儲存庫 | `StorageType::CAKE` |
| 放其他東西的儲存庫 | `StorageType::OTHER` |
首先請觀察 `include/Storage.hpp` `class Storage` 變數長這樣:
| 變數名稱 | 定義 |
|-----------------------------|--------------------|
| `std::vector<Snack> snacks` | 儲存點心的變數 |
| `StorageType type` | 糧食庫類型 |
| `int amount` | 糧食庫點心數量(不是點心的種類數量) |
| `int capacity` | 糧食庫容量 |
- 打開 `src/Storage.cpp` 並在這裡完成你剩下的實作。
- 對於 `class Storage` 的建構子,請根據傳入的糧食庫類型、糧食庫的容量 (沒有輸入預設為 10 ) ,初始預設糧食庫點心數量為 0 。
- ※備註:在 `include/Storage.hpp` 中看到的建構子不會有 "Storage::"
| 建構子 | 定義 |
|------------------------------------------------------------------|------------------------------------|
| `Storage::Storage(StorageType storageType)` | 初始設定糧食庫類型與糧食庫容量設為 10 ,糧食庫點心數量為 0 。 |
| `Storage::Storage(StorageType storageType, int storageCapacity)` | 初始設定糧食庫類型與糧食庫容量,糧食庫點心數量為 0 。 |
### 任務三、完成 `Storage` 剩下的 method
- 提示:在這個任務中會需使用 `vector` 中的 method。
| method | 定義 |
|-------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `void Storage::add(std::string snackName)` | 將一個名稱為 `snackName` 的點心加入糧食庫時,需依以下規則進行:<br/>1. 檢查是否已放滿:若糧食庫已達容量上限,則拋出 `std::invalid_argument` 例外。<br/>2. 檢查是否已存在相同點心:<br/>若該點心已存在於糧食庫中,則僅更新其數量。<br/>若該點心尚未存在,則將其新增至 `vector snacks` 中。<br/>注意:在 `vector snacks` 中,不得同時存在兩個同名的 `Snack` 物件。 |
| `void Storage::add(std::string snackName, int snackAmount)` | 將 `snackAmount` 個名稱為 `snackName` 的點心加入糧食庫時,需依以下規則進行:<br/>1. 檢查是否已放滿:若糧食庫已達容量上限,則拋出 `std::invalid_argument` 例外。<br/>2. 檢查是否已存在相同點心:<br/>若該點心已存在於糧食庫中,則僅更新其數量。<br/>若該點心尚未存在,則將其新增至 `vector snacks` 中。<br/>注意:在 `vector snacks` 中,不得同時存在兩個同名的 `Snack` 物件。 |
| `void Storage::eat(std::string snackName, int snackAmount)` | 將 `snackAmount` 個名稱為 `snackName` 的點心從糧食庫拿出來吃時,需依以下規則進行:<br/>1. 檢查點心是否存在於糧食庫:若不在糧食庫,則拋出 `std::invalid_argument` 例外。<br/>2. 檢查該點心是否夠吃:<br/>若點心夠吃,則更新其數量。若剛好吃完,則把該點心從 `vector` 中移除。<br/>若點心不夠吃,則拋出 `std::invalid_argument` 例外。 |
| `void Storage::eatFirst(int snackAmount)` | 將 `vector snacks` 中最前面的點心取出 `snackAmount` 個來吃時,需依以下規則進行:<br/>1. 檢查糧食庫中是否有點心可以吃:若沒有點心,則拋出 `std::invalid_argument` 例外。<br/>2. 檢查該點心是否夠吃:<br/>若點心夠吃,則更新其數量。若剛好吃完,則把該點心從 `vector` 中移除。<br/>若點心不夠吃,則拋出 `std::invalid_argument` 例外。 |
| `void Storage::eatLast(int snackAmount)` | 將 `vector snacks` 中最後面的點心取出 `snackAmount` 個來吃時,需依以下規則進行:<br/>1. 檢查糧食庫中是否有點心可以吃:若沒有點心,則拋出 `std::invalid_argument` 例外。<br/>2. 檢查該點心是否夠吃:<br/>若點心夠吃,則更新其數量。若剛好吃完,則把該點心從 `vector` 中移除。<br/>若點心不夠吃,則拋出 `std::invalid_argument` 例外。 |
| `void Storage::sortByAmount()` | 將 `vector snacks` 根據每種點心的**數量**由小排到大進行排序<br/>提示:利用 `std::sort` 以及自定義的 `compare function` 或是 `lambda function` 來完成。 |
| `void Storage::sortBySnackName()` | 將 `vector snacks` 根據每種點心的**名稱**安照字典序進行排序<br/>提示:利用 `std::sort` 以及自定義的 `compare function` 或是 `lambda function` 來完成。 |
| `void Storage::clear()` | 將 `vector snacks` 清空。<br/> |
| `int Storage::getAmount() const` | 回傳 `Storage` 中的變數 amount 。 |
| `int Storage::getCapacity() const` | 回傳 `Storage` 中的變數 capacity 。 |
| `StorageType Storage::getType() const` | 回傳 `Storage` 中的變數 type 。 |
| `const std::vector<Snack>& Storage::getSnacks() const` | 回傳 `Storage` 中的變數 snacks 。 |
| `void Storage::setCapacity(int storageCapacity)` | 設置 `Storage` 中的變數 capacity 為傳入的變數 storageCapacity 。 |
### 任務四、觀察 `StorageManager` 製作 `StorageManager` 的建構子
首先請觀察 `include/StorageManager.hpp` `class StorageManager` 變數長這樣:
| 變數名稱 | 定義 |
|----------------------------------|----------|
| `std::vector<Storage> storages;` | 儲存糧食庫的變數 |
- 打開 `src/StorageManager.cpp` 並在這裡完成你剩下的實作。
- 對於 `class StorageManager` 的建構子,請建構所有 `StorageType` 的糧食庫並照著enum中的順序放到 `vector storages` 中。
- ※備註:在 `include/StorageManager.hpp` 中看到的建構子不會有 "StorageManager::"
| 建構子 | 定義 |
|------------------------------------|-----------------------------------------------------------|
| `StorageManager::StorageManager()` | 建構所有 `StorageType` 的糧食庫並照著enum中的順序放到 `vector storages` 中 |
### 任務五、完成 `StorageManager` 剩下的 method
| method | 定義 |
|-------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `void StorageManager::AddAllStorageCapacity(int capacity)` | 將所有儲存庫的容量都加 `capacity` 。 |
| `void StorageManager::moveCapacity(StorageType fromType, StorageType toType, int moveCapacity)` | 將 `moveCapacity``FromType` 糧食庫的容量移動到 `ToType` 糧食庫,需依以下規則進行:<br/>1. 檢查 `FromType` 糧食庫的容量是否有足夠的空位轉移<br/>若有足夠的空位,則更新兩個儲存庫的容量<br/>若沒有足夠的空位,則拋出 `std::invalid_argument` 。 |
| `std::vector<Storage>& StorageManager::getStorages()` | 回傳 `StorageManager` 中的變數 storages 。 |
`&` 出現在 `std::vector<Storage>&`,代表的是 參考 (reference)
- 沒有 &:回傳一個新的 std::vector<Storage> 物件(拷貝一份出來)。
-&:回傳的是原本的 std::vector<Storage> 的參考(不會額外複製)。
### 任務附註
- 你只需要實作任務所描述的 `class` 中的 `method`
- 在後面的任務使用前面任務完成的 `method` ,可以加快你的實作速度。
- 你可以先參考 `docs` 資料夾裡的東西。
- 關於 `vector` ,你可以參考 [這篇](http://en.cppreference.com/w/cpp/container/vector) 。
- 使用 `vector` 時你應該使用 `#include<vector>`
- 關於 `invalid_argument()` ,你可以參考 [這篇](https://en.cppreference.com/w/cpp/error/invalid_argument) 。
- `invalid_argument()` 內不用寫特定東西,且你應該使用 `#include<stdexcept>`
- 關於 `sort()` ,你可以參考 [這篇](https://en.cppreference.com/w/cpp/algorithm/sort) 。
- 使用 `sort()` 時你應該使用 `#include<algorithm>`
## Homework's meme
![HW meme](https://hackmd.io/_uploads/BkOWrvHJT.png)

95
docs/sort.cpp Normal file
View File

@@ -0,0 +1,95 @@
#include <algorithm>
#include <iostream>
#include <vector>
// 比較函式:整數由大到小排序
bool cmpInt(int a, int b) {
return a > b;
}
// 定義學生結構
struct student {
int number; // 學號
int score; // 成績
};
// 學號升冪排序
bool cmpStucdentnumberUp(student a, student b) {
return a.number < b.number;
}
// 學號降冪排序
bool cmpStucdentnumberDown(student a, student b) {
return a.number > b.number;
}
// 成績升冪排序
bool cmpStucdentScoreUp(student a, student b) {
return a.score < b.score;
}
// 成績降冪排序
bool cmpStucdentScoreDown(student a, student b) {
return a.score > b.score;
}
int main() {
// ----------- int 排序範例 -----------
// 建立一個整數 vector
std::vector<int> nums = {10, 14, 6, 7, 8, 15, 1, 9, 12, 11, 3, 4, 5, 13, 2};
// 升冪排序 (小到大) - 預設比較器
std::sort(nums.begin(), nums.end());
for (auto num : nums) {
std::cout << num << ' ';
}
std::cout << std::endl;
// 降冪排序 (大到小) - 使用自定義比較函式 cmpInt
std::sort(nums.begin(), nums.end(), cmpInt);
for (auto num : nums) {
std::cout << num << ' ';
}
std::cout << std::endl;
// ----------- struct student 排序範例 -----------
// 建立一個學生 vector
std::vector<student> students;
students.push_back(student{2, 60});
students.push_back(student{1, 100});
students.push_back(student{4, 70});
students.push_back(student{5, 80});
students.push_back(student{3, 90});
// 用 number (學號) 升冪排序
std::sort(students.begin(), students.end(), cmpStucdentnumberUp);
for (auto student : students) {
std::cout << '(' << student.number << ',' << student.score << ") ";
}
std::cout << std::endl;
// 用 number (學號) 降冪排序
std::sort(students.begin(), students.end(), cmpStucdentnumberDown);
for (auto student : students) {
std::cout << '(' << student.number << ',' << student.score << ") ";
}
std::cout << std::endl;
// 用 score (成績) 升冪排序
std::sort(students.begin(), students.end(), cmpStucdentScoreUp);
for (auto student : students) {
std::cout << '(' << student.number << ',' << student.score << ") ";
}
std::cout << std::endl;
// 用 score (成績) 降冪排序
std::sort(students.begin(), students.end(), cmpStucdentScoreDown);
for (auto student : students) {
std::cout << '(' << student.number << ',' << student.score << ") ";
}
std::cout << std::endl;
return 0;
}

147
docs/vector.cpp Normal file
View File

@@ -0,0 +1,147 @@
#include <iostream>
#include <vector>
struct student {
int number; // 學號
int score; // 成績
};
int main(){
std::vector<student> students;
students.push_back(student{1, 100});
students.push_back(student{2, 90});
students.push_back(student{3, 80});
students.push_back(student{4, 70});
students.push_back(student{5, 60});
// ----------- 掃過 vector 中每一個值 範例 -----------
// 會改變原本的值
std::cout << "method 1:" << std::endl;
std::vector<student> s1 = students; //從 students 複製一個一模一樣的vector
for (int i = 0; i < s1.size(); i++) {
s1[i].number = 1;
std::cout << '(' << s1[i].number << ',' << s1[i].score << ") ";
}
std::cout << std::endl;
std::cout << "original vector:" << std::endl;
for (auto s: s1) { // auto也可以寫成 student
std::cout << '(' << s.number << ',' << s.score << ") ";
}
std::cout << std::endl << std::endl;
// 不會改變原本的值
std::cout << "method 2:" << std::endl;
std::vector<student> s2 = students;
for (auto s: s2) { // auto也可以寫成 student
s.number = 2;
std::cout << '(' << s.number << ',' << s.score << ") ";
}
std::cout << std::endl;
std::cout << "original vector:" << std::endl;
for (auto s: s2) { // auto也可以寫成 student
std::cout << '(' << s.number << ',' << s.score << ") ";
}
std::cout << std::endl << std::endl;
// 會改變原本的值
std::cout << "method 3:" << std::endl;
std::vector<student> s3 = students;
for (auto& s: s3) { // auto也可以寫成 student
s.number = 3;
std::cout << '(' << s.number << ',' << s.score << ") ";
}
std::cout << std::endl;
std::cout << "original vector:" << std::endl;
for (auto& s: s3) { // auto也可以寫成 student
std::cout << '(' << s.number << ',' << s.score << ") ";
}
std::cout << std::endl << std::endl;
// 結論:
// 只要在資料型態後面加上 & (reference) 就可以改變原本的資料
// auto 可以自動偵測資料型態
// ----------- 取得 vector 最前面的值 範例 -----------
std::cout << "number: " << students.front().number << std::endl;
std::cout << "score: " << students.front().score << std::endl;
// ----------- 取得 vector 最後面的值 範例 -----------
std::cout << "number: " << students.back().number << std::endl;
std::cout << "score: " << students.back().score << std::endl;
// ----------- 取得 vector 最前面的指標 範例 -----------
std::cout << "number: " << (*students.begin()).number << std::endl;
std::cout << "score: " << (*students.begin()).score << std::endl;
// ----------- 取得 vector 最後面的指標 範例 -----------
std::cout << "number: " << (*(students.end()-1)).number << std::endl;
std::cout << "score: " << (*(students.end()-1)).score << std::endl;
std::cout << std::endl;
// ----------- 刪除 vector 最前面的值 範例 -----------
std::vector<student> s4 = students;
std::cout << "original vector:" << std::endl;
for (auto& s: s4) { // auto也可以寫成 student
std::cout << '(' << s.number << ',' << s.score << ") ";
}
std::cout << std::endl;
s4.erase(s4.begin());
std::cout << "after erase vector:" << std::endl;
for (auto& s: s4) { // auto也可以寫成 student
std::cout << '(' << s.number << ',' << s.score << ") ";
}
std::cout << std::endl << std::endl;
// ----------- 刪除 vector 第i個的值 範例 -----------
std::vector<student> s5 = students;
std::cout << "original vector:" << std::endl;
for (auto& s: s5) { // auto也可以寫成 student
std::cout << '(' << s.number << ',' << s.score << ") ";
}
std::cout << std::endl << std::endl;
s5.erase(s5.begin() + 1);
std::cout << "after erase vector:" << std::endl;
for (auto& s: s5) { // auto也可以寫成 student
std::cout << '(' << s.number << ',' << s.score << ") ";
}
std::cout << std::endl << std::endl;
// ----------- 刪除 vector 最後面的值 範例 -----------
std::vector<student> s6 = students;
std::cout << "original vector:" << std::endl;
for (auto& s: s6) { // auto也可以寫成 student
std::cout << '(' << s.number << ',' << s.score << ") ";
}
std::cout << std::endl << std::endl;
s6.erase(s6.end());
std::cout << "after erase vector:" << std::endl;
for (auto& s: s6) { // auto也可以寫成 student
std::cout << '(' << s.number << ',' << s.score << ") ";
}
std::cout << std::endl << std::endl;
// ----------- 清空 vector 範例 -----------
std::vector<student> s7 = students;
std::cout << "original vector:" << std::endl;
for (auto& s: s7) { // auto也可以寫成 student
std::cout << '(' << s.number << ',' << s.score << ") ";
}
std::cout << std::endl << std::endl;
s7.clear();
std::cout << "after clear vector:" << std::endl;
for (auto& s: s7) { // auto也可以寫成 student
std::cout << '(' << s.number << ',' << s.score << ") ";
}
std::cout << std::endl << std::endl;
return 0;
}

17
files.cmake Normal file
View File

@@ -0,0 +1,17 @@
set(SRC_FILES
Snack.cpp
Storage.cpp
StorageManager.cpp
)
set(INCLUDE_FILES
Snack.hpp
Storage.hpp
StorageManager.hpp
)
set(TEST_FILES
ut_snack.cpp
ut_storage.cpp
ut_storage_manager.cpp
)

20
include/Snack.hpp Normal file
View File

@@ -0,0 +1,20 @@
#ifndef SNACK_HPP
#define SNACK_HPP
#include <string>
class Snack {
public:
Snack(std::string snackName);
Snack(std::string snackName, int snackAmount);
[[nodiscard]] std::string getName() const;
[[nodiscard]] int getAmount() const;
void setAmount(int snackAmount);
private:
std::string name;
int amount;
};
#endif

42
include/Storage.hpp Normal file
View File

@@ -0,0 +1,42 @@
#ifndef STORAGE_HPP
#define STORAGE_HPP
#include "Snack.hpp"
#include <string>
#include <vector>
enum class StorageType {
CANDY,
COOKIES,
CAKE,
OTHER
};
class Storage {
public:
Storage(StorageType storageType);
Storage(StorageType storageType, int storageCapacity);
void add(std::string snackName);
void add(std::string snackName, int snackAmount);
void eat(std::string snackName, int snackAmount);
void eatFirst(int snackAmount);
void eatLast(int snackAmount);
void sortByAmount();
void sortBySnackName();
void clear();
[[nodiscard]] int getAmount() const;
[[nodiscard]] int getCapacity() const;
[[nodiscard]] StorageType getType() const;
[[nodiscard]] const std::vector<Snack> &getSnacks() const;
void setCapacity(int storageCapacity);
private:
std::vector<Snack> snacks;
StorageType type;
int amount;
int capacity;
};
#endif

View File

@@ -0,0 +1,19 @@
#ifndef STORAGEMANAGER_HPP
#define STORAGEMANAGER_HPP
#include "Storage.hpp"
class StorageManager {
public:
StorageManager();
void AddAllStorageCapacity(int capacity);
void moveCapacity(StorageType fromType, StorageType toType, int moveCapacity);
[[nodiscard]] std::vector<Storage>& getStorages();
private:
std::vector<Storage> storages;
};
#endif

742
scripts/CodeCoverage.cmake Normal file
View File

@@ -0,0 +1,742 @@
# Copyright (c) 2012 - 2017, Lars Bilke
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its contributors
# may be used to endorse or promote products derived from this software without
# specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# CHANGES:
#
# 2012-01-31, Lars Bilke
# - Enable Code Coverage
#
# 2013-09-17, Joakim Söderberg
# - Added support for Clang.
# - Some additional usage instructions.
#
# 2016-02-03, Lars Bilke
# - Refactored functions to use named parameters
#
# 2017-06-02, Lars Bilke
# - Merged with modified version from github.com/ufz/ogs
#
# 2019-05-06, Anatolii Kurotych
# - Remove unnecessary --coverage flag
#
# 2019-12-13, FeRD (Frank Dana)
# - Deprecate COVERAGE_LCOVR_EXCLUDES and COVERAGE_GCOVR_EXCLUDES lists in favor
# of tool-agnostic COVERAGE_EXCLUDES variable, or EXCLUDE setup arguments.
# - CMake 3.4+: All excludes can be specified relative to BASE_DIRECTORY
# - All setup functions: accept BASE_DIRECTORY, EXCLUDE list
# - Set lcov basedir with -b argument
# - Add automatic --demangle-cpp in lcovr, if 'c++filt' is available (can be
# overridden with NO_DEMANGLE option in setup_target_for_coverage_lcovr().)
# - Delete output dir, .info file on 'make clean'
# - Remove Python detection, since version mismatches will break gcovr
# - Minor cleanup (lowercase function names, update examples...)
#
# 2019-12-19, FeRD (Frank Dana)
# - Rename Lcov outputs, make filtered file canonical, fix cleanup for targets
#
# 2020-01-19, Bob Apthorpe
# - Added gfortran support
#
# 2020-02-17, FeRD (Frank Dana)
# - Make all add_custom_target()s VERBATIM to auto-escape wildcard characters
# in EXCLUDEs, and remove manual escaping from gcovr targets
#
# 2021-01-19, Robin Mueller
# - Add CODE_COVERAGE_VERBOSE option which will allow to print out commands which are run
# - Added the option for users to set the GCOVR_ADDITIONAL_ARGS variable to supply additional
# flags to the gcovr command
#
# 2020-05-04, Mihchael Davis
# - Add -fprofile-abs-path to make gcno files contain absolute paths
# - Fix BASE_DIRECTORY not working when defined
# - Change BYPRODUCT from folder to index.html to stop ninja from complaining about double defines
#
# 2021-05-10, Martin Stump
# - Check if the generator is multi-config before warning about non-Debug builds
#
# 2022-02-22, Marko Wehle
# - Change gcovr output from -o <filename> for --xml <filename> and --html <filename> output respectively.
# This will allow for Multiple Output Formats at the same time by making use of GCOVR_ADDITIONAL_ARGS, e.g. GCOVR_ADDITIONAL_ARGS "--txt".
#
# 2022-09-28, Sebastian Mueller
# - fix append_coverage_compiler_flags_to_target to correctly add flags
# - replace "-fprofile-arcs -ftest-coverage" with "--coverage" (equivalent)
#
# USAGE:
#
# 1. Copy this file into your cmake modules path.
#
# 2. Add the following line to your CMakeLists.txt (best inside an if-condition
# using a CMake option() to enable it just optionally):
# include(CodeCoverage)
#
# 3. Append necessary compiler flags for all supported source files:
# append_coverage_compiler_flags()
# Or for specific target:
# append_coverage_compiler_flags_to_target(YOUR_TARGET_NAME)
#
# 3.a (OPTIONAL) Set appropriate optimization flags, e.g. -O0, -O1 or -Og
#
# 4. If you need to exclude additional directories from the report, specify them
# using full paths in the COVERAGE_EXCLUDES variable before calling
# setup_target_for_coverage_*().
# Example:
# set(COVERAGE_EXCLUDES
# '${PROJECT_SOURCE_DIR}/src/dir1/*'
# '/path/to/my/src/dir2/*')
# Or, use the EXCLUDE argument to setup_target_for_coverage_*().
# Example:
# setup_target_for_coverage_lcov(
# NAME coverage
# EXECUTABLE testrunner
# EXCLUDE "${PROJECT_SOURCE_DIR}/src/dir1/*" "/path/to/my/src/dir2/*")
#
# 4.a NOTE: With CMake 3.4+, COVERAGE_EXCLUDES or EXCLUDE can also be set
# relative to the BASE_DIRECTORY (default: PROJECT_SOURCE_DIR)
# Example:
# set(COVERAGE_EXCLUDES "dir1/*")
# setup_target_for_coverage_gcovr_html(
# NAME coverage
# EXECUTABLE testrunner
# BASE_DIRECTORY "${PROJECT_SOURCE_DIR}/src"
# EXCLUDE "dir2/*")
#
# 5. Use the functions described below to create a custom make target which
# runs your test executable and produces a code coverage report.
#
# 6. Build a Debug build:
# cmake -DCMAKE_BUILD_TYPE=Debug ..
# make
# make my_coverage_target
#
include(CMakeParseArguments)
option(CODE_COVERAGE_VERBOSE "Verbose information" FALSE)
# Check prereqs
find_program( GCOV_PATH gcov )
find_program( LCOV_PATH NAMES lcov lcov.bat lcov.exe lcov.perl)
find_program( FASTCOV_PATH NAMES fastcov fastcov.py )
find_program( GENHTML_PATH NAMES genhtml genhtml.perl genhtml.bat )
find_program( GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/scripts/test)
find_program( CPPFILT_PATH NAMES c++filt )
if(NOT GCOV_PATH)
message(FATAL_ERROR "gcov not found! Aborting...")
endif() # NOT GCOV_PATH
# Check supported compiler (Clang, GNU and Flang)
get_property(LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES)
foreach(LANG ${LANGUAGES})
if("${CMAKE_${LANG}_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang")
if("${CMAKE_${LANG}_COMPILER_VERSION}" VERSION_LESS 3)
message(FATAL_ERROR "Clang version must be 3.0.0 or greater! Aborting...")
endif()
elseif(NOT "${CMAKE_${LANG}_COMPILER_ID}" MATCHES "GNU"
AND NOT "${CMAKE_${LANG}_COMPILER_ID}" MATCHES "(LLVM)?[Ff]lang")
message(FATAL_ERROR "Compiler is not GNU or Flang! Aborting...")
endif()
endforeach()
set(COVERAGE_COMPILER_FLAGS "-g --coverage"
CACHE INTERNAL "")
if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)")
include(CheckCXXCompilerFlag)
check_cxx_compiler_flag(-fprofile-abs-path HAVE_fprofile_abs_path)
if(HAVE_fprofile_abs_path)
set(COVERAGE_COMPILER_FLAGS "${COVERAGE_COMPILER_FLAGS} -fprofile-abs-path")
endif()
endif()
set(CMAKE_Fortran_FLAGS_COVERAGE
${COVERAGE_COMPILER_FLAGS}
CACHE STRING "Flags used by the Fortran compiler during coverage builds."
FORCE )
set(CMAKE_CXX_FLAGS_COVERAGE
${COVERAGE_COMPILER_FLAGS}
CACHE STRING "Flags used by the C++ compiler during coverage builds."
FORCE )
set(CMAKE_C_FLAGS_COVERAGE
${COVERAGE_COMPILER_FLAGS}
CACHE STRING "Flags used by the C compiler during coverage builds."
FORCE )
set(CMAKE_EXE_LINKER_FLAGS_COVERAGE
""
CACHE STRING "Flags used for linking binaries during coverage builds."
FORCE )
set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE
""
CACHE STRING "Flags used by the shared libraries linker during coverage builds."
FORCE )
mark_as_advanced(
CMAKE_Fortran_FLAGS_COVERAGE
CMAKE_CXX_FLAGS_COVERAGE
CMAKE_C_FLAGS_COVERAGE
CMAKE_EXE_LINKER_FLAGS_COVERAGE
CMAKE_SHARED_LINKER_FLAGS_COVERAGE )
get_property(GENERATOR_IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if(NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR GENERATOR_IS_MULTI_CONFIG))
message(WARNING "Code coverage results with an optimised (non-Debug) build may be misleading")
endif() # NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR GENERATOR_IS_MULTI_CONFIG)
if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_Fortran_COMPILER_ID STREQUAL "GNU")
link_libraries(gcov)
endif()
# Defines a target for running and collection code coverage information
# Builds dependencies, runs the given executable and outputs reports.
# NOTE! The executable should always have a ZERO as exit code otherwise
# the coverage generation will not complete.
#
# setup_target_for_coverage_lcov(
# NAME testrunner_coverage # New target name
# EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
# DEPENDENCIES testrunner # Dependencies to build first
# BASE_DIRECTORY "../" # Base directory for report
# # (defaults to PROJECT_SOURCE_DIR)
# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative
# # to BASE_DIRECTORY, with CMake 3.4+)
# NO_DEMANGLE # Don't demangle C++ symbols
# # even if c++filt is found
# )
function(setup_target_for_coverage_lcov)
set(options NO_DEMANGLE SONARQUBE)
set(oneValueArgs BASE_DIRECTORY NAME)
set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES LCOV_ARGS GENHTML_ARGS)
cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if(NOT LCOV_PATH)
message(FATAL_ERROR "lcov not found! Aborting...")
endif() # NOT LCOV_PATH
if(NOT GENHTML_PATH)
message(FATAL_ERROR "genhtml not found! Aborting...")
endif() # NOT GENHTML_PATH
# Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR
if(DEFINED Coverage_BASE_DIRECTORY)
get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE)
else()
set(BASEDIR ${PROJECT_SOURCE_DIR})
endif()
# Collect excludes (CMake 3.4+: Also compute absolute paths)
set(LCOV_EXCLUDES "")
foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_LCOV_EXCLUDES})
if(CMAKE_VERSION VERSION_GREATER 3.4)
get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR})
endif()
list(APPEND LCOV_EXCLUDES "${EXCLUDE}")
endforeach()
list(REMOVE_DUPLICATES LCOV_EXCLUDES)
# Conditional arguments
if(CPPFILT_PATH AND NOT ${Coverage_NO_DEMANGLE})
set(GENHTML_EXTRA_ARGS "--demangle-cpp")
endif()
# Setting up commands which will be run to generate coverage data.
# Cleanup lcov
set(LCOV_CLEAN_CMD
${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -directory .
-b ${BASEDIR} --zerocounters
)
# Create baseline to make sure untouched files show up in the report
set(LCOV_BASELINE_CMD
${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -c -i -d . -b
${BASEDIR} -o ${Coverage_NAME}.base
)
# Run tests
set(LCOV_EXEC_TESTS_CMD
${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS}
)
# Capturing lcov counters and generating report
set(LCOV_CAPTURE_CMD
${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --directory . -b
${BASEDIR} --capture --output-file ${Coverage_NAME}.capture
)
# add baseline counters
set(LCOV_BASELINE_COUNT_CMD
${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -a ${Coverage_NAME}.base
-a ${Coverage_NAME}.capture --output-file ${Coverage_NAME}.total
)
# filter collected data to final coverage report
set(LCOV_FILTER_CMD
${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --remove
${Coverage_NAME}.total ${LCOV_EXCLUDES} --output-file ${Coverage_NAME}.info
)
# Generate HTML output
set(LCOV_GEN_HTML_CMD
${GENHTML_PATH} ${GENHTML_EXTRA_ARGS} ${Coverage_GENHTML_ARGS} -o
${Coverage_NAME} ${Coverage_NAME}.info
)
if(${Coverage_SONARQUBE})
# Generate SonarQube output
set(GCOVR_XML_CMD
${GCOVR_PATH} --sonarqube ${Coverage_NAME}_sonarqube.xml -r ${BASEDIR} ${GCOVR_ADDITIONAL_ARGS}
${GCOVR_EXCLUDE_ARGS} --object-directory=${PROJECT_BINARY_DIR}
)
set(GCOVR_XML_CMD_COMMAND
COMMAND ${GCOVR_XML_CMD}
)
set(GCOVR_XML_CMD_BYPRODUCTS ${Coverage_NAME}_sonarqube.xml)
set(GCOVR_XML_CMD_COMMENT COMMENT "SonarQube code coverage info report saved in ${Coverage_NAME}_sonarqube.xml.")
endif()
if(CODE_COVERAGE_VERBOSE)
message(STATUS "Executed command report")
message(STATUS "Command to clean up lcov: ")
string(REPLACE ";" " " LCOV_CLEAN_CMD_SPACED "${LCOV_CLEAN_CMD}")
message(STATUS "${LCOV_CLEAN_CMD_SPACED}")
message(STATUS "Command to create baseline: ")
string(REPLACE ";" " " LCOV_BASELINE_CMD_SPACED "${LCOV_BASELINE_CMD}")
message(STATUS "${LCOV_BASELINE_CMD_SPACED}")
message(STATUS "Command to run the tests: ")
string(REPLACE ";" " " LCOV_EXEC_TESTS_CMD_SPACED "${LCOV_EXEC_TESTS_CMD}")
message(STATUS "${LCOV_EXEC_TESTS_CMD_SPACED}")
message(STATUS "Command to capture counters and generate report: ")
string(REPLACE ";" " " LCOV_CAPTURE_CMD_SPACED "${LCOV_CAPTURE_CMD}")
message(STATUS "${LCOV_CAPTURE_CMD_SPACED}")
message(STATUS "Command to add baseline counters: ")
string(REPLACE ";" " " LCOV_BASELINE_COUNT_CMD_SPACED "${LCOV_BASELINE_COUNT_CMD}")
message(STATUS "${LCOV_BASELINE_COUNT_CMD_SPACED}")
message(STATUS "Command to filter collected data: ")
string(REPLACE ";" " " LCOV_FILTER_CMD_SPACED "${LCOV_FILTER_CMD}")
message(STATUS "${LCOV_FILTER_CMD_SPACED}")
message(STATUS "Command to generate lcov HTML output: ")
string(REPLACE ";" " " LCOV_GEN_HTML_CMD_SPACED "${LCOV_GEN_HTML_CMD}")
message(STATUS "${LCOV_GEN_HTML_CMD_SPACED}")
if(${Coverage_SONARQUBE})
message(STATUS "Command to generate SonarQube XML output: ")
string(REPLACE ";" " " GCOVR_XML_CMD_SPACED "${GCOVR_XML_CMD}")
message(STATUS "${GCOVR_XML_CMD_SPACED}")
endif()
endif()
# Setup target
add_custom_target(${Coverage_NAME}
COMMAND ${LCOV_CLEAN_CMD}
COMMAND ${LCOV_BASELINE_CMD}
COMMAND ${LCOV_EXEC_TESTS_CMD}
COMMAND ${LCOV_CAPTURE_CMD}
COMMAND ${LCOV_BASELINE_COUNT_CMD}
COMMAND ${LCOV_FILTER_CMD}
COMMAND ${LCOV_GEN_HTML_CMD}
${GCOVR_XML_CMD_COMMAND}
# Set output files as GENERATED (will be removed on 'make clean')
BYPRODUCTS
${Coverage_NAME}.base
${Coverage_NAME}.capture
${Coverage_NAME}.total
${Coverage_NAME}.info
${GCOVR_XML_CMD_BYPRODUCTS}
${Coverage_NAME}/index.html
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
DEPENDS ${Coverage_DEPENDENCIES}
VERBATIM # Protect arguments to commands
COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report."
)
# Show where to find the lcov info report
add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
COMMAND ;
COMMENT "Lcov code coverage info report saved in ${Coverage_NAME}.info."
${GCOVR_XML_CMD_COMMENT}
)
# Show info where to find the report
add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
COMMAND ;
COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report."
)
endfunction() # setup_target_for_coverage_lcov
# Defines a target for running and collection code coverage information
# Builds dependencies, runs the given executable and outputs reports.
# NOTE! The executable should always have a ZERO as exit code otherwise
# the coverage generation will not complete.
#
# setup_target_for_coverage_gcovr_xml(
# NAME ctest_coverage # New target name
# EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
# DEPENDENCIES executable_target # Dependencies to build first
# BASE_DIRECTORY "../" # Base directory for report
# # (defaults to PROJECT_SOURCE_DIR)
# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative
# # to BASE_DIRECTORY, with CMake 3.4+)
# )
# The user can set the variable GCOVR_ADDITIONAL_ARGS to supply additional flags to the
# GCVOR command.
function(setup_target_for_coverage_gcovr_xml)
set(options NONE)
set(oneValueArgs BASE_DIRECTORY NAME)
set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES)
cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if(NOT GCOVR_PATH)
message(FATAL_ERROR "gcovr not found! Aborting...")
endif() # NOT GCOVR_PATH
# Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR
if(DEFINED Coverage_BASE_DIRECTORY)
get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE)
else()
set(BASEDIR ${PROJECT_SOURCE_DIR})
endif()
# Collect excludes (CMake 3.4+: Also compute absolute paths)
set(GCOVR_EXCLUDES "")
foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_GCOVR_EXCLUDES})
if(CMAKE_VERSION VERSION_GREATER 3.4)
get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR})
endif()
list(APPEND GCOVR_EXCLUDES "${EXCLUDE}")
endforeach()
list(REMOVE_DUPLICATES GCOVR_EXCLUDES)
# Combine excludes to several -e arguments
set(GCOVR_EXCLUDE_ARGS "")
foreach(EXCLUDE ${GCOVR_EXCLUDES})
list(APPEND GCOVR_EXCLUDE_ARGS "-e")
list(APPEND GCOVR_EXCLUDE_ARGS "${EXCLUDE}")
endforeach()
# Set up commands which will be run to generate coverage data
# Run tests
set(GCOVR_XML_EXEC_TESTS_CMD
${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS}
)
# Running gcovr
set(GCOVR_XML_CMD
${GCOVR_PATH} --xml ${Coverage_NAME}.xml -r ${BASEDIR} ${GCOVR_ADDITIONAL_ARGS}
${GCOVR_EXCLUDE_ARGS} --object-directory=${PROJECT_BINARY_DIR}
)
if(CODE_COVERAGE_VERBOSE)
message(STATUS "Executed command report")
message(STATUS "Command to run tests: ")
string(REPLACE ";" " " GCOVR_XML_EXEC_TESTS_CMD_SPACED "${GCOVR_XML_EXEC_TESTS_CMD}")
message(STATUS "${GCOVR_XML_EXEC_TESTS_CMD_SPACED}")
message(STATUS "Command to generate gcovr XML coverage data: ")
string(REPLACE ";" " " GCOVR_XML_CMD_SPACED "${GCOVR_XML_CMD}")
message(STATUS "${GCOVR_XML_CMD_SPACED}")
endif()
add_custom_target(${Coverage_NAME}
COMMAND ${GCOVR_XML_EXEC_TESTS_CMD}
COMMAND ${GCOVR_XML_CMD}
BYPRODUCTS ${Coverage_NAME}.xml
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
DEPENDS ${Coverage_DEPENDENCIES}
VERBATIM # Protect arguments to commands
COMMENT "Running gcovr to produce Cobertura code coverage report."
)
# Show info where to find the report
add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
COMMAND ;
COMMENT "Cobertura code coverage report saved in ${Coverage_NAME}.xml."
)
endfunction() # setup_target_for_coverage_gcovr_xml
# Defines a target for running and collection code coverage information
# Builds dependencies, runs the given executable and outputs reports.
# NOTE! The executable should always have a ZERO as exit code otherwise
# the coverage generation will not complete.
#
# setup_target_for_coverage_gcovr_html(
# NAME ctest_coverage # New target name
# EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
# DEPENDENCIES executable_target # Dependencies to build first
# BASE_DIRECTORY "../" # Base directory for report
# # (defaults to PROJECT_SOURCE_DIR)
# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative
# # to BASE_DIRECTORY, with CMake 3.4+)
# )
# The user can set the variable GCOVR_ADDITIONAL_ARGS to supply additional flags to the
# GCVOR command.
function(setup_target_for_coverage_gcovr_html)
set(options NONE)
set(oneValueArgs BASE_DIRECTORY NAME)
set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES)
cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if(NOT GCOVR_PATH)
message(FATAL_ERROR "gcovr not found! Aborting...")
endif() # NOT GCOVR_PATH
# Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR
if(DEFINED Coverage_BASE_DIRECTORY)
get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE)
else()
set(BASEDIR ${PROJECT_SOURCE_DIR})
endif()
# Collect excludes (CMake 3.4+: Also compute absolute paths)
set(GCOVR_EXCLUDES "")
foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_GCOVR_EXCLUDES})
if(CMAKE_VERSION VERSION_GREATER 3.4)
get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR})
endif()
list(APPEND GCOVR_EXCLUDES "${EXCLUDE}")
endforeach()
list(REMOVE_DUPLICATES GCOVR_EXCLUDES)
# Combine excludes to several -e arguments
set(GCOVR_EXCLUDE_ARGS "")
foreach(EXCLUDE ${GCOVR_EXCLUDES})
list(APPEND GCOVR_EXCLUDE_ARGS "-e")
list(APPEND GCOVR_EXCLUDE_ARGS "${EXCLUDE}")
endforeach()
# Set up commands which will be run to generate coverage data
# Run tests
set(GCOVR_HTML_EXEC_TESTS_CMD
${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS}
)
# Create folder
set(GCOVR_HTML_FOLDER_CMD
${CMAKE_COMMAND} -E make_directory ${PROJECT_BINARY_DIR}/${Coverage_NAME}
)
# Running gcovr
set(GCOVR_HTML_CMD
${GCOVR_PATH} --html ${Coverage_NAME}/index.html --html-details -r ${BASEDIR} ${GCOVR_ADDITIONAL_ARGS}
${GCOVR_EXCLUDE_ARGS} --object-directory=${PROJECT_BINARY_DIR}
)
if(CODE_COVERAGE_VERBOSE)
message(STATUS "Executed command report")
message(STATUS "Command to run tests: ")
string(REPLACE ";" " " GCOVR_HTML_EXEC_TESTS_CMD_SPACED "${GCOVR_HTML_EXEC_TESTS_CMD}")
message(STATUS "${GCOVR_HTML_EXEC_TESTS_CMD_SPACED}")
message(STATUS "Command to create a folder: ")
string(REPLACE ";" " " GCOVR_HTML_FOLDER_CMD_SPACED "${GCOVR_HTML_FOLDER_CMD}")
message(STATUS "${GCOVR_HTML_FOLDER_CMD_SPACED}")
message(STATUS "Command to generate gcovr HTML coverage data: ")
string(REPLACE ";" " " GCOVR_HTML_CMD_SPACED "${GCOVR_HTML_CMD}")
message(STATUS "${GCOVR_HTML_CMD_SPACED}")
endif()
add_custom_target(${Coverage_NAME}
COMMAND ${GCOVR_HTML_EXEC_TESTS_CMD}
COMMAND ${GCOVR_HTML_FOLDER_CMD}
COMMAND ${GCOVR_HTML_CMD}
BYPRODUCTS ${PROJECT_BINARY_DIR}/${Coverage_NAME}/index.html # report directory
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
DEPENDS ${Coverage_DEPENDENCIES}
VERBATIM # Protect arguments to commands
COMMENT "Running gcovr to produce HTML code coverage report."
)
# Show info where to find the report
add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
COMMAND ;
COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report."
)
endfunction() # setup_target_for_coverage_gcovr_html
# Defines a target for running and collection code coverage information
# Builds dependencies, runs the given executable and outputs reports.
# NOTE! The executable should always have a ZERO as exit code otherwise
# the coverage generation will not complete.
#
# setup_target_for_coverage_fastcov(
# NAME testrunner_coverage # New target name
# EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
# DEPENDENCIES testrunner # Dependencies to build first
# BASE_DIRECTORY "../" # Base directory for report
# # (defaults to PROJECT_SOURCE_DIR)
# EXCLUDE "src/dir1/" "src/dir2/" # Patterns to exclude.
# NO_DEMANGLE # Don't demangle C++ symbols
# # even if c++filt is found
# SKIP_HTML # Don't create html report
# POST_CMD perl -i -pe s!${PROJECT_SOURCE_DIR}/!!g ctest_coverage.json # E.g. for stripping source dir from file paths
# )
function(setup_target_for_coverage_fastcov)
set(options NO_DEMANGLE SKIP_HTML)
set(oneValueArgs BASE_DIRECTORY NAME)
set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES FASTCOV_ARGS GENHTML_ARGS POST_CMD)
cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if(NOT FASTCOV_PATH)
message(FATAL_ERROR "fastcov not found! Aborting...")
endif()
if(NOT Coverage_SKIP_HTML AND NOT GENHTML_PATH)
message(FATAL_ERROR "genhtml not found! Aborting...")
endif()
# Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR
if(Coverage_BASE_DIRECTORY)
get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE)
else()
set(BASEDIR ${PROJECT_SOURCE_DIR})
endif()
# Collect excludes (Patterns, not paths, for fastcov)
set(FASTCOV_EXCLUDES "")
foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_FASTCOV_EXCLUDES})
list(APPEND FASTCOV_EXCLUDES "${EXCLUDE}")
endforeach()
list(REMOVE_DUPLICATES FASTCOV_EXCLUDES)
# Conditional arguments
if(CPPFILT_PATH AND NOT ${Coverage_NO_DEMANGLE})
set(GENHTML_EXTRA_ARGS "--demangle-cpp")
endif()
# Set up commands which will be run to generate coverage data
set(FASTCOV_EXEC_TESTS_CMD ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS})
set(FASTCOV_CAPTURE_CMD ${FASTCOV_PATH} ${Coverage_FASTCOV_ARGS} --gcov ${GCOV_PATH}
--search-directory ${BASEDIR}
--process-gcno
--output ${Coverage_NAME}.json
--exclude ${FASTCOV_EXCLUDES}
)
set(FASTCOV_CONVERT_CMD ${FASTCOV_PATH}
-C ${Coverage_NAME}.json --lcov --output ${Coverage_NAME}.info
)
if(Coverage_SKIP_HTML)
set(FASTCOV_HTML_CMD ";")
else()
set(FASTCOV_HTML_CMD ${GENHTML_PATH} ${GENHTML_EXTRA_ARGS} ${Coverage_GENHTML_ARGS}
-o ${Coverage_NAME} ${Coverage_NAME}.info
)
endif()
set(FASTCOV_POST_CMD ";")
if(Coverage_POST_CMD)
set(FASTCOV_POST_CMD ${Coverage_POST_CMD})
endif()
if(CODE_COVERAGE_VERBOSE)
message(STATUS "Code coverage commands for target ${Coverage_NAME} (fastcov):")
message(" Running tests:")
string(REPLACE ";" " " FASTCOV_EXEC_TESTS_CMD_SPACED "${FASTCOV_EXEC_TESTS_CMD}")
message(" ${FASTCOV_EXEC_TESTS_CMD_SPACED}")
message(" Capturing fastcov counters and generating report:")
string(REPLACE ";" " " FASTCOV_CAPTURE_CMD_SPACED "${FASTCOV_CAPTURE_CMD}")
message(" ${FASTCOV_CAPTURE_CMD_SPACED}")
message(" Converting fastcov .json to lcov .info:")
string(REPLACE ";" " " FASTCOV_CONVERT_CMD_SPACED "${FASTCOV_CONVERT_CMD}")
message(" ${FASTCOV_CONVERT_CMD_SPACED}")
if(NOT Coverage_SKIP_HTML)
message(" Generating HTML report: ")
string(REPLACE ";" " " FASTCOV_HTML_CMD_SPACED "${FASTCOV_HTML_CMD}")
message(" ${FASTCOV_HTML_CMD_SPACED}")
endif()
if(Coverage_POST_CMD)
message(" Running post command: ")
string(REPLACE ";" " " FASTCOV_POST_CMD_SPACED "${FASTCOV_POST_CMD}")
message(" ${FASTCOV_POST_CMD_SPACED}")
endif()
endif()
# Setup target
add_custom_target(${Coverage_NAME}
# Cleanup fastcov
COMMAND ${FASTCOV_PATH} ${Coverage_FASTCOV_ARGS} --gcov ${GCOV_PATH}
--search-directory ${BASEDIR}
--zerocounters
COMMAND ${FASTCOV_EXEC_TESTS_CMD}
COMMAND ${FASTCOV_CAPTURE_CMD}
COMMAND ${FASTCOV_CONVERT_CMD}
COMMAND ${FASTCOV_HTML_CMD}
COMMAND ${FASTCOV_POST_CMD}
# Set output files as GENERATED (will be removed on 'make clean')
BYPRODUCTS
${Coverage_NAME}.info
${Coverage_NAME}.json
${Coverage_NAME}/index.html # report directory
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
DEPENDS ${Coverage_DEPENDENCIES}
VERBATIM # Protect arguments to commands
COMMENT "Resetting code coverage counters to zero. Processing code coverage counters and generating report."
)
set(INFO_MSG "fastcov code coverage info report saved in ${Coverage_NAME}.info and ${Coverage_NAME}.json.")
if(NOT Coverage_SKIP_HTML)
string(APPEND INFO_MSG " Open ${PROJECT_BINARY_DIR}/${Coverage_NAME}/index.html in your browser to view the coverage report.")
endif()
# Show where to find the fastcov info report
add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E echo ${INFO_MSG}
)
endfunction() # setup_target_for_coverage_fastcov
function(append_coverage_compiler_flags)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
message(STATUS "Appending code coverage compiler flags: ${COVERAGE_COMPILER_FLAGS}")
endfunction() # append_coverage_compiler_flags
# Setup coverage for specific library
function(append_coverage_compiler_flags_to_target name)
separate_arguments(_flag_list NATIVE_COMMAND "${COVERAGE_COMPILER_FLAGS}")
target_compile_options(${name} PRIVATE ${_flag_list})
if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_Fortran_COMPILER_ID STREQUAL "GNU")
target_link_libraries(${name} PRIVATE gcov)
endif()
endfunction()

23
scripts/coverage.sh Normal file
View File

@@ -0,0 +1,23 @@
#!/bin/bash
# This script is intended to be used in automated testing
# Specifically, in a Linux environment with certain packages installed
# If you want to use this script, please copy this file and make changes
# However, we DO NOT guarantee this script would work on every machine
: ${CC:=gcc}
: ${CXX:=g++}
BUILD_DIR=coverage_build
cmake \
-B $BUILD_DIR \
-DCOVERAGE=ON \
-DCMAKE_C_COMPILER=$CC \
-DCMAKE_CXX_COMPILER=$CXX \
cmake --build $BUILD_DIR
"./${BUILD_DIR}/ut_all"
cmake --build $BUILD_DIR --target ut_all_coverage

11
src/Snack.cpp Normal file
View File

@@ -0,0 +1,11 @@
#include "Snack.hpp"
Snack::Snack(std::string snackName){}
Snack::Snack(std::string snackName, int snackAmount){}
std::string Snack::getName() const{}
int Snack::getAmount() const{}
void Snack::setAmount(int snackAmount){}

42
src/Storage.cpp Normal file
View File

@@ -0,0 +1,42 @@
#include "Storage.hpp"
Storage::Storage(StorageType storageType){}
Storage::Storage(StorageType storageType, int storageCapacity){}
void Storage::add(std::string snackName){}
void Storage::add(std::string snackName, int snackAmount){}
void Storage::eat(std::string snackName, int snackAmount){}
void Storage::eatFirst(int snackAmount){}
void Storage::eatLast(int snackAmount){}
void Storage::sortByAmount(){}
void Storage::sortBySnackName(){}
void Storage::clear(){}
int Storage::getAmount() const{
return amount;
}
int Storage::getCapacity() const{
return capacity;
}
StorageType Storage::getType() const{
return type;
}
const std::vector<Snack>& Storage::getSnacks() const {
return snacks;
}
void Storage::setCapacity(int storageCapacity) {
capacity = storageCapacity;
}

10
src/StorageManager.cpp Normal file
View File

@@ -0,0 +1,10 @@
#include "StorageManager.hpp"
StorageManager::StorageManager(){}
void StorageManager::AddAllStorageCapacity(int capacity){}
void StorageManager::moveCapacity(StorageType fromType, StorageType toType, int moveCapacity){}
std::vector<Storage>& StorageManager::getStorages() {
return storages;
}

34
test/ut_snack.cpp Normal file
View File

@@ -0,0 +1,34 @@
#include <gtest/gtest.h>
#include "Snack.hpp"
TEST(SnackTest, test_construct_with_name) {
Snack snack = Snack("Chips");
ASSERT_EQ("Chips", snack.getName());
ASSERT_EQ(1, snack.getAmount());
}
TEST(SnackTest, test_construct_with_name_and_amount) {
Snack snack = Snack("Chocolate", 5);
ASSERT_EQ("Chocolate", snack.getName());
ASSERT_EQ(5, snack.getAmount());
}
TEST(SnackTest, test_set_amount_works) {
Snack snack = Snack("Cookie");
snack.setAmount(10);
ASSERT_EQ(10, snack.getAmount());
}
TEST(SnackTest, test_different_objects_are_independent) {
Snack snack1 = Snack("Juice", 2);
Snack snack2 = Snack("Candy", 3);
snack1.setAmount(8);
ASSERT_EQ("Juice", snack1.getName());
ASSERT_EQ(8, snack1.getAmount());
ASSERT_EQ("Candy", snack2.getName());
ASSERT_EQ(3, snack2.getAmount());
}

179
test/ut_storage.cpp Normal file
View File

@@ -0,0 +1,179 @@
#include "gtest/gtest.h"
#include "Storage.hpp"
TEST(StorageTest, test_construct_with_type) {
Storage storage = Storage(StorageType::CAKE);
ASSERT_EQ(StorageType::CAKE, storage.getType());
ASSERT_EQ(0, storage.getAmount());
ASSERT_EQ(10, storage.getCapacity());
}
TEST(StorageTest, test_construct_with_type_and_capacity) {
Storage storage = Storage(StorageType::CAKE, 15);
ASSERT_EQ(StorageType::CAKE, storage.getType());
ASSERT_EQ(0, storage.getAmount());
ASSERT_EQ(15, storage.getCapacity());
}
TEST(StorageTest, test_add_snack_with_name) {
Storage storage = Storage(StorageType::CANDY, 5);
storage.add("KitKat");
ASSERT_EQ(1, storage.getSnacks().size());
ASSERT_EQ(1, storage.getAmount());
ASSERT_EQ(1, storage.getSnacks()[0].getAmount());
ASSERT_EQ("KitKat", storage.getSnacks()[0].getName());
}
TEST(StorageTest, test_add_snack_with_name_twice) {
Storage storage = Storage(StorageType::CANDY, 5);
storage.add("KitKat");
storage.add("KitKat");
ASSERT_EQ(1, storage.getSnacks().size());
ASSERT_EQ(2, storage.getAmount());
ASSERT_EQ(2, storage.getSnacks()[0].getAmount());
ASSERT_EQ("KitKat", storage.getSnacks()[0].getName());
}
TEST(StorageTest, test_add_snack_overflow_with_name) {
Storage storage = Storage(StorageType::CAKE, 2);
storage.add("CheeseCake", 2);
ASSERT_THROW(storage.add("Brownie"), std::invalid_argument);
}
TEST(StorageTest, test_add_snack_with_name_and_amount) {
Storage storage = Storage(StorageType::CANDY, 5);
storage.add("Oreo", 2);
ASSERT_EQ(1, storage.getSnacks().size());
ASSERT_EQ(2, storage.getAmount());
ASSERT_EQ(2, storage.getSnacks()[0].getAmount());
ASSERT_EQ("Oreo", storage.getSnacks()[0].getName());
}
TEST(StorageTest, test_add_snack_with_name_and_amount_twice) {
Storage storage = Storage(StorageType::CANDY, 5);
storage.add("Oreo", 2);
storage.add("Oreo", 2);
ASSERT_EQ(1, storage.getSnacks().size());
ASSERT_EQ(4, storage.getAmount());
ASSERT_EQ(4, storage.getSnacks()[0].getAmount());
ASSERT_EQ("Oreo", storage.getSnacks()[0].getName());
}
TEST(StorageTest, test_add_snack_overflow_with_name_and_amount) {
Storage storage = Storage(StorageType::CAKE, 2);
ASSERT_THROW(storage.add("Brownie", 3), std::invalid_argument);
}
TEST(StorageTest, test_eat_snack_with_name_and_amount) {
Storage storage = Storage(StorageType::COOKIES, 5);
storage.add("Oreo", 3);
storage.eat("Oreo", 2);
ASSERT_EQ(1, storage.getSnacks()[0].getAmount());
}
TEST(StorageTest, test_eat_all_snack_with_name_and_amount) {
Storage storage = Storage(StorageType::COOKIES, 5);
storage.add("Oreo", 3);
storage.eat("Oreo", 3);
ASSERT_TRUE(storage.getSnacks().empty());
}
TEST(StorageTest, test_eat_snack_overflow_with_name_and_amount) {
Storage storage = Storage(StorageType::COOKIES, 5);
storage.add("Oreo", 3);
ASSERT_THROW(storage.eat("Oreo", 6), std::invalid_argument);
}
TEST(StorageTest, test_eat_snack_not_exist_with_name_and_amount) {
Storage storage = Storage(StorageType::COOKIES, 5);
ASSERT_THROW(storage.eat("Oreo", 6), std::invalid_argument);
}
TEST(StorageTest, test_eat_first_with_amount) {
Storage storage = Storage(StorageType::OTHER, 10);
storage.add("Mars", 3);
storage.add("Twix", 4);
storage.eatFirst(2);
ASSERT_EQ(1, storage.getSnacks().front().getAmount());
ASSERT_EQ(4, storage.getSnacks().back().getAmount());
}
TEST(StorageTest, test_eat_all_first_with_amount) {
Storage storage = Storage(StorageType::OTHER, 10);
storage.add("Mars", 3);
storage.add("Twix", 4);
storage.eatFirst(3);
ASSERT_EQ(1, storage.getSnacks().size());
ASSERT_EQ("Twix", storage.getSnacks().front().getName());
ASSERT_EQ(4, storage.getSnacks().front().getAmount());
}
TEST(StorageTest, test_eat_last_with_amount) {
Storage storage = Storage(StorageType::OTHER, 10);
storage.add("Mars", 3);
storage.add("Twix", 4);
storage.eatLast(3);
ASSERT_EQ(3, storage.getSnacks().front().getAmount());
ASSERT_EQ(1, storage.getSnacks().back().getAmount());
}
TEST(StorageTest, test_eat_all_last_with_amount) {
Storage storage = Storage(StorageType::OTHER, 10);
storage.add("Mars", 3);
storage.add("Twix", 4);
storage.eatLast(4);
ASSERT_EQ(1, storage.getSnacks().size());
ASSERT_EQ("Mars", storage.getSnacks().back().getName());
ASSERT_EQ(3, storage.getSnacks().back().getAmount());
}
TEST(StorageTest, test_sort_snacks_by_amount) {
Storage storage = Storage(StorageType::CANDY, 15);
storage.add("Zebra", 5);
storage.add("Apple", 2);
storage.add("Mango", 7);
storage.sortBySnackName();
ASSERT_EQ("Apple", storage.getSnacks()[0].getName());
ASSERT_EQ("Mango", storage.getSnacks()[1].getName());
ASSERT_EQ("Zebra", storage.getSnacks()[2].getName());
}
TEST(StorageTest, test_sort_snacks_by_Name) {
Storage storage = Storage(StorageType::CANDY, 15);
storage.add("Zebra", 5);
storage.add("Apple", 2);
storage.add("Mango", 7);
storage.sortByAmount();
ASSERT_EQ(2, storage.getSnacks()[0].getAmount());
ASSERT_EQ(5, storage.getSnacks()[1].getAmount());
ASSERT_EQ(7, storage.getSnacks()[2].getAmount());
}
TEST(StorageTest, test_clear_snacks) {
Storage storage = Storage(StorageType::CANDY, 10);
storage.add("Candy", 3);
storage.add("Cookie", 2);
storage.clear();
ASSERT_TRUE(storage.getSnacks().empty());
}

View File

@@ -0,0 +1,93 @@
#include <gtest/gtest.h>
#include "StorageManager.hpp"
TEST(StorageManagerTest, test_construct) {
StorageManager manager;
auto& storages = manager.getStorages();
ASSERT_EQ(4, storages.size());
ASSERT_EQ(StorageType::CANDY, storages[0].getType());
ASSERT_EQ(StorageType::COOKIES, storages[1].getType());
ASSERT_EQ(StorageType::CAKE, storages[2].getType());
ASSERT_EQ(StorageType::OTHER, storages[3].getType());
for (const auto& storage : storages) {
ASSERT_EQ(10, storage.getCapacity());
ASSERT_EQ(0, storage.getAmount());
}
}
TEST(StorageManagerTest, test_add_all_storage_capacity) {
StorageManager manager;
manager.AddAllStorageCapacity(50);
auto &storages = manager.getStorages();
for (const auto &storage : storages) {
ASSERT_EQ(60, storage.getCapacity());
}
}
TEST(StorageManagerTest, test_move_capacity_success) {
StorageManager manager;
manager.moveCapacity(StorageType::CANDY, StorageType::COOKIES, 5);
auto &storages = manager.getStorages();
ASSERT_EQ(5, storages[0].getCapacity());
ASSERT_EQ(15, storages[1].getCapacity());
}
TEST(StorageManagerTest, test_move_capacity_throws) {
StorageManager manager;
auto &storages = manager.getStorages();
storages[0].add("Lollipop", 8); // 先塞點心
ASSERT_THROW(
manager.moveCapacity(StorageType::CANDY, StorageType::COOKIES, 5),
std::invalid_argument);
}
TEST(StorageManagerTest, test_move_capacity_zero) {
StorageManager manager;
manager.moveCapacity(StorageType::CANDY, StorageType::COOKIES, 0);
auto &storages = manager.getStorages();
ASSERT_EQ(10, storages[0].getCapacity());
ASSERT_EQ(10, storages[1].getCapacity());
}
TEST(StorageManagerTest, test_move_capacity_all) {
StorageManager manager;
manager.moveCapacity(StorageType::CAKE, StorageType::OTHER, 10);
auto &storages = manager.getStorages();
ASSERT_EQ(0, storages[2].getCapacity());
ASSERT_EQ(20, storages[3].getCapacity());
}
TEST(StorageManagerTest, test_move_capacity_multiple_times) {
StorageManager manager;
manager.moveCapacity(StorageType::CANDY, StorageType::COOKIES, 3);
manager.moveCapacity(StorageType::COOKIES, StorageType::CAKE, 2);
manager.moveCapacity(StorageType::CAKE, StorageType::OTHER, 1);
auto &storages = manager.getStorages();
ASSERT_EQ(7, storages[0].getCapacity()); // Candy
ASSERT_EQ(11, storages[1].getCapacity()); // Cookies
ASSERT_EQ(11, storages[2].getCapacity()); // Cake
ASSERT_EQ(11, storages[3].getCapacity()); // Other
}
TEST(StorageManagerTest, test_move_capacity_same_type) {
StorageManager manager;
manager.moveCapacity(StorageType::CANDY, StorageType::CANDY, 5);
auto &storages = manager.getStorages();
ASSERT_EQ(10, storages[0].getCapacity());
}