From 13cf62cf8ba5f951c86eba085264253a6f13919c Mon Sep 17 00:00:00 2001 From: James Wilson Date: Wed, 11 May 2022 15:40:39 -0700 Subject: [PATCH] Add unit test coverage of stenc output (#67) using stream in functions instead of directly writing to cout --- .gitignore | 4 ++ src/main.cpp | 162 +++++++++++++++++++++++++--------------------- tests/Makefile.am | 5 +- tests/output.cpp | 112 ++++++++++++++++++++++++++++++++ 4 files changed, 208 insertions(+), 75 deletions(-) create mode 100644 tests/output.cpp diff --git a/.gitignore b/.gitignore index 0ee5fa1..f9dfaac 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,10 @@ *.app src/stenc tests/scsi +tests/output + +# Other outputs +man/stenc.1 # dist outputs *.tar.gz diff --git a/src/main.cpp b/src/main.cpp index e8bb955..3c83be3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -99,6 +99,7 @@ static std::optional> key_from_hex_chars(const std::string& return bytes; } +#if !defined(CATCH_CONFIG_MAIN) int main(int argc, const char **argv) { bitcheck bc; memset(&bc, 0, 1); @@ -341,6 +342,7 @@ int main(int argc, const char **argv) { errorOut("Turning encryption off for '" + tapeDrive + "' failed!"); } } +#endif // defined(CATCH_CONFIG_MAIN) // exits to shell with an error message void errorOut(const std::string& message) { @@ -358,28 +360,30 @@ void showUsage() { "Type 'man stenc' for more information.\n"; } +static void print_device_inquiry(std::ostream& os, const SCSI_PAGE_INQ *iresult) +{ + os << std::left << std::setw(25) << "Vendor:"; + os.write((const char *)iresult->vender, 8); + os.put('\n'); + os << std::left << std::setw(25) << "Product ID:"; + os.write((const char *)iresult->productID, 16); + os.put('\n'); + os << std::left << std::setw(25) << "Product Revision:"; + os.write((const char *)iresult->productRev, 4); + os.put('\n'); +} + void inquiryDrive(const std::string& tapeDevice) { // todo: std::cout should not be used outside main() SCSI_PAGE_INQ *const iresult = SCSIGetInquiry(tapeDevice); - std::cout << std::left << std::setw(25) << "Device Mfg:"; - std::cout.write((const char *)iresult->vender, 8); - std::cout << std::endl; - std::cout << std::left << std::setw(25) << "Product ID:"; - std::cout.write((const char *)iresult->productID, 16); - std::cout << std::endl; - std::cout << std::left << std::setw(25) << "Product Revision:"; - std::cout.write((const char *)iresult->productRev, 4); - std::cout << std::endl; - + print_device_inquiry(std::cout, iresult); delete iresult; } -void showDriveStatus(const std::string& tapeDrive, bool detail) { - SSP_DES *opt = SSPGetDES(tapeDrive); - if (opt == NULL) - return; +static void print_device_status(std::ostream& os, const SSP_DES *opt, bool detail) +{ std::string emode = "unknown"; - std::cout << std::left << std::setw(25) << "Drive Encryption:"; + os << std::left << std::setw(25) << "Drive Encryption:"; if ((int)opt->des.encryptionMode == 0x2 && // encrypt (int)opt->des.decryptionMode == 0x2 // read only encrypted data ) @@ -399,58 +403,58 @@ void showDriveStatus(const std::string& tapeDrive, bool detail) { ) emode = "off"; - std::cout << emode << "\n"; + os << emode << "\n"; if (detail) { - std::cout << std::left << std::setw(25) << "Drive Output:"; + os << std::left << std::setw(25) << "Drive Output:"; switch ((int)opt->des.decryptionMode) { case 0x0: - std::cout << "Not decrypting\n"; - std::cout << std::setw(25) << " " - << "Raw encrypted data not outputted\n"; + os << "Not decrypting\n"; + os << std::setw(25) << " " + << "Raw encrypted data not outputted\n"; break; case 0x1: - std::cout << "Not decrypting\n"; - std::cout << std::setw(25) << " " - << "Raw encrypted data outputted\n"; + os << "Not decrypting\n"; + os << std::setw(25) << " " + << "Raw encrypted data outputted\n"; break; case 0x2: - std::cout << "Decrypting\n"; - std::cout << std::setw(25) << " " - << "Unencrypted data not outputted\n"; + os << "Decrypting\n"; + os << std::setw(25) << " " + << "Unencrypted data not outputted\n"; break; case 0x3: - std::cout << "Decrypting\n"; - std::cout << std::setw(25) << " " - << "Unencrypted data outputted\n"; + os << "Decrypting\n"; + os << std::setw(25) << " " + << "Unencrypted data outputted\n"; break; default: - std::cout << "Unknown '0x" << std::hex << (int)opt->des.decryptionMode - << "' \n"; + os << "Unknown '0x" << std::hex << (int)opt->des.decryptionMode + << "' \n"; break; } - std::cout << std::setw(25) << "Drive Input:"; + os << std::setw(25) << "Drive Input:"; switch ((int)opt->des.encryptionMode) { case 0x0: - std::cout << "Not encrypting\n"; + os << "Not encrypting\n"; break; case 0x2: - std::cout << "Encrypting\n"; + os << "Encrypting\n"; break; default: - std::cout << "Unknown result '0x" << std::hex + os << "Unknown result '0x" << std::hex << (int)opt->des.encryptionMode << "'\n"; break; } if (opt->des.RDMD == 1) { - std::cout << std::setw(25) << " " - << "Protecting from raw read\n"; + os << std::setw(25) << " " + << "Protecting from raw read\n"; } - std::cout << std::setw(25) << "Key Instance Counter:" << std::dec + os << std::setw(25) << "Key Instance Counter:" << std::dec << BSLONG(opt->des.keyInstance) << "\n"; if (opt->des.algorithmIndex != 0) { - std::cout << std::setw(25) << "Encryption Algorithm:" << std::hex - << (int)opt->des.algorithmIndex << "\n"; + os << std::setw(25) << "Encryption Algorithm:" << std::hex + << (int)opt->des.algorithmIndex << "\n"; } } if (opt->kads.size() > 0) { @@ -460,60 +464,65 @@ void showDriveStatus(const std::string& tapeDrive, bool detail) { switch (opt->kads[i].type) { case KAD_TYPE_UKAD: lbl << "uKAD): "; - std::cout << std::setw(25) << lbl.str(); - std::cout.write((const char *)&opt->kads[i].descriptor, + os << std::setw(25) << lbl.str(); + os.write((const char *)&opt->kads[i].descriptor, BSSHORT(opt->kads[i].descriptorLength)); - std::cout << std::endl; + os.put('\n'); break; case KAD_TYPE_AKAD: lbl << "aKAD): "; - std::cout << std::setw(25) << lbl.str(); - std::cout.write((const char *)&opt->kads[i].descriptor, + os << std::setw(25) << lbl.str(); + os.write((const char *)&opt->kads[i].descriptor, BSSHORT(opt->kads[i].descriptorLength)); - std::cout << std::endl; + os.put('\n'); break; } } } +} +void showDriveStatus(const std::string& tapeDrive, bool detail) { + SSP_DES *opt = SSPGetDES(tapeDrive); + if (opt == NULL) + return; + + print_device_status(std::cout, opt, detail); delete opt; } -void showVolumeStatus(const std::string& tapeDrive) { - SSP_NBES *opt = SSPGetNBES(tapeDrive, true); - if (opt == NULL) - return; +static void print_volume_status(std::ostream& os, const SSP_NBES *opt) +{ if (opt->nbes.compressionStatus != 0) { - std::cout << std::left << std::setw(25) << "Volume Compressed:"; + os << std::left << std::setw(25) << "Volume Compressed:"; switch (opt->nbes.compressionStatus) { case 0x00: - std::cout << "Drive cannot determine\n"; + os << "Drive cannot determine\n"; break; default: - std::cout << "Unknown result '" << std::hex - << (int)opt->nbes.compressionStatus << "'\n"; + os << "Unknown result '" << std::hex + << (int)opt->nbes.compressionStatus << "'\n"; break; } } - std::cout << std::left << std::setw(25) << "Volume Encryption:"; + os << std::left << std::setw(25) << "Volume Encryption:"; switch ((int)opt->nbes.encryptionStatus) { case 0x01: - std::cout << "Unable to determine\n"; + os << "Unable to determine\n"; break; case 0x02: - std::cout << "Logical block is not a logical block\n"; + os << "Logical block is not a logical block\n"; break; case 0x03: - std::cout << "Not encrypted\n"; + os << "Not encrypted\n"; break; case 0x05: - std::cout << "Encrypted and able to decrypt\n"; + os << "Encrypted and able to decrypt\n"; if (opt->nbes.RDMDS == 1) - std::cout << std::left << std::setw(25) - << " Protected from raw read\n"; + os << std::left << std::setw(25) + << " Protected from raw read\n"; break; case 0x06: - std::cout << "Encrypted, but unable to decrypt due to invalid key.\n"; + os << "Encrypted, but unable to decrypt due to invalid key.\n"; if (opt->kads.size() > 0) { for (unsigned int i = 0; i < opt->kads.size(); i++) { std::stringstream lbl; @@ -521,35 +530,42 @@ void showVolumeStatus(const std::string& tapeDrive) { switch (opt->kads[i].type) { case KAD_TYPE_UKAD: lbl << "uKAD): "; - std::cout << std::setw(25) << lbl.str(); - std::cout.write((const char *)&opt->kads[i].descriptor, + os << std::setw(25) << lbl.str(); + os.write((const char *)&opt->kads[i].descriptor, BSSHORT(opt->kads[i].descriptorLength)); - std::cout << std::endl; + os.put('\n'); break; case KAD_TYPE_AKAD: lbl << "aKAD): "; - std::cout << std::setw(25) << lbl.str(); - std::cout.write((const char *)&opt->kads[i].descriptor, + os << std::setw(25) << lbl.str(); + os.write((const char *)&opt->kads[i].descriptor, BSSHORT(opt->kads[i].descriptorLength)); - std::cout << std::endl; + os.put('\n'); break; } } } if (opt->nbes.RDMDS == 1) - std::cout << std::left << std::setw(25) << " Protected from raw read\n"; + os << std::left << std::setw(25) << " Protected from raw read\n"; break; default: - std::cout << "Unknown result '" << std::hex - << (int)opt->nbes.encryptionStatus << "'\n"; + os << "Unknown result '" << std::hex + << (int)opt->nbes.encryptionStatus << "'\n"; break; } if (opt->nbes.algorithmIndex != 0) { - std::cout << std::left << std::setw(25) - << "Volume Algorithm:" << (int)opt->nbes.algorithmIndex << "\n"; + os << std::left << std::setw(25) + << "Volume Algorithm:" << (int)opt->nbes.algorithmIndex << "\n"; } +} +void showVolumeStatus(const std::string& tapeDrive) { + SSP_NBES *opt = SSPGetNBES(tapeDrive, true); + if (opt == NULL) + return; + + print_volume_status(std::cout, opt); delete opt; } diff --git a/tests/Makefile.am b/tests/Makefile.am index 0883c01..1037149 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,4 +1,5 @@ AM_CPPFLAGS=-std=c++17 -I${top_srcdir}/src -TESTS=scsi -check_PROGRAMS=scsi +TESTS=scsi output +check_PROGRAMS=scsi output scsi_SOURCES=catch.hpp scsi.cpp ${top_srcdir}/src/scsiencrypt.cpp +output_SOURCES=catch.hpp output.cpp ${top_srcdir}/src/scsiencrypt.cpp diff --git a/tests/output.cpp b/tests/output.cpp new file mode 100644 index 0000000..549535e --- /dev/null +++ b/tests/output.cpp @@ -0,0 +1,112 @@ +#define CATCH_CONFIG_MAIN +#include "catch.hpp" + +#include +#include +#include + +#include "config.h" +#include "main.cpp" + +using namespace std::literals::string_literals; + +/** + * Compare the output of stenc given device responses + * + * These tests check the representation and interpretation of raw device data + * and that the program output accurately reports the meaning of the data. + */ +TEST_CASE("Test SCSI inquiry output", "[output]") +{ + const uint8_t response[] { + 0x01, 0x80, 0x00, 0x02, 0x5b, 0x00, 0x00, 0x02, + 0x41, 0x43, 0x4d, 0x45, 0x20, 0x20, 0x20, 0x20, + 0x55, 0x6c, 0x74, 0x72, 0x69, 0x75, 0x6d, 0x2d, + 0x31, 0x30, 0x30, 0x30, 0x20, 0x20, 0x20, 0x20, + 0x31, 0x32, 0x33, 0x34, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + // note: fixed width strings in output + const std::string expected_output {"\ +Vendor: ACME \n\ +Product ID: Ultrium-1000 \n\ +Product Revision: 1234\n"s}; + std::ostringstream oss; + print_device_inquiry(oss, reinterpret_cast(response)); + REQUIRE(oss.str() == expected_output); +} + +TEST_CASE("SCSI get device encryption status output 1", "[output]") +{ + const uint8_t page[] { + 0x00, 0x20, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + const std::string expected_output {"\ +Drive Encryption: off\n\ +Drive Output: Not decrypting\n\ + Raw encrypted data not outputted\n\ +Drive Input: Not encrypting\n\ +Key Instance Counter: 0\n"s}; + std::ostringstream oss; + print_device_status(oss, std::make_unique(reinterpret_cast(page)).get(), true); + REQUIRE(oss.str() == expected_output); +} + +TEST_CASE("SCSI get device encryption status output 2", "[output]") +{ + const uint8_t page[] { + 0x00, 0x20, 0x00, 0x24, 0x42, 0x02, 0x02, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0c, 0x48, 0x65, 0x6c, 0x6c, + 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21, + }; + const std::string expected_output {"\ +Drive Encryption: on\n\ +Drive Output: Decrypting\n\ + Unencrypted data not outputted\n\ +Drive Input: Encrypting\n\ +Key Instance Counter: 1\n\ +Encryption Algorithm: 1\n\ +Drive Key Desc.(uKAD): Hello world!\n"s}; + std::ostringstream oss; + print_device_status(oss, std::make_unique(reinterpret_cast(page)).get(), true); + REQUIRE(oss.str() == expected_output); +} + +TEST_CASE("Test SCSI get next block encryption status output 1", "[output]") +{ + const uint8_t page[] { + 0x00, 0x21, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + }; + const std::string expected_output {"\ +Volume Encryption: Not encrypted\n"s}; + std::ostringstream oss; + print_volume_status(oss, std::make_unique(reinterpret_cast(page)).get()); + REQUIRE(oss.str() == expected_output); +} + +TEST_CASE("Test SCSI get next block encryption status output 2", "[output]") +{ + const uint8_t page[] { + 0x00, 0x21, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x01, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x0c, 0x48, 0x65, 0x6c, 0x6c, + 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21, + }; + const std::string expected_output {"\ +Volume Encryption: Encrypted and able to decrypt\n\ +Volume Algorithm: 1\n"s}; + std::ostringstream oss; + print_volume_status(oss, std::make_unique(reinterpret_cast(page)).get()); + REQUIRE(oss.str() == expected_output); +}