第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を使用している
  • ex01

  • [ ] addNumberが正しく動作する
  • [ ] addNumbersがイテレータ範囲で動作する
  • [ ] shortestSpanが正しい値を返す
  • [ ] longestSpanが正しい値を返す
  • [ ] 容量超過で例外を投げる
  • [ ] 要素不足で例外を投げる
  • [ ] 10000要素以上でも動作する
  • ex02

  • [ ] push/pop/top/size/emptyが動作する
  • [ ] begin/endイテレータが動作する
  • [ ] rbegin/rendイテレータが動作する
  • [ ] std::listと同じ順序でイテレートする
  • [ ] コピーコンストラクタが動作する
  • [ ] 代入演算子が動作する
  • [ ] std::stackにコピー可能
  • ---

    まとめ

    CPP Module 08のテストでは以下を確認:

  • easyfind: 様々なコンテナ、例外処理
  • Span: 範囲挿入、計算アルゴリズム、例外
  • MutantStack: イテレータ、std::listとの互換性