|
| 1 | +#include <gtest/gtest.h> |
| 2 | +#include "environment/environment.h" |
| 3 | +#include "backend/evaluator/evaluator.h" |
| 4 | +#include "backend/container/block/blockDefs.h" |
| 5 | +#include "backend/blockData/blockDataManager.h" |
| 6 | +#include "computerAPI/directoryManager.h" |
| 7 | + |
| 8 | +class BasicFuzzingEvaluatorTest : public ::testing::TestWithParam<uint64_t> { |
| 9 | +protected: |
| 10 | + void SetUp() override; |
| 11 | + void TearDown() override; |
| 12 | + std::mt19937_64 gen; |
| 13 | + Environment environment {false}; |
| 14 | + SharedCircuit circuit = nullptr; |
| 15 | + SharedEvaluator tEval = nullptr; // testing evaluator |
| 16 | + SharedEvaluator rEval = nullptr; // reference evaluator |
| 17 | +}; |
| 18 | + |
| 19 | +void BasicFuzzingEvaluatorTest::SetUp() { |
| 20 | + gen.seed(GetParam()); |
| 21 | + circuit_id_t circuitId = environment.getBackend().getCircuitManager().createNewCircuit(false); |
| 22 | + circuit = environment.getBackend().getCircuit(circuitId); |
| 23 | + evaluator_id_t evalId = environment.getBackend().createEvaluator(circuitId).value(); |
| 24 | + tEval = environment.getBackend().getEvaluator(evalId); |
| 25 | + ASSERT_TRUE(tEval->isPause()); |
| 26 | +} |
| 27 | + |
| 28 | +void BasicFuzzingEvaluatorTest::TearDown() { |
| 29 | + circuit.reset(); |
| 30 | + tEval.reset(); |
| 31 | + rEval.reset(); |
| 32 | +} |
| 33 | + |
| 34 | +std::optional<connection_end_id_t> getRandomConnectionEnd(const BlockData* blockData, std::mt19937_64& gen, bool wantInput) { |
| 35 | + if (blockData->isDefaultData()) { |
| 36 | + if (wantInput) { |
| 37 | + return connection_end_id_t(0); |
| 38 | + } else { |
| 39 | + return connection_end_id_t(1); |
| 40 | + } |
| 41 | + } |
| 42 | + int numConnections = blockData->getBidirectionalConnectionCount().get(); |
| 43 | + if (wantInput) numConnections += blockData->getInputConnectionCount().get(); |
| 44 | + else numConnections += blockData->getOutputConnectionCount().get(); |
| 45 | + if (numConnections == 0) return std::nullopt; |
| 46 | + std::uniform_int_distribution<int> dist(0, numConnections - 1); |
| 47 | + int index = dist(gen); |
| 48 | + const std::unordered_map<connection_end_id_t, BlockData::ConnectionData>& connections = blockData->getConnections(); |
| 49 | + int i = 0; |
| 50 | + for (auto& pair : connections) { |
| 51 | + if (wantInput) { |
| 52 | + if (pair.second.portType == BlockData::ConnectionData::PortType::INPUT || pair.second.portType == BlockData::ConnectionData::PortType::BIDIRECTIONAL) { |
| 53 | + if (i == index) return pair.first; |
| 54 | + i++; |
| 55 | + } |
| 56 | + } else { |
| 57 | + if (pair.second.portType == BlockData::ConnectionData::PortType::OUTPUT || pair.second.portType == BlockData::ConnectionData::PortType::BIDIRECTIONAL) { |
| 58 | + if (i == index) return pair.first; |
| 59 | + i++; |
| 60 | + } |
| 61 | + } |
| 62 | + } |
| 63 | + return std::nullopt; |
| 64 | +} |
| 65 | + |
| 66 | +TEST_P(BasicFuzzingEvaluatorTest, FuzzInteractions) { |
| 67 | + bool runRealistic = gen() % 2 == 0; |
| 68 | + tEval->setRealistic(runRealistic); |
| 69 | + BlockDataManager& blockDataManager = environment.getBackend().getBlockDataManager(); |
| 70 | + std::uniform_int_distribution<int> distPos(-20, 20); |
| 71 | + std::vector<block_id_t> blockIds; |
| 72 | + int numEditOperations = 5000; |
| 73 | + int numTestOperations = 100; |
| 74 | + int numTicksBetweenTests = 3; |
| 75 | + int numStatesSetPerTest = 50; |
| 76 | + for (int i = 0; i < numEditOperations; ++i) { |
| 77 | + int operation = gen() % 8; // 0,1: place, 2: remove, 3,4,5: connect, 6,7: disconnect |
| 78 | + if (operation <= 1) { // place block |
| 79 | + Orientation orientation(Rotation(gen() % 4), (gen() % 2) == 0); |
| 80 | + BlockType blockType = BlockType((gen() % blockDataManager.maxBlockId()) + 1); |
| 81 | + if (!blockDataManager.blockExists(blockType)) { |
| 82 | + continue; |
| 83 | + } |
| 84 | + Position pos(distPos(gen), distPos(gen)); |
| 85 | + bool success = circuit->tryInsertBlock(pos, orientation, blockType); |
| 86 | + if (!success) continue; |
| 87 | + const Block* block = circuit->getBlockContainer().getBlock(pos); |
| 88 | + ASSERT_NE(block, nullptr); |
| 89 | + blockIds.push_back(block->id()); |
| 90 | + } else if (operation <= 2) { |
| 91 | + if (blockIds.empty()) continue; |
| 92 | + std::uniform_int_distribution<size_t> distIndex(0, blockIds.size() - 1); |
| 93 | + size_t index = distIndex(gen); |
| 94 | + block_id_t blockId = blockIds[index]; |
| 95 | + const Block* block = circuit->getBlockContainer().getBlock(blockId); |
| 96 | + ASSERT_NE(block, nullptr); |
| 97 | + Position pos = block->getPosition(); |
| 98 | + ASSERT_TRUE(circuit->tryRemoveBlock(pos)); |
| 99 | + std::swap(blockIds[index], blockIds.back()); |
| 100 | + blockIds.pop_back(); |
| 101 | + } else if (operation <= 5) { // connect |
| 102 | + if (blockIds.empty()) continue; |
| 103 | + block_id_t blockIdA = blockIds[gen() % blockIds.size()]; |
| 104 | + block_id_t blockIdB = blockIds[gen() % blockIds.size()]; |
| 105 | + const Block* blockA = circuit->getBlockContainer().getBlock(blockIdA); |
| 106 | + const Block* blockB = circuit->getBlockContainer().getBlock(blockIdB); |
| 107 | + ASSERT_NE(blockA, nullptr); |
| 108 | + ASSERT_NE(blockB, nullptr); |
| 109 | + const BlockData* blockDataA = blockDataManager.getBlockData(blockA->type()); |
| 110 | + const BlockData* blockDataB = blockDataManager.getBlockData(blockB->type()); |
| 111 | + ASSERT_NE(blockDataA, nullptr); |
| 112 | + ASSERT_NE(blockDataB, nullptr); |
| 113 | + std::optional<connection_end_id_t> connA = getRandomConnectionEnd(blockDataA, gen, false); |
| 114 | + std::optional<connection_end_id_t> connB = getRandomConnectionEnd(blockDataB, gen, true); |
| 115 | + if (!connA || !connB) continue; |
| 116 | + circuit->tryCreateConnection({blockIdA, *connA}, {blockIdB, *connB}); |
| 117 | + } else if (operation <= 7) { // disconnect |
| 118 | + if (blockIds.empty()) continue; |
| 119 | + block_id_t blockIdA = blockIds[gen() % blockIds.size()]; |
| 120 | + block_id_t blockIdB = blockIds[gen() % blockIds.size()]; |
| 121 | + const Block* blockA = circuit->getBlockContainer().getBlock(blockIdA); |
| 122 | + const Block* blockB = circuit->getBlockContainer().getBlock(blockIdB); |
| 123 | + ASSERT_NE(blockA, nullptr); |
| 124 | + ASSERT_NE(blockB, nullptr); |
| 125 | + const BlockData* blockDataA = blockDataManager.getBlockData(blockA->type()); |
| 126 | + const BlockData* blockDataB = blockDataManager.getBlockData(blockB->type()); |
| 127 | + ASSERT_NE(blockDataA, nullptr); |
| 128 | + ASSERT_NE(blockDataB, nullptr); |
| 129 | + std::optional<connection_end_id_t> connA = getRandomConnectionEnd(blockDataA, gen, false); |
| 130 | + std::optional<connection_end_id_t> connB = getRandomConnectionEnd(blockDataB, gen, true); |
| 131 | + if (!connA || !connB) continue; |
| 132 | + circuit->tryRemoveConnection({blockIdA, *connA}, {blockIdB, *connB}); |
| 133 | + } |
| 134 | + } |
| 135 | + |
| 136 | + // SAVE CIRCUIT TO FILE |
| 137 | + { |
| 138 | + CircuitFileManager& circuitFileManager = environment.getCircuitFileManager(); |
| 139 | + const std::filesystem::path savePath = |
| 140 | + DirectoryManager::getConfigDirectory() / "tmp" / ("BasicFuzzingCircuit_" + std::to_string(GetParam()) + ".cir"); |
| 141 | + std::filesystem::create_directories(savePath.parent_path()); |
| 142 | + ASSERT_TRUE(circuitFileManager.saveToFile(savePath.string(), circuit->getUUID())); |
| 143 | + logInfo("Saved fuzzing circuit to " + savePath.string(), "BasicFuzzingEvaluatorTest"); |
| 144 | + } |
| 145 | + |
| 146 | + logInfo("Creating reference evaluator", "BasicFuzzingEvaluatorTest"); |
| 147 | + evaluator_id_t evalId = environment.getBackend().createEvaluator(circuit->getCircuitId()).value(); |
| 148 | + rEval = environment.getBackend().getEvaluator(evalId); |
| 149 | + rEval->setRealistic(runRealistic); |
| 150 | + ASSERT_TRUE(tEval->isPause()); |
| 151 | + ASSERT_TRUE(rEval->isPause()); |
| 152 | + tEval->resetStates(); |
| 153 | + rEval->resetStates(); |
| 154 | + std::vector<simulator_id_t> simulatorIdsTest; |
| 155 | + std::vector<simulator_id_t> simulatorIdsRef; |
| 156 | + std::unordered_map<block_id_t, Position> blockIdToPosition; |
| 157 | + std::vector<std::string> ps; |
| 158 | + for (block_id_t blockId : blockIds) { |
| 159 | + const Block* block = circuit->getBlockContainer().getBlock(blockId); |
| 160 | + ASSERT_NE(block, nullptr); |
| 161 | + Position pos = block->getPosition(); |
| 162 | + blockIdToPosition[blockId] = pos; |
| 163 | + simulatorIdsTest.push_back(tEval->getBlockSimulatorId(pos)); |
| 164 | + simulatorIdsRef.push_back(rEval->getBlockSimulatorId(pos)); |
| 165 | + ps.push_back("B " + pos.toString()); |
| 166 | + const BlockData* blockData = blockDataManager.getBlockData(block->type()); |
| 167 | + ASSERT_NE(blockData, nullptr); |
| 168 | + if (blockData->isDefaultData()) { |
| 169 | + std::variant<simulator_id_t, std::vector<simulator_id_t>> simIdTest = tEval->getPinSimulatorId(pos); |
| 170 | + std::variant<simulator_id_t, std::vector<simulator_id_t>> simIdRef = rEval->getPinSimulatorId(pos); |
| 171 | + if (std::holds_alternative<simulator_id_t>(simIdTest) && std::holds_alternative<simulator_id_t>(simIdRef)) { |
| 172 | + simulatorIdsTest.push_back(std::get<simulator_id_t>(simIdTest)); |
| 173 | + simulatorIdsRef.push_back(std::get<simulator_id_t>(simIdRef)); |
| 174 | + ps.push_back("P " + pos.toString()); |
| 175 | + } else if (std::holds_alternative<std::vector<simulator_id_t>>(simIdTest) && std::holds_alternative<std::vector<simulator_id_t>>(simIdRef)) { |
| 176 | + std::vector<simulator_id_t>& vecTest = std::get<std::vector<simulator_id_t>>(simIdTest); |
| 177 | + std::vector<simulator_id_t>& vecRef = std::get<std::vector<simulator_id_t>>(simIdRef); |
| 178 | + simulatorIdsTest.insert(simulatorIdsTest.end(), vecTest.begin(), vecTest.end()); |
| 179 | + simulatorIdsRef.insert(simulatorIdsRef.end(), vecRef.begin(), vecRef.end()); |
| 180 | + for (size_t i = 0; i < vecTest.size(); ++i) { |
| 181 | + ps.push_back("P " + pos.toString() + "[" + std::to_string(i) + "]"); |
| 182 | + } |
| 183 | + } else { |
| 184 | + FAIL() << "Mismatched simulator ID types for pin at position " << pos.toString(); |
| 185 | + } |
| 186 | + } else { |
| 187 | + const std::unordered_map<connection_end_id_t, BlockData::ConnectionData>& connections = blockData->getConnections(); |
| 188 | + for (const auto& [connectionId, connectionData] : connections) { |
| 189 | + if (connectionData.portType == BlockData::ConnectionData::PortType::INPUT) { |
| 190 | + continue; |
| 191 | + } |
| 192 | + std::optional<Position> portPositionOpt = block->getConnectionPosition(connectionId); |
| 193 | + ASSERT_TRUE(portPositionOpt.has_value()); |
| 194 | + Position portPosition = portPositionOpt.value(); |
| 195 | + std::variant<simulator_id_t, std::vector<simulator_id_t>> simIdTest = tEval->getPinSimulatorId(portPosition); |
| 196 | + std::variant<simulator_id_t, std::vector<simulator_id_t>> simIdRef = rEval->getPinSimulatorId(portPosition); |
| 197 | + if (std::holds_alternative<simulator_id_t>(simIdTest) && std::holds_alternative<simulator_id_t>(simIdRef)) { |
| 198 | + simulatorIdsTest.push_back(std::get<simulator_id_t>(simIdTest)); |
| 199 | + simulatorIdsRef.push_back(std::get<simulator_id_t>(simIdRef)); |
| 200 | + ps.push_back("P " + portPosition.toString()); |
| 201 | + } else if (std::holds_alternative<std::vector<simulator_id_t>>(simIdTest) && std::holds_alternative<std::vector<simulator_id_t>>(simIdRef)) { |
| 202 | + std::vector<simulator_id_t>& vecTest = std::get<std::vector<simulator_id_t>>(simIdTest); |
| 203 | + std::vector<simulator_id_t>& vecRef = std::get<std::vector<simulator_id_t>>(simIdRef); |
| 204 | + ASSERT_EQ(vecTest.size(), vecRef.size()) << "Mismatched simulator ID vector sizes for pin at position " << portPosition.toString(); |
| 205 | + simulatorIdsTest.insert(simulatorIdsTest.end(), vecTest.begin(), vecTest.end()); |
| 206 | + simulatorIdsRef.insert(simulatorIdsRef.end(), vecRef.begin(), vecRef.end()); |
| 207 | + for (size_t i = 0; i < vecTest.size(); ++i) { |
| 208 | + ps.push_back("P " + portPosition.toString() + "[" + std::to_string(i) + "]"); |
| 209 | + } |
| 210 | + } else { |
| 211 | + FAIL() << "Mismatched simulator ID types for pin at position " << portPosition.toString(); |
| 212 | + } |
| 213 | + } |
| 214 | + } |
| 215 | + } |
| 216 | + |
| 217 | + ASSERT_EQ(simulatorIdsTest.size(), simulatorIdsRef.size()); |
| 218 | + for (int i = 0; i < numTestOperations; ++i) { |
| 219 | + // set random states |
| 220 | + for (int j = 0; j < numStatesSetPerTest; ++j) { |
| 221 | + block_id_t blockId = blockIds[gen() % blockIds.size()]; |
| 222 | + Position pos = blockIdToPosition.at(blockId); |
| 223 | + logic_state_t state = logic_state_t(gen() % 4); |
| 224 | + rEval->setState(pos, state); |
| 225 | + tEval->setState(pos, state); |
| 226 | + } |
| 227 | + |
| 228 | + // compare states |
| 229 | + std::vector<logic_state_t> statesTest = tEval->getStatesFromSimulatorIds(simulatorIdsTest); |
| 230 | + std::vector<logic_state_t> statesRef = rEval->getStatesFromSimulatorIds(simulatorIdsRef); |
| 231 | + ASSERT_EQ(statesTest.size(), statesRef.size()); |
| 232 | + for (size_t k = 0; k < statesTest.size(); ++k) { |
| 233 | + // if (statesTest[k] != statesRef[k]) { |
| 234 | + // simulator_id_t testSimId = simulatorIdsTest[k]; |
| 235 | + // simulator_id_t refSimId = simulatorIdsRef[k]; |
| 236 | + // logInfo("Mismatch at simulator ID index {} (simulator ID test: {}, ref: {}) at p {}", "BasicFuzzingEvaluatorTest", k, testSimId.get(), refSimId.get(), ps.at(k)); |
| 237 | + // int stepBackAmount = 6; |
| 238 | + // for (int m = 0; m < stepBackAmount; ++m) { |
| 239 | + // tEval->stepBack(); |
| 240 | + // rEval->stepBack(); |
| 241 | + // } |
| 242 | + // for (size_t m = 0; m < stepBackAmount; ++m) { |
| 243 | + // logInfo("After stepping back {} ticks:", "BasicFuzzingEvaluatorTest", stepBackAmount - m); |
| 244 | + // logic_state_t stateTest = tEval->getStateFromSimulatorId(testSimId); |
| 245 | + // logic_state_t stateRef = rEval->getStateFromSimulatorId(refSimId); |
| 246 | + // logInfo(" Test evaluator state: {}, Reference evaluator state: {}", "BasicFuzzingEvaluatorTest", logicstate_to_string(stateTest), logicstate_to_string(stateRef)); |
| 247 | + // rEval->stepForward(); |
| 248 | + // tEval->stepForward(); |
| 249 | + // } |
| 250 | + // } |
| 251 | + ASSERT_EQ(statesTest[k], statesRef[k]) << "Mismatch at simulator ID index " << k << " at p " << ps.at(k); |
| 252 | + } |
| 253 | + |
| 254 | + // tick |
| 255 | + tEval->tickStep(numTicksBetweenTests); |
| 256 | + rEval->tickStep(numTicksBetweenTests); |
| 257 | + |
| 258 | + // compare states |
| 259 | + statesTest = tEval->getStatesFromSimulatorIds(simulatorIdsTest); |
| 260 | + statesRef = rEval->getStatesFromSimulatorIds(simulatorIdsRef); |
| 261 | + ASSERT_EQ(statesTest.size(), statesRef.size()); |
| 262 | + for (size_t k = 0; k < statesTest.size(); ++k) { |
| 263 | + // if (statesTest[k] != statesRef[k]) { |
| 264 | + // simulator_id_t testSimId = simulatorIdsTest[k]; |
| 265 | + // simulator_id_t refSimId = simulatorIdsRef[k]; |
| 266 | + // logInfo("Mismatch at simulator ID index {} (simulator ID test: {}, ref: {}) at p {}", "BasicFuzzingEvaluatorTest", k, testSimId.get(), refSimId.get(), ps.at(k)); |
| 267 | + // int stepBackAmount = 6; |
| 268 | + // for (int m = 0; m < stepBackAmount; ++m) { |
| 269 | + // tEval->stepBack(); |
| 270 | + // rEval->stepBack(); |
| 271 | + // } |
| 272 | + // for (size_t m = 0; m < stepBackAmount; ++m) { |
| 273 | + // logInfo("After stepping back {} ticks:", "BasicFuzzingEvaluatorTest", stepBackAmount - m); |
| 274 | + // logic_state_t stateTest = tEval->getStateFromSimulatorId(testSimId); |
| 275 | + // logic_state_t stateRef = rEval->getStateFromSimulatorId(refSimId); |
| 276 | + // logInfo(" Test evaluator state: {}, Reference evaluator state: {}", "BasicFuzzingEvaluatorTest", logicstate_to_string(stateTest), logicstate_to_string(stateRef)); |
| 277 | + // rEval->stepForward(); |
| 278 | + // tEval->stepForward(); |
| 279 | + // } |
| 280 | + // } |
| 281 | + ASSERT_EQ(statesTest[k], statesRef[k]) << "Mismatch at simulator ID index " << k << " at p " << ps.at(k); |
| 282 | + } |
| 283 | + } |
| 284 | +} |
| 285 | + |
| 286 | +INSTANTIATE_TEST_SUITE_P( |
| 287 | + RandomSeeds, |
| 288 | + BasicFuzzingEvaluatorTest, |
| 289 | + ::testing::Range(uint64_t(0), uint64_t(10)) |
| 290 | + // ::testing::Values(uint64_t(3)) |
| 291 | +); |
0 commit comments