Skip to content

Commit e006b2b

Browse files
committed
update test
1 parent e4f903b commit e006b2b

File tree

14 files changed

+1057
-7
lines changed

14 files changed

+1057
-7
lines changed

src/logic/Trip.h

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,15 @@ class Trip {
5757

5858
if (isGnssUpdated) {
5959
processGnssUpdate(navData, currentMillis);
60+
// GNSS更新時は常に平均速度を再計算
61+
state.avgSpeed = calculateAverageSpeed(state.tripDistance, state.totalMovingMs);
6062
} else {
6163
handleGnssTimeout(currentMillis);
64+
// GNSS未更新時でも、1秒(1000ms)ごとに平均速度を更新(移動時間による減衰を反映)
65+
if (dt >= 1000 || (currentMillis % 1000 < dt)) {
66+
state.avgSpeed = calculateAverageSpeed(state.tripDistance, state.totalMovingMs);
67+
}
6268
}
63-
64-
state.avgSpeed = calculateAverageSpeed(state.tripDistance, state.totalMovingMs);
6569
}
6670

6771
void resetTrip() {
@@ -132,7 +136,7 @@ class Trip {
132136
state.status = determineStatus(state.status, moving);
133137
state.currentSpeed = calculateCurrentSpeed(state.status, rawKmh);
134138

135-
if (fix) {
139+
if (fix && isValidCoordinate(navData.latitude, navData.longitude)) {
136140
float deltaKm = updateOdometer(navData.latitude, navData.longitude, moving);
137141
if (state.status != Status::Paused) { state.tripDistance += deltaKm; }
138142
}
@@ -148,14 +152,13 @@ class Trip {
148152
}
149153

150154
float updateOdometer(float lat, float lon, bool moving) {
151-
if (!isValidCoordinate(lat, lon)) return 0.0f;
152-
153155
if (!hasLastCoord) {
154156
updateLastCoordinate(lat, lon);
155157
hasLastCoord = true;
156158
return 0.0f;
157159
}
158160

161+
// If not moving, no distance is accumulated for the odometer
159162
if (!moving) return 0.0f;
160163

161164
const float dist = planarDistanceKm(lastLat, lastLon, lat, lon);

tests/host/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ set(TEST_SOURCES
1616
UITest.cpp
1717
HardwareTest.cpp
1818
AppTest.cpp
19+
EquivalenceTest.cpp
20+
CalculationErrorTest.cpp
21+
HardwareFailureTest.cpp
22+
NegativeTest.cpp
23+
SystemIntegrationTest.cpp
24+
PowerLossTest.cpp
1925
)
2026

2127
add_executable(run_tests
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
#include "TripTestBase.h"
2+
#include <limits>
3+
4+
/**
5+
* @brief 内部計算におけるエラー(精度低下、溢れ、ゼロ除算など)の可能性を検証するテスト
6+
*/
7+
class CalculationTest : public TripTestBase {};
8+
9+
// --- 1. 統計計算の安定性 ---
10+
11+
TEST_F(CalculationTest, AverageSpeed_ZeroDivision) {
12+
EXPECT_FLOAT_EQ(trip.getState().avgSpeed, 0.0f);
13+
14+
navData.velocity = 0.0f;
15+
updateTrip(1000);
16+
updateTrip(2000);
17+
EXPECT_FLOAT_EQ(trip.getState().avgSpeed, 0.0f);
18+
EXPECT_FALSE(std::isnan(trip.getState().avgSpeed));
19+
}
20+
21+
TEST_F(CalculationTest, AverageSpeed_SmallTime) {
22+
setupMovingState(1000);
23+
24+
// 実際に少し移動させる
25+
navData.moveByMeters(10.0f);
26+
updateTrip(1101); // +1ms 経過
27+
28+
EXPECT_FALSE(std::isnan(trip.getState().avgSpeed));
29+
EXPECT_GT(trip.getState().avgSpeed, 0.0f);
30+
}
31+
32+
// --- 2. 距離計算の精度と累積誤差 ---
33+
34+
TEST_F(CalculationTest, Odometer_PrecisionLoss) {
35+
trip.restore(10000.0f, 0.0f, 0, 0.0f);
36+
setupMovingState(1000);
37+
38+
float initialTotal = trip.getState().totalKm;
39+
40+
// 10mの移動を100回繰り返す
41+
for (int i = 0; i < 100; ++i) {
42+
navData.moveByMeters(11.0f);
43+
updateTrip(2000 + i * 1000);
44+
}
45+
46+
EXPECT_NEAR(trip.getState().totalKm, initialTotal + 1.1f, 0.1f);
47+
}
48+
49+
// --- 3. 座標計算の境界ケース ---
50+
51+
TEST_F(CalculationTest, Distance_NearPoles) {
52+
navData.latitude = 89.9f;
53+
setupMovingState(1000);
54+
55+
float startKm = trip.getState().totalKm;
56+
navData.moveByMeters(10.0f);
57+
updateTrip(2000);
58+
59+
EXPECT_GT(trip.getState().totalKm, startKm);
60+
}
61+
62+
TEST_F(CalculationTest, Distance_LongitudeWrap) {
63+
navData.longitude = 179.999f;
64+
setupMovingState(1000);
65+
66+
float startKm = trip.getState().totalKm;
67+
navData.longitude = -179.999f; // 反対側へジャンプ
68+
updateTrip(2000);
69+
70+
// 跳躍は無視されるべき
71+
EXPECT_FLOAT_EQ(trip.getState().totalKm, startKm);
72+
}
73+
74+
// --- 4. 時間のオーバーフロー ---
75+
76+
TEST_F(CalculationTest, Time_OverflowHandling) {
77+
unsigned long nearlyMax = std::numeric_limits<unsigned long>::max() - 500;
78+
79+
setupMovingState(nearlyMax);
80+
81+
updateTrip(nearlyMax + 1000); // Wrap-around
82+
83+
// 2回目のupdate(nearlyMax+100)から3回目のupdate(nearlyMax+1000)の差分 900ms が加算される
84+
EXPECT_EQ(trip.getState().totalMovingMs, 900);
85+
}
86+
87+
// --- 5. 無効な数値入力 ---
88+
89+
TEST_F(CalculationTest, Speed_NaN_Input) {
90+
setupMovingState(1000);
91+
navData.velocity = std::numeric_limits<float>::quiet_NaN();
92+
updateTrip(2000);
93+
94+
EXPECT_FALSE(std::isnan(trip.getState().currentSpeed));
95+
EXPECT_FLOAT_EQ(trip.getState().currentSpeed, 0.0f);
96+
}

tests/host/EquivalenceTest.cpp

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
#include "TripTestBase.h"
2+
3+
/**
4+
* @brief 同値分割法(Equivalence Partitioning)に基づくテスト
5+
*/
6+
class EquivalenceTest : public TripTestBase {};
7+
8+
// --- 1. GNSS測位状態 (Fix Mode) ---
9+
10+
TEST_F(EquivalenceTest, FixMode_Valid_3D) {
11+
navData.posFixMode = Fix3D;
12+
setupMovingState();
13+
EXPECT_EQ(trip.getState().status, Trip::Status::Moving);
14+
}
15+
16+
TEST_F(EquivalenceTest, FixMode_Valid_2D) {
17+
navData.posFixMode = Fix2D;
18+
setupMovingState();
19+
EXPECT_EQ(trip.getState().status, Trip::Status::Moving);
20+
}
21+
22+
TEST_F(EquivalenceTest, FixMode_Invalid_Invalid) {
23+
navData.posFixMode = FixInvalid;
24+
setupMovingState();
25+
// 無効なFix状態では、速度があってもStoppedになるべき
26+
EXPECT_EQ(trip.getState().status, Trip::Status::Stopped);
27+
}
28+
29+
// --- 2. 走行速度 (Speed) ---
30+
31+
TEST_F(EquivalenceTest, Speed_Moving_Typical) {
32+
navData.velocity = 20.0f / 3.6f;
33+
setupMovingState();
34+
EXPECT_EQ(trip.getState().status, Trip::Status::Moving);
35+
EXPECT_NEAR(trip.getState().currentSpeed, 20.0f, 0.01f);
36+
}
37+
38+
TEST_F(EquivalenceTest, Speed_Stopped_Zero) {
39+
navData.velocity = 0.0f;
40+
setupMovingState();
41+
EXPECT_EQ(trip.getState().status, Trip::Status::Stopped);
42+
}
43+
44+
TEST_F(EquivalenceTest, Speed_Stopped_VerySlow) {
45+
navData.velocity = (MIN_MOVING_SPEED_KMH - 0.0001f) / 3.6f;
46+
setupMovingState();
47+
EXPECT_EQ(trip.getState().status, Trip::Status::Stopped);
48+
}
49+
50+
// --- 3. 座標変化 (Distance Delta) ---
51+
52+
TEST_F(EquivalenceTest, DistanceDelta_Valid) {
53+
setupMovingState(1000);
54+
55+
navData.moveByMeters(10.0f); // 10m移動
56+
updateTrip(2000);
57+
58+
EXPECT_GT(trip.getState().totalKm, 0.0f);
59+
EXPECT_LT(trip.getState().totalKm, 0.1f);
60+
}
61+
62+
TEST_F(EquivalenceTest, DistanceDelta_Noise_TooSmall) {
63+
setupMovingState(1000);
64+
65+
navData.moveByMeters(0.1f); // 0.1m (MIN_DELTA 2m 以下)
66+
updateTrip(2000);
67+
68+
EXPECT_FLOAT_EQ(trip.getState().totalKm, 0.0f);
69+
}
70+
71+
TEST_F(EquivalenceTest, DistanceDelta_Jump_TooLarge) {
72+
setupMovingState(1000);
73+
74+
navData.moveByMeters(2000.0f); // 2km (MAX_DELTA 1km 以上)
75+
updateTrip(2000);
76+
77+
EXPECT_FLOAT_EQ(trip.getState().totalKm, 0.0f);
78+
}
79+
80+
// --- 4. 座標の妥当性 (Coordinate Validity) ---
81+
82+
TEST_F(EquivalenceTest, Coordinate_Invalid_Zero) {
83+
navData.latitude = 0.0f;
84+
navData.longitude = 0.0f;
85+
setupMovingState();
86+
87+
navData.moveByMeters(10.0f);
88+
updateTrip(3000);
89+
EXPECT_FLOAT_EQ(trip.getState().totalKm, 0.0f);
90+
}
91+
92+
// --- 5. 追加の重要ケース (元LogicTestより) ---
93+
94+
TEST_F(EquivalenceTest, GnssTimeout) {
95+
setupMovingState(1000);
96+
EXPECT_EQ(trip.getState().status, Trip::Status::Moving);
97+
98+
// Timeout (isGnssUpdated = false)
99+
// setupMovingState(1000) で lastGnssUpdateMs は 1100 になっている
100+
updateTrip(1100 + SIGNAL_TIMEOUT_MS + 100, false);
101+
EXPECT_EQ(trip.getState().status, Trip::Status::Stopped);
102+
EXPECT_FLOAT_EQ(trip.getState().currentSpeed, 0.0f);
103+
}
104+
105+
TEST_F(EquivalenceTest, PauseToggle) {
106+
trip.pause();
107+
EXPECT_EQ(trip.getState().status, Trip::Status::Paused);
108+
trip.pause();
109+
EXPECT_EQ(trip.getState().status, Trip::Status::Stopped);
110+
}

tests/host/HardwareFailureTest.cpp

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
#include "hardware/Button.h"
2+
#include "hardware/Gnss.h"
3+
#include "hardware/OLED.h"
4+
#include "hardware/VoltageSensor.h"
5+
#include "mocks/Arduino.h"
6+
#include <gtest/gtest.h>
7+
8+
/**
9+
* @brief ハードウェアの故障、ショート、未接続などの異常状態を想定したテスト
10+
*/
11+
12+
class HardwareFailureTest : public ::testing::Test {
13+
protected:
14+
void SetUp() override {
15+
_mock_millis = 0;
16+
_mock_pin_states.clear();
17+
_mock_analog_values.clear();
18+
}
19+
};
20+
21+
// --- 1. ボタン故障 (Button Failure) ---
22+
23+
TEST_F(HardwareFailureTest, Button_ShortGND) {
24+
// ボタンが常にLOWに張り付いている(ショート)状態
25+
Button button(PIN_D02);
26+
setPinState(PIN_D02, LOW);
27+
button.begin();
28+
29+
// 常に押されていると判定されるが、エッジトリガーによる「一回押した」判定は出ないべき
30+
button.update();
31+
EXPECT_FALSE(button.isPressed());
32+
EXPECT_TRUE(button.isHeld()); // 長押し状態として検出される
33+
}
34+
35+
TEST_F(HardwareFailureTest, Button_Disconnected) {
36+
// ボタンが接続されていない、または断線している場合(プルアップにより常にHIGH)
37+
Button button(PIN_D02);
38+
setPinState(PIN_D02, HIGH);
39+
button.begin();
40+
41+
button.update();
42+
EXPECT_FALSE(button.isPressed());
43+
EXPECT_FALSE(button.isHeld());
44+
}
45+
46+
// --- 2. 電圧センサ異常 (Voltage Sensor Failure) ---
47+
48+
TEST_F(HardwareFailureTest, Voltage_ShortToGND) {
49+
// アナログピンがGNDにショートしている場合
50+
VoltageSensor sensor(PIN_A5);
51+
setAnalogReadValue(PIN_A5, 0);
52+
53+
float v = sensor.readVoltage();
54+
EXPECT_FLOAT_EQ(v, 0.0f);
55+
}
56+
57+
TEST_F(HardwareFailureTest, Voltage_ShortToVCC) {
58+
// アナログピンがVCC(3.3V)にショートしている場合
59+
VoltageSensor sensor(PIN_A5);
60+
setAnalogReadValue(PIN_A5, 1023);
61+
62+
float v = sensor.readVoltage();
63+
EXPECT_FLOAT_EQ(v, 3.3f);
64+
}
65+
66+
TEST_F(HardwareFailureTest, Voltage_OverVoltage) {
67+
// 想定以上の電圧(ADC最大値を超える場合、通常は1023に張り付く)
68+
VoltageSensor sensor(PIN_A5);
69+
setAnalogReadValue(PIN_A5, 2000); // 10bit ADCを超える異常値
70+
71+
float v = sensor.readVoltage();
72+
// ロジック的に 2000/1023 * 3.3 となるが、実機では1023で飽和することを考慮したテスト
73+
EXPECT_GT(v, 3.3f);
74+
}
75+
76+
// --- 3. I2Cバス異常 (Communication Failure) ---
77+
78+
TEST_F(HardwareFailureTest, OLED_NoResponse) {
79+
// OLEDが接続されていない、またはI2Cバスが死んでいる場合
80+
OLED oled;
81+
Adafruit_SSD1306::mockBeginResult = false; // beginが失敗を返す
82+
83+
bool success = oled.begin();
84+
EXPECT_FALSE(success);
85+
86+
// 失敗した状態でメソッドを呼んでもクラッシュしないことを確認
87+
oled.clear();
88+
oled.display();
89+
}
90+
91+
// --- 4. GNSSモジュール異常 (Module Failure) ---
92+
93+
TEST_F(HardwareFailureTest, GNSS_NotResponding) {
94+
// GNSSモジュールが応答しない場合
95+
Gnss gnss;
96+
SpGnss::mockBeginResult = -1; // 初期化失敗
97+
98+
bool success = gnss.begin();
99+
EXPECT_FALSE(success);
100+
}
101+
102+
TEST_F(HardwareFailureTest, GNSS_StuckOnUpdate) {
103+
// 更新が全く来なくなった場合(タイムアウトの模倣)
104+
// これはTripロジック側でのテストに近いが、ハードウェア層の抽象化としても重要
105+
}

0 commit comments

Comments
 (0)