Skip to content

Commit

Permalink
Merge pull request #647 from zzam/action_built_add_test
Browse files Browse the repository at this point in the history
Action_built: Add unittest for percentage lookup
  • Loading branch information
Jarod42 committed Mar 30, 2024
2 parents f91f7db + 418576d commit 8fc31ab
Show file tree
Hide file tree
Showing 4 changed files with 264 additions and 6 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,7 @@ set(stratagus_generic_HDRS

set(stratagus_tests_SRCS
tests/main.cpp
tests/stratagus/test_action_built.cpp
tests/stratagus/test_depend.cpp
tests/stratagus/test_luacallback.cpp
tests/stratagus/test_missile_fire.cpp
Expand Down
12 changes: 8 additions & 4 deletions src/action/action_built.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -321,8 +321,12 @@ void COrder_Built::AiUnitKilled(CUnit &unit)
}


static std::size_t FindCFramePercent(const std::vector<CConstructionFrame> &cframes, int percent)
static std::optional<std::size_t> FindCFramePercent(const std::vector<CConstructionFrame> &cframes, int percent)
{
if (cframes.empty())
{
return {};
}
const auto it =
ranges::find_if(cframes, [&](const auto &frame) { return percent < frame.Percent; });
const std::size_t index = std::distance(cframes.begin(), it);
Expand All @@ -340,9 +344,9 @@ void COrder_Built::UpdateConstructionFrame(CUnit &unit)
const int percent = this->ProgressCounter / (type.Stats[unit.Player->Index].Costs[TimeCost] * 6);
const auto index = FindCFramePercent(type.Construction->Frames, percent);

if (index != this->Frame) {
this->Frame = index;
const CConstructionFrame &cframe = type.Construction->Frames[index];
if (index.has_value() && index.value() != this->Frame) {
this->Frame = index.value();
const CConstructionFrame &cframe = type.Construction->Frames[index.value()];
unit.Frame = (unit.Frame < 0) ? -cframe.Frame - 1 : cframe.Frame;
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/include/action/action_built.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,11 @@ class COrder_Built : public COrder
const CUnitPtr &GetWorker() const { return Worker; }
CUnit *GetWorkerPtr() { return Worker; }

private:
protected:
void Boost(CUnit &building, int amount, int varIndex) const;
void UpdateConstructionFrame(CUnit &unit);

private:
protected:
CUnitPtr Worker = nullptr; /// Worker building this unit
int ProgressCounter = 0; /// Progress counter, in 1/100 cycles.
bool IsCancelled = false; /// Cancel construction
Expand Down
253 changes: 253 additions & 0 deletions tests/stratagus/test_action_built.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
// _________ __ __
// / _____// |_____________ _/ |______ ____ __ __ ______
// \_____ \\ __\_ __ \__ \\ __\__ \ / ___\| | \/ ___/
// / \| | | | \// __ \| | / __ \_/ /_/ > | /\___ |
// /_______ /|__| |__| (____ /__| (____ /\___ /|____//____ >
// \/ \/ \//_____/ \/
// ______________________ ______________________
// T H E W A R B E G I N S
// Stratagus - A free fantasy real time strategy game engine
//
/**@name test_action_built.cpp - The test file for action_built.cpp. */
//
// (c) Copyright 2024 by Matthias Schwarzott
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; only version 2 of the License.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
// 02111-1307, USA.
//

#include <doctest.h>

#include "stratagus.h"
#include "unit.h"
#include "action/action_built.h"
#include "construct.h"

class tested_COrder_Built : public COrder_Built
{
public:
using COrder_Built::ProgressCounter;

int testUpdateConstrFrame_Percent(CUnit &unit, int percent, int initialFrame = 9999)
{
const int progressOnePercent = unit.Type->Stats[0].Costs[TimeCost] * 6;
return testUpdateConstrFrame_Progress(unit, percent * progressOnePercent, initialFrame);
}

int testUpdateConstrFrame_Progress(CUnit &unit, int progress, int initialFrame = 9999)
{
unit.Frame = initialFrame; // to see if it was set
Frame = -5; // force re-calculation
ProgressCounter = progress;
UpdateConstructionFrame(unit);
return unit.Frame;
}
};

namespace {
std::vector<CConstructionFrame> makeFrames(
const std::vector<std::pair<int, int>>& vec)
{
std::vector<CConstructionFrame> cframes;
const std::size_t size = vec.size();
cframes.resize(size);
for (int i = 0; i < size; ++i) {
cframes[i].Percent = vec[i].first;
cframes[i].Frame = vec[i].second;
}
return cframes;
}
};

TEST_CASE("COrder_Built Frame")
{
CPlayer player;
player.Index = 0;
CUnitType type;
type.Stats[0].Costs[TimeCost] = 1;
CConstruction construction;
type.Construction = &construction;
auto &frames = construction.Frames;
CUnit unit;
unit.Type = &type;
unit.Player = &player;
tested_COrder_Built order;

SUBCASE("init values") {
CHECK(unit.Frame == 0);
CHECK(order.ProgressCounter == 0);
}

SUBCASE("size=0") {
frames = makeFrames({});

// check unit.Frame is not modified
CHECK(order.testUpdateConstrFrame_Percent(unit, 0, 123) == 123);
}

SUBCASE("size=1") {
frames = makeFrames({
{0, 0},
});

CHECK(order.testUpdateConstrFrame_Percent(unit, 0) == 0);
CHECK(order.testUpdateConstrFrame_Percent(unit, 1) == 0);
CHECK(order.testUpdateConstrFrame_Percent(unit, 100) == 0);
}

SUBCASE("large steps") {
frames = makeFrames({
{ 0, 100},
{50, 110},
});

SUBCASE("normal") {
CHECK(order.testUpdateConstrFrame_Percent(unit, 0) == 100);
CHECK(order.testUpdateConstrFrame_Percent(unit, 1) == 100);
CHECK(order.testUpdateConstrFrame_Percent(unit, 49) == 100);
CHECK(order.testUpdateConstrFrame_Percent(unit, 50) == 110);
CHECK(order.testUpdateConstrFrame_Percent(unit, 51) == 110);
CHECK(order.testUpdateConstrFrame_Percent(unit, 100) == 110);
}

SUBCASE("mirrored") {
CHECK(order.testUpdateConstrFrame_Percent(unit, 49, -1) == -101);
CHECK(order.testUpdateConstrFrame_Percent(unit, 50, -1) == -111);
}

SUBCASE("out of bounds") {
// return frame of first entry
CHECK(order.testUpdateConstrFrame_Percent(unit, -1) == 100);
CHECK(order.testUpdateConstrFrame_Percent(unit, 101) == 110);
}
}

SUBCASE("partially dense")
{
frames = makeFrames({
{ 0, 0},
{ 1, 1},
{ 2, 2},
{ 65, 3},
{ 66, 4},
{ 67, 5},
{ 99, 6},
{100, 7},
});

CHECK(order.testUpdateConstrFrame_Percent(unit, 0) == 0);
CHECK(order.testUpdateConstrFrame_Percent(unit, 1) == 1);
CHECK(order.testUpdateConstrFrame_Percent(unit, 2) == 2);
CHECK(order.testUpdateConstrFrame_Percent(unit, 3) == 2);

CHECK(order.testUpdateConstrFrame_Percent(unit, 50) == 2);

CHECK(order.testUpdateConstrFrame_Percent(unit, 64) == 2);
CHECK(order.testUpdateConstrFrame_Percent(unit, 65) == 3);
CHECK(order.testUpdateConstrFrame_Percent(unit, 66) == 4);
CHECK(order.testUpdateConstrFrame_Percent(unit, 67) == 5);
CHECK(order.testUpdateConstrFrame_Percent(unit, 68) == 5);

CHECK(order.testUpdateConstrFrame_Percent(unit, 98) == 5);
CHECK(order.testUpdateConstrFrame_Percent(unit, 99) == 6);
CHECK(order.testUpdateConstrFrame_Percent(unit, 100) == 7);

SUBCASE("by progress")
{
CHECK(order.testUpdateConstrFrame_Progress(unit, 0) == 0);
CHECK(order.testUpdateConstrFrame_Progress(unit, 5) == 0);
CHECK(order.testUpdateConstrFrame_Progress(unit, 6) == 1);
CHECK(order.testUpdateConstrFrame_Progress(unit, 594) == 6);
CHECK(order.testUpdateConstrFrame_Progress(unit, 595) == 6);
CHECK(order.testUpdateConstrFrame_Progress(unit, 600) == 7);
}
}

SUBCASE("repeated values")
{
frames = makeFrames({
{ 0, 8},
{ 80, 1},
{ 90, 8},
});

CHECK(order.testUpdateConstrFrame_Percent(unit, 79) == 8);
CHECK(order.testUpdateConstrFrame_Percent(unit, 80) == 1);
CHECK(order.testUpdateConstrFrame_Percent(unit, 89) == 1);
CHECK(order.testUpdateConstrFrame_Percent(unit, 90) == 8);
}

SUBCASE("first key larger") {
frames = makeFrames({
{ 10, 5},
});

CHECK(order.testUpdateConstrFrame_Percent(unit, 9) == 5);
CHECK(order.testUpdateConstrFrame_Percent(unit, 10) == 5);
}

SUBCASE("duplicate keys")
{
frames = makeFrames({
{ 0, 0},
{ 20, 1},
{ 20, 2},
{ 60, 3},
});

CHECK(order.testUpdateConstrFrame_Percent(unit, 19) == 0);
CHECK(order.testUpdateConstrFrame_Percent(unit, 20) == 2);
CHECK(order.testUpdateConstrFrame_Percent(unit, 21) == 2);
}

SUBCASE("wrong key order") {
frames = makeFrames({
{ 75, 9},
{ 50, 8},
{ 0, 7},
});

CHECK_FALSE(ranges::is_sorted(frames, std::less<>{}, &CConstructionFrame::Percent));
const bool allgood =
order.testUpdateConstrFrame_Percent(unit, 0) == 7 &&
order.testUpdateConstrFrame_Percent(unit, 49) == 7 &&
order.testUpdateConstrFrame_Percent(unit, 50) == 8 &&
order.testUpdateConstrFrame_Percent(unit, 74) == 8 &&
order.testUpdateConstrFrame_Percent(unit, 75) == 9 &&
order.testUpdateConstrFrame_Percent(unit, 76) == 9;
CHECK_FALSE(allgood);
}

SUBCASE("too small key value") {
frames = makeFrames({
{ -1, 7},
{100, 8},
});

CHECK(order.testUpdateConstrFrame_Percent(unit, 0) == 7);
CHECK(order.testUpdateConstrFrame_Percent(unit, 1) == 7);
CHECK(order.testUpdateConstrFrame_Percent(unit, 99) == 7);
CHECK(order.testUpdateConstrFrame_Percent(unit, 100) == 8);
}

SUBCASE("too large key value") {
frames = makeFrames({
{ 0, 7},
{101, 8},
});

CHECK(order.testUpdateConstrFrame_Percent(unit, 0) == 7);
CHECK(order.testUpdateConstrFrame_Percent(unit, 100) == 7);
}
}

0 comments on commit 8fc31ab

Please sign in to comment.