bgneal@4: // Copyright (C) 2012 by Brian Neal. bgneal@4: // This file is part of Cpp-Enigma, the Enigma Machine simulation. bgneal@4: // Cpp-Enigma is released under the MIT License (see License.txt). bgneal@4: // bgneal@4: // test_machine.t.h - Unit tests for the enigma_machine class. bgneal@4: bgneal@4: #include bgneal@4: #include bgneal@4: #include bgneal@4: #include bgneal@4: #include "machine.h" bgneal@4: #include "enigma_utils.h" bgneal@4: bgneal@4: using namespace enigma; bgneal@4: bgneal@4: bgneal@7: class display_suite : public CxxTest::TestSuite bgneal@7: { bgneal@7: public: bgneal@7: bgneal@7: void test_set_display3() bgneal@7: { bgneal@7: enigma_machine m({"II", "IV", "V"}, {}, "B"); bgneal@7: TS_ASSERT_THROWS(m.set_display("ABCD"), enigma_machine_error); bgneal@7: TS_ASSERT_THROWS_NOTHING(m.set_display("ABC")); bgneal@7: } bgneal@7: bgneal@7: void test_set_display4() bgneal@7: { bgneal@7: enigma_machine m({"Gamma", "II", "IV", "V"}, {}, "B-Thin"); bgneal@7: TS_ASSERT_THROWS(m.set_display("BCD"), enigma_machine_error); bgneal@7: TS_ASSERT_THROWS_NOTHING(m.set_display("ABCD")); bgneal@7: } bgneal@8: bgneal@8: void test_navy_str() bgneal@8: { bgneal@8: const std::string stecker = "1/20 2/12 4/6 7/10 8/13 14/23 15/16 17/25 18/26 22/24"; bgneal@8: enigma_machine machine({"Beta", "II", "IV", "I"}, {0, 0, 0, 21}, "B-Thin", stecker); bgneal@8: bgneal@8: TS_ASSERT_EQUALS(machine.navy_str(), "B-Thin Beta/0 II/0 IV/0 I/21 AAAA " bgneal@8: "1/20 2/12 4/6 7/10 8/13 14/23 15/16 17/25 18/26 22/24"); bgneal@8: } bgneal@8: bgneal@8: void test_army_str() bgneal@8: { bgneal@8: enigma_machine machine({"II", "IV", "V"}, {1, 20, 11}, "B", bgneal@8: "AV BS CG DL FU HZ IN KM OW RX"); bgneal@8: bgneal@8: TS_ASSERT_EQUALS(machine.army_str(), "B II/1 IV/20 V/11 AAA " bgneal@8: "AV BS CG DL FU HZ IN KM OW RX"); bgneal@8: } bgneal@7: }; bgneal@7: bgneal@4: class stepping_test_suite : public CxxTest::TestSuite bgneal@4: { bgneal@4: public: bgneal@4: bgneal@4: void test_double_stepping() bgneal@4: { bgneal@4: // Ensure the rotors step realistically by testing for a "double-step" bgneal@4: // This example taken from bgneal@4: // http://users.telenet.be/d.rijmenants/en/enigmatech.htm bgneal@4: // in the section on "The Stepping Mechanism." bgneal@4: bgneal@4: enigma_machine m({"III", "II", "I"}, {}); bgneal@4: m.set_display('K', 'D', 'O'); bgneal@4: bgneal@4: const std::vector truth_data = { bgneal@4: "KDP", "KDQ", "KER", "LFS", "LFT", "LFU", bgneal@4: }; bgneal@4: bgneal@4: for (const auto& expected : truth_data) bgneal@4: { bgneal@4: m.key_press('A'); bgneal@4: TS_ASSERT_EQUALS(m.get_display(), expected); bgneal@4: } bgneal@4: } bgneal@4: }; bgneal@4: bgneal@4: bgneal@4: class simple_cipher_test_suite : public CxxTest::TestSuite bgneal@4: { bgneal@4: public: bgneal@4: bgneal@4: void setUp() bgneal@4: { bgneal@4: m.reset(new enigma_machine({"I", "II", "III"}, {})); bgneal@4: m->set_display('A', 'A', 'A'); bgneal@4: plaintext = "AAAAA"; bgneal@4: ciphertext = "BDZGO"; bgneal@4: } bgneal@4: bgneal@4: void test_simple_encrypt() bgneal@4: { bgneal@4: std::vector buffer(plaintext.size()); bgneal@4: m->process_text(plaintext.c_str(), buffer.data(), plaintext.size()); bgneal@4: TS_ASSERT_EQUALS(ciphertext, std::string(buffer.begin(), buffer.end())); bgneal@4: } bgneal@4: bgneal@4: void test_simple_decrypt() bgneal@4: { bgneal@4: std::vector buffer(plaintext.size()); bgneal@4: m->process_text(ciphertext.c_str(), buffer.data(), ciphertext.size()); bgneal@4: TS_ASSERT_EQUALS(plaintext, std::string(buffer.begin(), buffer.end())); bgneal@4: } bgneal@4: bgneal@4: private: bgneal@4: std::unique_ptr m; bgneal@4: std::string plaintext; bgneal@4: std::string ciphertext; bgneal@4: }; bgneal@4: bgneal@4: bgneal@4: // This example taken from Dirk Rijmenants' simulator manual. bgneal@4: // bgneal@4: // It is credited to Frode Weierud and Geoff Sullivan. bgneal@4: // http://cryptocellar.com bgneal@4: // bgneal@4: class actual_decrypt_test_suite : public CxxTest::TestSuite bgneal@4: { bgneal@4: public: bgneal@4: void setUp() bgneal@4: { bgneal@4: m.reset(new enigma_machine({"II", "IV", "V"}, {1, 20, 11}, "B", bgneal@4: "AV BS CG DL FU HZ IN KM OW RX")); bgneal@4: } bgneal@4: bgneal@4: void decrypt(const std::string& start, bgneal@4: const std::string& enc_key, bgneal@4: const std::string& ciphertext, bgneal@4: const std::string& truth_data) bgneal@4: { bgneal@4: // remove spaces & Kenngruppen from ciphertext bgneal@4: std::string ctext(ciphertext.begin() + 5, ciphertext.end()); bgneal@4: ctext = remove_spaces(ctext); bgneal@4: bgneal@4: // remove spaces from truth_data bgneal@4: const std::string expected(remove_spaces(truth_data)); bgneal@4: bgneal@4: // decrypt message key to get start position bgneal@4: m->set_display(start[0], start[1], start[2]); bgneal@4: std::string key = m->process_text(enc_key); bgneal@4: bgneal@4: // decrypt the message with the key bgneal@4: m->set_display(key[0], key[1], key[2]); bgneal@4: bgneal@4: const std::string plaintext = m->process_text(ctext); bgneal@4: bgneal@4: TS_ASSERT_EQUALS(plaintext, expected); bgneal@4: } bgneal@4: bgneal@4: void test_decrpyt_1() bgneal@4: { bgneal@4: const std::string ciphertext = bgneal@4: "RFUGZ EDPUD NRGYS ZRCXN" bgneal@4: "UYTPO MRMBO FKTBZ REZKM" bgneal@4: "LXLVE FGUEY SIOZV EQMIK" bgneal@4: "UBPMM YLKLT TDEIS MDICA" bgneal@4: "GYKUA CTCDO MOHWX MUUIA" bgneal@4: "UBSTS LRNBZ SZWNR FXWFY" bgneal@4: "SSXJZ VIJHI DISHP RKLKA" bgneal@4: "YUPAD TXQSP INQMA TLPIF" bgneal@4: "SVKDA SCTAC DPBOP VHJK"; bgneal@4: bgneal@4: const std::string truth_data = bgneal@4: "AUFKL XABTE ILUNG XVONX" bgneal@4: "KURTI NOWAX KURTI NOWAX" bgneal@4: "NORDW ESTLX SEBEZ XSEBE" bgneal@4: "ZXUAF FLIEG ERSTR ASZER" bgneal@4: "IQTUN GXDUB ROWKI XDUBR" bgneal@4: "OWKIX OPOTS CHKAX OPOTS" bgneal@4: "CHKAX UMXEI NSAQT DREIN" bgneal@4: "ULLXU HRANG ETRET ENXAN" bgneal@4: "GRIFF XINFX RGTX"; bgneal@4: bgneal@4: decrypt("WXC", "KCH", ciphertext, truth_data); bgneal@4: } bgneal@4: bgneal@4: void test_decrpyt_2() bgneal@4: { bgneal@4: const std::string ciphertext = bgneal@4: "FNJAU SFBWD NJUSE GQOBH" bgneal@4: "KRTAR EEZMW KPPRB XOHDR" bgneal@4: "OEQGB BGTQV PGVKB VVGBI" bgneal@4: "MHUSZ YDAJQ IROAX SSSNR" bgneal@4: "EHYGG RPISE ZBOVM QIEMM" bgneal@4: "ZCYSG QDGRE RVBIL EKXYQ" bgneal@4: "IRGIR QNRDN VRXCY YTNJR"; bgneal@4: bgneal@4: const std::string truth_data = bgneal@4: "DREIG EHTLA NGSAM ABERS" bgneal@4: "IQERV ORWAE RTSXE INSSI" bgneal@4: "EBENN ULLSE QSXUH RXROE" bgneal@4: "MXEIN SXINF RGTXD REIXA" bgneal@4: "UFFLI EGERS TRASZ EMITA" bgneal@4: "NFANG XEINS SEQSX KMXKM" bgneal@4: "XOSTW XKAME NECXK"; bgneal@4: bgneal@4: decrypt("CRS", "YPJ", ciphertext, truth_data); bgneal@4: } bgneal@4: bgneal@4: private: bgneal@4: std::unique_ptr m; bgneal@4: }; bgneal@4: bgneal@4: bgneal@4: // This is the Kriegsmarine example from Dirk Rijmenants' simulator manual. bgneal@4: // bgneal@4: // It is credited to Stefan Krah and the M4 project: bgneal@4: // http://www.bytereef.org/m4_project.html bgneal@4: // bgneal@4: class kriegsmarine_test_suite : public CxxTest::TestSuite bgneal@4: { bgneal@4: public: bgneal@4: bgneal@4: void test_decrypt() bgneal@4: { bgneal@4: const std::string stecker = "1/20 2/12 4/6 7/10 8/13 14/23 15/16 17/25 18/26 22/24"; bgneal@4: bgneal@4: enigma_machine machine({"Beta", "II", "IV", "I"}, {0, 0, 0, 21}, "B-Thin", stecker); bgneal@4: bgneal@4: std::string ciphertext = remove_spaces( bgneal@4: "FCLC QRKN NCZW VUSX PNYM INHZ XMQX SFWX WLKJ AHSH NMCO CCAK UQPM KCSM" bgneal@4: "HKSE INJU SBLK IOSX CKUB HMLL XCSJ USRR DVKO HULX WCCB GVLI YXEO AHXR" bgneal@4: "HKKF VDRE WEZL XOBA FGYU JQUK GRTV UKAM EURB VEKS UHHV OYHA BCJW MAKL" bgneal@4: "FKLM YFVN RIZR VVRT KOFD ANJM OLBG FFLE OPRG TFLV RHOW OPBE KVWM UQFM" bgneal@4: "PWPA RMFH AGKX IIBG FCLC QRKM VA"); bgneal@4: bgneal@4: // remove the message indicators from the message (the first and last 2 bgneal@4: // groups of the message -- it appears the last partial group 'VA' should bgneal@4: // be removed also) bgneal@4: bgneal@4: ciphertext = std::string(ciphertext.begin() + 8, ciphertext.end() - 10); bgneal@4: bgneal@4: machine.set_display('V', 'J', 'N', 'A'); bgneal@4: const std::string plaintext = machine.process_text(ciphertext); bgneal@4: bgneal@4: const std::string truth_data = remove_spaces( bgneal@4: "VONV ONJL OOKS JHFF TTTE" bgneal@4: "INSE INSD REIZ WOYY QNNS" bgneal@4: "NEUN INHA LTXX BEIA NGRI" bgneal@4: "FFUN TERW ASSE RGED RUEC" bgneal@4: "KTYW ABOS XLET ZTER GEGN" bgneal@4: "ERST ANDN ULAC HTDR EINU" bgneal@4: "LUHR MARQ UANT ONJO TANE" bgneal@4: "UNAC HTSE YHSD REIY ZWOZ" bgneal@4: "WONU LGRA DYAC HTSM YSTO" bgneal@4: "SSEN ACHX EKNS VIER MBFA" bgneal@4: "ELLT YNNN NNNO OOVI ERYS" bgneal@4: "ICHT EINS NULL"); bgneal@4: bgneal@4: TS_ASSERT_EQUALS(plaintext, truth_data); bgneal@4: } bgneal@4: bgneal@4: }; bgneal@16: bgneal@16: class ring_settings_test_suite : public CxxTest::TestSuite bgneal@16: { bgneal@16: public: bgneal@16: bgneal@16: void test_ring_settings() bgneal@16: { bgneal@16: enigma_machine machine({"Beta", "II", "IV", "I"}, {0, 2, 8, 21}, "B-Thin"); bgneal@16: bgneal@16: TS_ASSERT_EQUALS(machine.get_ring_setting(0), 0); bgneal@16: TS_ASSERT_EQUALS(machine.get_ring_setting(1), 2); bgneal@16: TS_ASSERT_EQUALS(machine.get_ring_setting(2), 8); bgneal@16: TS_ASSERT_EQUALS(machine.get_ring_setting(3), 21); bgneal@16: bgneal@16: std::vector rings(machine.get_ring_settings()); bgneal@16: std::vector expected{ 0, 2, 8, 21 }; bgneal@16: TS_ASSERT_EQUALS(rings, expected); bgneal@16: bgneal@16: machine.set_ring_setting(0, 25); bgneal@16: TS_ASSERT_EQUALS(machine.get_ring_setting(0), 25); bgneal@16: TS_ASSERT_EQUALS(machine.get_ring_setting(1), 2); bgneal@16: TS_ASSERT_EQUALS(machine.get_ring_setting(2), 8); bgneal@16: TS_ASSERT_EQUALS(machine.get_ring_setting(3), 21); bgneal@16: bgneal@16: expected = { 25, 2, 8, 21 }; bgneal@16: TS_ASSERT_EQUALS(machine.get_ring_settings(), expected); bgneal@16: bgneal@16: machine.set_ring_setting(1, 18); bgneal@16: TS_ASSERT_EQUALS(machine.get_ring_setting(0), 25); bgneal@16: TS_ASSERT_EQUALS(machine.get_ring_setting(1), 18); bgneal@16: TS_ASSERT_EQUALS(machine.get_ring_setting(2), 8); bgneal@16: TS_ASSERT_EQUALS(machine.get_ring_setting(3), 21); bgneal@16: bgneal@16: expected = { 25, 18, 8, 21 }; bgneal@16: TS_ASSERT_EQUALS(machine.get_ring_settings(), expected); bgneal@16: bgneal@16: machine.set_ring_setting(2, 11); bgneal@16: TS_ASSERT_EQUALS(machine.get_ring_setting(0), 25); bgneal@16: TS_ASSERT_EQUALS(machine.get_ring_setting(1), 18); bgneal@16: TS_ASSERT_EQUALS(machine.get_ring_setting(2), 11); bgneal@16: TS_ASSERT_EQUALS(machine.get_ring_setting(3), 21); bgneal@16: bgneal@16: expected = { 25, 18, 11, 21 }; bgneal@16: TS_ASSERT_EQUALS(machine.get_ring_settings(), expected); bgneal@16: bgneal@16: machine.set_ring_setting(3, 3); bgneal@16: TS_ASSERT_EQUALS(machine.get_ring_setting(0), 25); bgneal@16: TS_ASSERT_EQUALS(machine.get_ring_setting(1), 18); bgneal@16: TS_ASSERT_EQUALS(machine.get_ring_setting(2), 11); bgneal@16: TS_ASSERT_EQUALS(machine.get_ring_setting(3), 3); bgneal@16: bgneal@16: expected = { 25, 18, 11, 3 }; bgneal@16: TS_ASSERT_EQUALS(machine.get_ring_settings(), expected); bgneal@16: bgneal@16: expected = { 8, 9, 10, 11 }; bgneal@16: machine.set_ring_settings(expected); bgneal@16: TS_ASSERT_EQUALS(machine.get_ring_settings(), expected); bgneal@16: } bgneal@16: };