diff --git a/sources/BaseAPI/API/Commands/Tests/TEST_Voting.uc b/sources/BaseAPI/API/Commands/Tests/TEST_Voting.uc new file mode 100644 index 0000000..6dd7016 --- /dev/null +++ b/sources/BaseAPI/API/Commands/Tests/TEST_Voting.uc @@ -0,0 +1,351 @@ +/** + * Author: dkanus + * Home repo: https://www.insultplayers.ru/git/AcediaFramework/AcediaCore + * License: GPL + * Copyright 2023 Anton Tarasenko + *------------------------------------------------------------------------------ + * This file is part of Acedia. + * + * Acedia 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, version 3 of the License, or + * (at your option) any later version. + * + * Acedia 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 Acedia. If not, see . + */ +class TEST_Voting extends TestCase + abstract + dependsOn(VotingModel); + +enum ExpectedOutcome { + TEST_EO_Continue, + TEST_EO_End, +}; + +protected static function VotingModel MakeVotingModel(bool drawMeansWin) { + local VotingModel model; + + model = VotingModel(__().memory.Allocate(class'VotingModel')); + model.Start(drawMeansWin); + return model; +} + +protected static function SetVoters( + VotingModel model, + optional string voterID0, + optional string voterID1, + optional string voterID2, + optional string voterID3, + optional string voterID4, + optional string voterID5, + optional string voterID6, + optional string voterID7, + optional string voterID8, + optional string voterID9 +) { + local UserID nextID; + local array voterIDs; + + if (voterID0 != "") { + nextID = UserID(__().memory.Allocate(class'UserID')); + nextID.Initialize(__().text.FromString(voterID0)); + voterIDs[voterIDs.length] = nextID; + } + if (voterID1 != "") { + nextID = UserID(__().memory.Allocate(class'UserID')); + nextID.Initialize(__().text.FromString(voterID1)); + voterIDs[voterIDs.length] = nextID; + } + if (voterID2 != "") { + nextID = UserID(__().memory.Allocate(class'UserID')); + nextID.Initialize(__().text.FromString(voterID2)); + voterIDs[voterIDs.length] = nextID; + } + if (voterID3 != "") { + nextID = UserID(__().memory.Allocate(class'UserID')); + nextID.Initialize(__().text.FromString(voterID3)); + voterIDs[voterIDs.length] = nextID; + } + if (voterID4 != "") { + nextID = UserID(__().memory.Allocate(class'UserID')); + nextID.Initialize(__().text.FromString(voterID4)); + voterIDs[voterIDs.length] = nextID; + } + if (voterID5 != "") { + nextID = UserID(__().memory.Allocate(class'UserID')); + nextID.Initialize(__().text.FromString(voterID5)); + voterIDs[voterIDs.length] = nextID; + } + if (voterID6 != "") { + nextID = UserID(__().memory.Allocate(class'UserID')); + nextID.Initialize(__().text.FromString(voterID6)); + voterIDs[voterIDs.length] = nextID; + } + if (voterID7 != "") { + nextID = UserID(__().memory.Allocate(class'UserID')); + nextID.Initialize(__().text.FromString(voterID7)); + voterIDs[voterIDs.length] = nextID; + } + if (voterID8 != "") { + nextID = UserID(__().memory.Allocate(class'UserID')); + nextID.Initialize(__().text.FromString(voterID8)); + voterIDs[voterIDs.length] = nextID; + } + if (voterID9 != "") { + nextID = UserID(__().memory.Allocate(class'UserID')); + nextID.Initialize(__().text.FromString(voterID9)); + voterIDs[voterIDs.length] = nextID; + } + model.UpdatePotentialVoters(voterIDs); +} + +protected static function MakeFaultyYesVote( + VotingModel model, + string voterID, + VotingModel.VotingResult expected) { + local UserID id; + + id = UserID(__().memory.Allocate(class'UserID')); + id.Initialize(__().text.FromString(voterID)); + Issue("Illegal vote had unexpected result."); + TEST_ExpectTrue(model.CastVote(id, true) == expected); +} + +protected static function MakeFaultyNoVote( + VotingModel model, + string voterID, + VotingModel.VotingResult expected) { + local UserID id; + + id = UserID(__().memory.Allocate(class'UserID')); + id.Initialize(__().text.FromString(voterID)); + Issue("Illegal vote had unexpected result."); + TEST_ExpectTrue(model.CastVote(id, false) == expected); +} + +protected static function VoteYes(VotingModel model, string voterID, ExpectedOutcome expected) { + local UserID id; + + id = UserID(__().memory.Allocate(class'UserID')); + id.Initialize(__().text.FromString(voterID)); + Issue("Failed to add legitimate vote."); + TEST_ExpectTrue(model.CastVote(id, true) == VFR_Success); + if (expected == TEST_EO_Continue) { + Issue("Vote, that shouldn't have ended voting, ended it."); + TEST_ExpectTrue(model.GetStatus() == VPM_InProgress); + } else if (expected == TEST_EO_End) { + Issue("Vote, that should've ended voting with one side's victory, didn't do it."); + TEST_ExpectTrue(model.GetStatus() == VPM_Success); + } +} + +protected static function VoteNo(VotingModel model, string voterID, ExpectedOutcome expected) { + local UserID id; + + id = UserID(__().memory.Allocate(class'UserID')); + id.Initialize(__().text.FromString(voterID)); + Issue("Failed to add legitimate vote."); + TEST_ExpectTrue(model.CastVote(id, false) == VFR_Success); + if (expected == TEST_EO_Continue) { + Issue("Vote, that shouldn't have ended voting, ended it."); + TEST_ExpectTrue(model.GetStatus() == VPM_InProgress); + } else if (expected == TEST_EO_End) { + Issue("Vote, that should've ended voting with one side's victory, didn't do it."); + TEST_ExpectTrue(model.GetStatus() == VPM_Failure); + } +} + +protected static function TESTS() { + Test_VotingModel(); +} + +protected static function Test_VotingModel() { + SubTest_YesVoting(); + SubTest_NoVoting(); + SubTest_FaultyVoting(); + SubTest_DisconnectVoting_DrawMeansWin(); + SubTest_DisconnectVoting_DrawMeansLoss(); + SubTest_ReconnectVoting_DrawMeansWin(); + SubTest_ReconnectVoting_DrawMeansLoss(); +} + +protected static function SubTest_YesVoting() { + local VotingModel model; + + Context("Testing \"yes\" voting."); + model = MakeVotingModel(true); + SetVoters(model, "1", "2", "3", "4", "5", "6"); + VoteYes(model, "2", TEST_EO_Continue); + VoteYes(model, "4", TEST_EO_Continue); + VoteNo(model, "6", TEST_EO_Continue); + VoteNo(model, "1", TEST_EO_Continue); + VoteYes(model, "6", TEST_EO_End); + + model = MakeVotingModel(false); + SetVoters(model, "1", "2", "3", "4", "5", "6"); + VoteYes(model, "2", TEST_EO_Continue); + VoteYes(model, "4", TEST_EO_Continue); + VoteYes(model, "3", TEST_EO_Continue); + VoteNo(model, "6", TEST_EO_Continue); + VoteNo(model, "1", TEST_EO_Continue); + VoteYes(model, "6", TEST_EO_End); +} + +protected static function SubTest_NoVoting() { + local VotingModel model; + + Context("Testing \"no\" voting."); + model = MakeVotingModel(true); + SetVoters(model, "1", "2", "3", "4", "5", "6"); + VoteNo(model, "1", TEST_EO_Continue); + VoteNo(model, "2", TEST_EO_Continue); + VoteNo(model, "6", TEST_EO_Continue); + VoteYes(model, "5", TEST_EO_Continue); + VoteYes(model, "4", TEST_EO_Continue); + VoteNo(model, "5", TEST_EO_End); + + model = MakeVotingModel(false); + SetVoters(model, "1", "2", "3", "4", "5", "6"); + VoteNo(model, "1", TEST_EO_Continue); + VoteNo(model, "2", TEST_EO_Continue); + VoteYes(model, "5", TEST_EO_Continue); + VoteYes(model, "4", TEST_EO_Continue); + VoteNo(model, "5", TEST_EO_End); +} + +protected static function SubTest_FaultyVoting() { + local VotingModel model; + + Context("Testing \"faulty\" voting."); + model = MakeVotingModel(false); + SetVoters(model, "1", "2", "3", "4", "5", "6"); + VoteYes(model, "3", TEST_EO_Continue); + VoteYes(model, "5", TEST_EO_Continue); + VoteYes(model, "2", TEST_EO_Continue); + VoteNo(model, "5", TEST_EO_Continue); + VoteYes(model, "1", TEST_EO_Continue); + VoteYes(model, "6", TEST_EO_End); + MakeFaultyYesVote(model, "4", VFR_VotingEnded); // voting already ended, so no more votes here + + model = MakeVotingModel(true); + SetVoters(model, "1", "2", "3", "4", "5", "6"); + VoteYes(model, "3", TEST_EO_Continue); + VoteYes(model, "5", TEST_EO_Continue); + VoteNo(model, "5", TEST_EO_Continue); + VoteYes(model, "1", TEST_EO_Continue); + VoteYes(model, "6", TEST_EO_End); + MakeFaultyYesVote(model, "4", VFR_VotingEnded); // voting already ended, so no more votes here +} + +protected static function SubTest_DisconnectVoting_DrawMeansWin() { + local VotingModel model; + + Context("Testing \"disconnect\" voting when draw means victory."); + model = MakeVotingModel(true); + SetVoters(model, "1", "2", "3", "4", "5", "6", "7", "8", "9", "10"); + VoteYes(model, "1", TEST_EO_Continue); + VoteYes(model, "2", TEST_EO_Continue); + VoteYes(model, "3", TEST_EO_Continue); + VoteNo(model, "5", TEST_EO_Continue); + VoteYes(model, "5", TEST_EO_Continue); + VoteNo(model, "5", TEST_EO_Continue); + VoteYes(model, "4", TEST_EO_Continue); + VoteNo(model, "6", TEST_EO_Continue); + SetVoters(model, "2", "4", "5", "6", "8", "9", "10"); // remove 1, 3, 7 - 2 "yes" votes + MakeFaultyNoVote(model, "1", VFR_NotAllowed); + VoteNo(model, "8", TEST_EO_Continue); + VoteYes(model, "5", TEST_EO_Continue); + VoteNo(model, "9", TEST_EO_Continue); + // Here we're at 3 "no" votes, 3 "yes" votes out of 7 total; + // disconnect "6" and "9" for "yes" to win + SetVoters(model, "2", "4", "5", "8", "10"); + Issue("Unexpected result after voting users disconnected."); + TEST_ExpectTrue(model.GetStatus() == VPM_Success); +} + +protected static function SubTest_DisconnectVoting_DrawMeansLoss() { + local VotingModel model; + + Context("Testing \"disconnect\" voting when draw means loss."); + model = MakeVotingModel(false); + SetVoters(model, "1", "2", "3", "4", "5", "6", "7", "8", "9", "10"); + VoteYes(model, "1", TEST_EO_Continue); + VoteYes(model, "2", TEST_EO_Continue); + VoteYes(model, "6", TEST_EO_Continue); + VoteYes(model, "3", TEST_EO_Continue); + VoteNo(model, "5", TEST_EO_Continue); + VoteYes(model, "5", TEST_EO_Continue); + VoteNo(model, "5", TEST_EO_Continue); + VoteYes(model, "4", TEST_EO_Continue); + VoteNo(model, "6", TEST_EO_Continue); + VoteYes(model, "7", TEST_EO_Continue); + SetVoters(model, "2", "4", "5", "6", "8", "9", "10"); // remove 1, 3, 7 - 3 "yes" votes + MakeFaultyNoVote(model, "1", VFR_NotAllowed); + VoteNo(model, "8", TEST_EO_Continue); + VoteYes(model, "5", TEST_EO_Continue); + VoteNo(model, "9", TEST_EO_Continue); + // Here we're at 3 "no" votes, 3 "yes" votes out of 7 total; + // disconnect "6" and "9" for "yes" to win + SetVoters(model, "2", "4", "5", "8", "10"); + Issue("Unexpected result after voting users disconnected."); + TEST_ExpectTrue(model.GetStatus() == VPM_Success); +} + +protected static function SubTest_ReconnectVoting_DrawMeansWin() { + local VotingModel model; + + Context("Testing \"reconnect\" voting when draw means victory."); + model = MakeVotingModel(true); + SetVoters(model, "1", "2", "3", "4", "5", "6", "7", "8", "9", "10"); + VoteYes(model, "1", TEST_EO_Continue); + VoteNo(model, "2", TEST_EO_Continue); + VoteYes(model, "3", TEST_EO_Continue); + VoteNo(model, "4", TEST_EO_Continue); + VoteYes(model, "5", TEST_EO_Continue); + VoteNo(model, "6", TEST_EO_Continue); + // Disconnect 1 3 "yes" voters + SetVoters(model, "2", "4", "5", "6", "7", "8", "9", "10"); + VoteYes(model, "7", TEST_EO_Continue); + VoteNo(model, "8", TEST_EO_Continue); + VoteYes(model, "9", TEST_EO_Continue); + MakeFaultyNoVote(model, "1", VFR_NotAllowed); + MakeFaultyNoVote(model, "3", VFR_NotAllowed); + // Restore 3 "yes" voter + SetVoters(model, "2", "3", "4", "5", "6", "7", "8", "9", "10"); + VoteNo(model, "3", TEST_EO_End); +} + +protected static function SubTest_ReconnectVoting_DrawMeansLoss() { + local VotingModel model; + + Context("Testing \"reconnect\" voting when draw means loss."); + model = MakeVotingModel(false); + SetVoters(model, "1", "2", "3", "4", "5", "6", "7", "8", "9", "10"); + VoteYes(model, "1", TEST_EO_Continue); + VoteNo(model, "2", TEST_EO_Continue); + VoteYes(model, "3", TEST_EO_Continue); + VoteNo(model, "4", TEST_EO_Continue); + VoteYes(model, "5", TEST_EO_Continue); + VoteNo(model, "6", TEST_EO_Continue); + // Disconnect 1 3 "yes" voters + SetVoters(model, "2", "4", "5", "6", "7", "8", "9", "10"); + VoteYes(model, "7", TEST_EO_Continue); + VoteYes(model, "9", TEST_EO_Continue); + MakeFaultyNoVote(model, "1", VFR_NotAllowed); + MakeFaultyNoVote(model, "3", VFR_NotAllowed); + // Restore 3 "yes" voter + SetVoters(model, "2", "3", "4", "5", "6", "7", "8", "9", "10"); + VoteNo(model, "8", TEST_EO_Continue); + VoteNo(model, "3", TEST_EO_End); +} + +defaultproperties { + caseGroup = "Commands" + caseName = "Voting model" +} \ No newline at end of file