第3章:テストとデバッグ
はじめに
本章では、CPP Module 08のテストとデバッグ方法を解説します。
---
1. easyfindのテスト
1.1 様々なコンテナでのテスト
#include "easyfind.hpp"
#include <iostream>
#include <vector>
#include <list>
#include <deque>
#include <set>
void testVector() {
std::cout << "=== Vector Test ===" << std::endl;
std::vector<int> vec;
for (int i = 0; i < 10; i++) {
vec.push_back(i);
}
// 存在する要素
try {
std::vector<int>::iterator it = easyfind(vec, 5);
std::cout << "Found 5 at position: " << std::distance(vec.begin(), it) << std::endl;
} catch (const std::exception& e) {
std::cout << "Error: " << e.what() << std::endl;
}
// 存在しない要素
try {
easyfind(vec, 100);
std::cout << "Error: Should have thrown" << std::endl;
} catch (const std::exception& e) {
std::cout << "Correctly threw: " << e.what() << std::endl;
}
}
void testList() {
std::cout << "\n=== List Test ===" << std::endl;
std::list<int> lst;
lst.push_back(10);
lst.push_back(20);
lst.push_back(30);
try {
std::list<int>::iterator it = easyfind(lst, 20);
std::cout << "Found: " << *it << std::endl;
} catch (const std::exception& e) {
std::cout << "Error: " << e.what() << std::endl;
}
}
void testDeque() {
std::cout << "\n=== Deque Test ===" << std::endl;
std::deque<int> deq;
deq.push_front(1);
deq.push_back(2);
deq.push_front(0); // {0, 1, 2}
try {
std::deque<int>::iterator it = easyfind(deq, 1);
std::cout << "Found: " << *it << std::endl;
} catch (const std::exception& e) {
std::cout << "Error: " << e.what() << std::endl;
}
}
1.2 エッジケースのテスト
void testEdgeCases() {
std::cout << "\n=== Edge Cases ===" << std::endl;
// 空のコンテナ
std::vector<int> empty;
try {
easyfind(empty, 1);
std::cout << "Error: Should have thrown" << std::endl;
} catch (const std::exception& e) {
std::cout << "Empty container: " << e.what() << std::endl;
}
// 1要素のコンテナ
std::vector<int> single;
single.push_back(42);
try {
std::vector<int>::iterator it = easyfind(single, 42);
std::cout << "Single element found: " << *it << std::endl;
} catch (const std::exception& e) {
std::cout << "Error: " << e.what() << std::endl;
}
// 重複要素(最初のものが返される)
std::vector<int> duplicates;
duplicates.push_back(1);
duplicates.push_back(2);
duplicates.push_back(1);
duplicates.push_back(3);
try {
std::vector<int>::iterator it = easyfind(duplicates, 1);
std::cout << "First occurrence at position: " << std::distance(duplicates.begin(), it) << std::endl;
} catch (const std::exception& e) {
std::cout << "Error: " << e.what() << std::endl;
}
// 負の数
std::vector<int> negatives;
negatives.push_back(-10);
negatives.push_back(-5);
negatives.push_back(0);
try {
std::vector<int>::iterator it = easyfind(negatives, -5);
std::cout << "Negative number found: " << *it << std::endl;
} catch (const std::exception& e) {
std::cout << "Error: " << e.what() << std::endl;
}
}
1.3 constコンテナのテスト
void testConstContainer() {
std::cout << "\n=== Const Container Test ===" << std::endl;
std::vector<int> vec;
vec.push_back(1);
vec.push_back(2);
vec.push_back(3);
const std::vector<int>& constVec = vec;
try {
std::vector<int>::const_iterator it = easyfind(constVec, 2);
std::cout << "Found in const container: " << *it << std::endl;
} catch (const std::exception& e) {
std::cout << "Error: " << e.what() << std::endl;
}
}
---
2. Spanのテスト
2.1 基本機能テスト
#include "Span.hpp"
#include <iostream>
#include <cassert>
void testBasicSpan() {
std::cout << "=== Basic Span Test ===" << std::endl;
Span sp(5);
sp.addNumber(6);
sp.addNumber(3);
sp.addNumber(17);
sp.addNumber(9);
sp.addNumber(11);
assert(sp.size() == 5);
assert(sp.capacity() == 5);
// shortestSpan: |9-11| = 2 または |6-3| = 3 → 最小は2
// ソート後: {3, 6, 9, 11, 17}
// 差: 3, 3, 2, 6 → 最小は2
int shortest = sp.shortestSpan();
assert(shortest == 2);
std::cout << "Shortest span: " << shortest << " (expected: 2)" << std::endl;
// longestSpan: max - min = 17 - 3 = 14
int longest = sp.longestSpan();
assert(longest == 14);
std::cout << "Longest span: " << longest << " (expected: 14)" << std::endl;
std::cout << "Basic test PASSED" << std::endl;
}
2.2 大量データテスト
void testLargeSpan() {
std::cout << "\n=== Large Span Test ===" << std::endl;
const unsigned int SIZE = 100000;
Span bigSpan(SIZE);
// 0から99999まで追加
for (unsigned int i = 0; i < SIZE; i++) {
bigSpan.addNumber(i);
}
assert(bigSpan.size() == SIZE);
// 連続した数なのでshortestSpanは1
int shortest = bigSpan.shortestSpan();
assert(shortest == 1);
std::cout << "Shortest span: " << shortest << " (expected: 1)" << std::endl;
// longestSpan: 99999 - 0 = 99999
int longest = bigSpan.longestSpan();
assert(longest == 99999);
std::cout << "Longest span: " << longest << " (expected: 99999)" << std::endl;
std::cout << "Large test PASSED" << std::endl;
}
2.3 範囲挿入テスト
void testRangeInsert() {
std::cout << "\n=== Range Insert Test ===" << std::endl;
Span sp(100);
// vectorから挿入
std::vector<int> vec;
for (int i = 0; i < 50; i++) {
vec.push_back(i * 2); // 0, 2, 4, ..., 98
}
sp.addNumbers(vec.begin(), vec.end());
assert(sp.size() == 50);
std::cout << "Size after vector insert: " << sp.size() << std::endl;
// listから挿入
std::list<int> lst;
for (int i = 0; i < 25; i++) {
lst.push_back(i * 2 + 1); // 1, 3, 5, ..., 49
}
sp.addNumbers(lst.begin(), lst.end());
assert(sp.size() == 75);
std::cout << "Size after list insert: " << sp.size() << std::endl;
// 0から98まで連続した数
int shortest = sp.shortestSpan();
assert(shortest == 1);
std::cout << "Shortest span: " << shortest << " (expected: 1)" << std::endl;
std::cout << "Range insert test PASSED" << std::endl;
}
2.4 例外テスト
void testSpanExceptions() {
std::cout << "\n=== Span Exception Test ===" << std::endl;
// 容量超過テスト
{
Span sp(3);
sp.addNumber(1);
sp.addNumber(2);
sp.addNumber(3);
try {
sp.addNumber(4);
std::cout << "FAIL: Should have thrown" << std::endl;
} catch (const Span::CapacityExceededException& e) {
std::cout << "Capacity exception: " << e.what() << " PASS" << std::endl;
}
}
// 範囲挿入での容量超過
{
Span sp(5);
std::vector<int> vec;
for (int i = 0; i < 10; i++) {
vec.push_back(i);
}
try {
sp.addNumbers(vec.begin(), vec.end());
std::cout << "FAIL: Should have thrown" << std::endl;
} catch (const Span::CapacityExceededException& e) {
std::cout << "Range capacity exception: " << e.what() << " PASS" << std::endl;
}
}
// 要素不足(空)
{
Span sp(5);
try {
sp.shortestSpan();
std::cout << "FAIL: Should have thrown" << std::endl;
} catch (const Span::NotEnoughElementsException& e) {
std::cout << "Empty span exception: " << e.what() << " PASS" << std::endl;
}
}
// 要素不足(1要素)
{
Span sp(5);
sp.addNumber(42);
try {
sp.longestSpan();
std::cout << "FAIL: Should have thrown" << std::endl;
} catch (const Span::NotEnoughElementsException& e) {
std::cout << "Single element exception: " << e.what() << " PASS" << std::endl;
}
}
}
2.5 特殊なケースのテスト
void testSpecialCases() {
std::cout << "\n=== Special Cases Test ===" << std::endl;
// 同じ数字のみ
{
Span sp(5);
for (int i = 0; i < 5; i++) {
sp.addNumber(42);
}
int shortest = sp.shortestSpan();
assert(shortest == 0);
std::cout << "Same numbers shortest: " << shortest << " (expected: 0)" << std::endl;
int longest = sp.longestSpan();
assert(longest == 0);
std::cout << "Same numbers longest: " << longest << " (expected: 0)" << std::endl;
}
// 負の数
{
Span sp(4);
sp.addNumber(-100);
sp.addNumber(-50);
sp.addNumber(0);
sp.addNumber(50);
int shortest = sp.shortestSpan();
assert(shortest == 50);
std::cout << "Negative numbers shortest: " << shortest << " (expected: 50)" << std::endl;
int longest = sp.longestSpan();
assert(longest == 150);
std::cout << "Negative numbers longest: " << longest << " (expected: 150)" << std::endl;
}
// 2要素のみ
{
Span sp(2);
sp.addNumber(10);
sp.addNumber(100);
int shortest = sp.shortestSpan();
int longest = sp.longestSpan();
assert(shortest == longest);
assert(shortest == 90);
std::cout << "Two elements: " << shortest << " (expected: 90)" << std::endl;
}
}
---
3. MutantStackのテスト
3.1 基本機能テスト
#include "MutantStack.hpp"
#include <iostream>
#include <cassert>
#include <list>
void testMutantStackBasic() {
std::cout << "=== MutantStack Basic Test ===" << std::endl;
MutantStack<int> mstack;
// push
mstack.push(5);
mstack.push(17);
assert(mstack.top() == 17);
assert(mstack.size() == 2);
// pop
mstack.pop();
assert(mstack.top() == 5);
assert(mstack.size() == 1);
// empty
assert(!mstack.empty());
mstack.pop();
assert(mstack.empty());
std::cout << "Basic test PASSED" << std::endl;
}
3.2 イテレータテスト
void testMutantStackIterator() {
std::cout << "\n=== MutantStack Iterator Test ===" << std::endl;
MutantStack<int> mstack;
mstack.push(5);
mstack.push(17);
mstack.push(3);
mstack.push(5);
mstack.push(737);
mstack.push(0);
// 順方向イテレータ
std::cout << "Forward: ";
for (MutantStack<int>::iterator it = mstack.begin(); it != mstack.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
// 逆方向イテレータ
std::cout << "Reverse: ";
for (MutantStack<int>::reverse_iterator it = mstack.rbegin(); it != mstack.rend(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
// イテレータの++と--
MutantStack<int>::iterator it = mstack.begin();
++it;
assert(*it == 17);
--it;
assert(*it == 5);
std::cout << "Iterator test PASSED" << std::endl;
}
3.3 std::listとの比較
void testCompareWithList() {
std::cout << "\n=== Compare with std::list ===" << std::endl;
// MutantStack
MutantStack<int> mstack;
mstack.push(5);
mstack.push(17);
mstack.push(3);
mstack.push(5);
mstack.push(737);
mstack.push(0);
// std::list
std::list<int> lst;
lst.push_back(5);
lst.push_back(17);
lst.push_back(3);
lst.push_back(5);
lst.push_back(737);
lst.push_back(0);
// イテレータで比較
MutantStack<int>::iterator mIt = mstack.begin();
std::list<int>::iterator lIt = lst.begin();
bool same = true;
while (mIt != mstack.end() && lIt != lst.end()) {
if (*mIt != *lIt) {
same = false;
break;
}
++mIt;
++lIt;
}
if (mIt != mstack.end() || lIt != lst.end()) {
same = false;
}
if (same) {
std::cout << "MutantStack and std::list produce same iteration order: PASS" << std::endl;
} else {
std::cout << "FAIL: Different order" << std::endl;
}
}
3.4 コピーテスト
void testMutantStackCopy() {
std::cout << "\n=== MutantStack Copy Test ===" << std::endl;
MutantStack<int> original;
original.push(1);
original.push(2);
original.push(3);
// コピーコンストラクタ
MutantStack<int> copy1(original);
assert(copy1.size() == original.size());
assert(copy1.top() == original.top());
// 代入演算子
MutantStack<int> copy2;
copy2 = original;
assert(copy2.size() == original.size());
assert(copy2.top() == original.top());
// 独立性確認
original.push(4);
assert(copy1.size() != original.size());
assert(copy2.size() != original.size());
std::cout << "Copy test PASSED" << std::endl;
}
3.5 std::stackとの互換性
void testStackCompatibility() {
std::cout << "\n=== std::stack Compatibility Test ===" << std::endl;
MutantStack<int> mstack;
mstack.push(1);
mstack.push(2);
mstack.push(3);
// MutantStackからstd::stackへのコピー
std::stack<int> sstack(mstack);
assert(sstack.size() == mstack.size());
assert(sstack.top() == mstack.top());
// 同じ順序でpopされるか確認
while (!sstack.empty() && !mstack.empty()) {
assert(sstack.top() == mstack.top());
sstack.pop();
mstack.pop();
}
std::cout << "Stack compatibility PASSED" << std::endl;
}
---
4. よくある間違い
4.1 イテレータの型
// 間違い: typenameを忘れる
template <typename T>
T::iterator easyfind(T& container, int value); // コンパイルエラー
// 正しい
template <typename T>
typename T::iterator easyfind(T& container, int value);
4.2 endイテレータの参照
// 間違い: end()の参照外し
auto it = std::find(vec.begin(), vec.end(), value);
std::cout << *it; // 見つからなかった場合、未定義動作
// 正しい
auto it = std::find(vec.begin(), vec.end(), value);
if (it != vec.end()) {
std::cout << *it;
}
4.3 Spanの計算
// 間違い: オーバーフローの可能性
int diff = numbers[i] - numbers[j]; // 差が負になる可能性
// 正しい: ソート後なら常に正
std::sort(sorted.begin(), sorted.end());
int diff = sorted[i] - sorted[i-1]; // i > i-1 なので常に正
4.4 MutantStackのthis->
// 間違い: 依存名でないと認識される
iterator begin() { return c.begin(); }
// 正しい: this->で明示
iterator begin() { return this->c.begin(); }
4.5 イテレータの無効化
// 間違い: 範囲挿入後にイテレータを使用
auto it = span._numbers.begin();
span.addNumbers(vec.begin(), vec.end());
// itは無効化されている可能性
// 正しい: 必要なら再取得
span.addNumbers(vec.begin(), vec.end());
// 新しいイテレータを取得
---
5. 提出前チェックリスト
ex00
- [ ] std::vectorで動作する
- [ ] std::listで動作する
- [ ] std::dequeで動作する
- [ ] 見つからない場合に例外を投げる
- [ ] constコンテナでも動作する
- [ ] typename T::iteratorを使用している
- [ ] addNumberが正しく動作する
- [ ] addNumbersがイテレータ範囲で動作する
- [ ] shortestSpanが正しい値を返す
- [ ] longestSpanが正しい値を返す
- [ ] 容量超過で例外を投げる
- [ ] 要素不足で例外を投げる
- [ ] 10000要素以上でも動作する
- [ ] push/pop/top/size/emptyが動作する
- [ ] begin/endイテレータが動作する
- [ ] rbegin/rendイテレータが動作する
- [ ] std::listと同じ順序でイテレートする
- [ ] コピーコンストラクタが動作する
- [ ] 代入演算子が動作する
- [ ] std::stackにコピー可能
- easyfind: 様々なコンテナ、例外処理
- Span: 範囲挿入、計算アルゴリズム、例外
- MutantStack: イテレータ、std::listとの互換性
ex01
ex02
---
まとめ
CPP Module 08のテストでは以下を確認: