diff --git a/ci_cd/.gitlab-ci.yml b/ci_cd/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..dd2181c397966c11e670fe2dd5de38ae89396cc4 --- /dev/null +++ b/ci_cd/.gitlab-ci.yml @@ -0,0 +1,13 @@ +stages: + - prebuild + - compile + - test + - extra + +include: + - local: 'ci_cd/problem3a.yml' + - local: 'ci_cd/problem3b.yml' + - local: 'ci_cd/problem3c.yml' + +default: + timeout: 5m diff --git a/ci_cd/problem3a.yml b/ci_cd/problem3a.yml new file mode 100644 index 0000000000000000000000000000000000000000..159c3c3a6d7657f29d253fd2ed4e2bd8e035b434 --- /dev/null +++ b/ci_cd/problem3a.yml @@ -0,0 +1,59 @@ +prebuild_problem_3a: + stage: prebuild + script: + - | + # Check if source files exist + if [ ! -f "impl/Device.cpp" ]; then + echo "Device.cpp does not exist under impl directory"; + exit 1; + fi + if [ ! -f "impl/Password.cpp" ]; then + echo "Password.cpp does not exist under impl directory"; + exit 1; + fi + if [ ! -f "impl/Service.cpp" ]; then + echo "Service.cpp does not exist under impl directory"; + exit 1; + fi + if [ ! -f "impl/System.cpp" ]; then + echo "System.cpp does not exist under impl directory"; + exit 1; + fi + - git clone https://agile.bu.edu/gitlab/configs/ec330/homeworks/homeworkfive.git hw5 + artifacts: + paths: + - hw5/ + rules: + - if: '$CI_COMMIT_REF_NAME == "problem3a"' + tags: [c++-17] + +compile_problem_3a: + stage: compile + needs: + - job: prebuild_problem_3a + artifacts: true + script: + - cp impl/*.cpp hw5/tests/impl + - cd hw5/tests + - make problem3a + artifacts: + paths: + - hw5/ + rules: + - if: '$CI_COMMIT_REF_NAME == "problem3a"' + tags: [c++-17] + +exec_problem_3a: + stage: test + needs: + - job: compile_problem_3a + artifacts: true + script: + - cd hw5/tests + - ./problem3a + artifacts: + paths: + - hw5/ + rules: + - if: '$CI_COMMIT_REF_NAME == "problem3a"' + tags: [c++-17] \ No newline at end of file diff --git a/ci_cd/problem3b.yml b/ci_cd/problem3b.yml new file mode 100644 index 0000000000000000000000000000000000000000..ce525311a4cbe476cf95cbc10ec57da4ecdc42a6 --- /dev/null +++ b/ci_cd/problem3b.yml @@ -0,0 +1,63 @@ +prebuild_problem_3b: + stage: prebuild + script: + - | + # Check if source files exist + if [ ! -f "impl/Device.cpp" ]; then + echo "Device.cpp does not exist under impl directory"; + exit 1; + fi + if [ ! -f "impl/Password.cpp" ]; then + echo "Password.cpp does not exist under impl directory"; + exit 1; + fi + if [ ! -f "impl/Service.cpp" ]; then + echo "Service.cpp does not exist under impl directory"; + exit 1; + fi + if [ ! -f "impl/System.cpp" ]; then + echo "System.cpp does not exist under impl directory"; + exit 1; + fi + if [ ! -f "impl/MakeSecure.cpp" ]; then + echo "MakeSecure.cpp does not exist under impl directory"; + exit 1; + fi + - git clone https://agile.bu.edu/gitlab/configs/ec330/homeworks/homeworkfive.git hw5 + artifacts: + paths: + - hw5/ + rules: + - if: '$CI_COMMIT_REF_NAME == "problem3b"' + tags: [c++-17] + +compile_problem_3b: + stage: compile + needs: + - job: prebuild_problem_3b + artifacts: true + script: + - cp impl/*.cpp hw5/tests/impl + - cd hw5/tests + - make problem3b + artifacts: + paths: + - hw5/ + rules: + - if: '$CI_COMMIT_REF_NAME == "problem3b"' + tags: [c++-17] + +exec_problem_3b: + stage: test + needs: + - job: compile_problem_3b + artifacts: true + script: + - cd hw5/tests + - ./problem3b + artifacts: + paths: + - hw5/ + rules: + - if: '$CI_COMMIT_REF_NAME == "problem3b"' + tags: [c++-17] \ No newline at end of file diff --git a/ci_cd/problem3c.yml b/ci_cd/problem3c.yml new file mode 100644 index 0000000000000000000000000000000000000000..c40b3a467f45c4bf4a9883c44e487901ef1a91dd --- /dev/null +++ b/ci_cd/problem3c.yml @@ -0,0 +1,63 @@ +prebuild_problem_3c: + stage: prebuild + script: + - | + # Check if source files exist + if [ ! -f "impl/Device.cpp" ]; then + echo "Device.cpp does not exist under impl directory"; + exit 1; + fi + if [ ! -f "impl/Password.cpp" ]; then + echo "Password.cpp does not exist under impl directory"; + exit 1; + fi + if [ ! -f "impl/Service.cpp" ]; then + echo "Service.cpp does not exist under impl directory"; + exit 1; + fi + if [ ! -f "impl/System.cpp" ]; then + echo "System.cpp does not exist under impl directory"; + exit 1; + fi + if [ ! -f "impl/MakeSecure.cpp" ]; then + echo "MakeSecure.cpp does not exist under impl directory"; + exit 1; + fi + - git clone https://agile.bu.edu/gitlab/configs/ec330/homeworks/homeworkfive.git hw5 + artifacts: + paths: + - hw5/ + rules: + - if: '$CI_COMMIT_REF_NAME == "problem3c"' + tags: [c++-17] + +compile_problem_3c: + stage: compile + needs: + - job: prebuild_problem_3c + artifacts: true + script: + - cp impl/*.cpp hw5/tests/impl + - cd hw5/tests + - make problem3c + artifacts: + paths: + - hw5/ + rules: + - if: '$CI_COMMIT_REF_NAME == "problem3c"' + tags: [c++-17] + +exec_problem_3c: + stage: test + needs: + - job: compile_problem_3c + artifacts: true + script: + - cd hw5/tests + - ./problem3c + artifacts: + paths: + - hw5/ + rules: + - if: '$CI_COMMIT_REF_NAME == "problem3c"' + tags: [c++-17] \ No newline at end of file diff --git a/impl/Service.cpp b/impl/Service.cpp index c3bc6b7cbeb80c935a38a099c9ab35297b308f13..817b2098625dc3ddc8f811dfd5393964bec4eb42 100644 --- a/impl/Service.cpp +++ b/impl/Service.cpp @@ -7,13 +7,13 @@ int Service::globalServiceID = 0; // IDs start at 0 void Service::addDevice(Device &dev) { - // ... + // ... } const unordered_set &Service::getDevices() const { - // ... + // ... } bool Service::hasDevice(Device &dev) const { - // ... + // ... } diff --git a/impl/System.cpp b/impl/System.cpp index 3d0251cfa557cad61b1868ee45e7e5229f7b6b87..46054443f6b264f587eae4a88e849e7570a6da13 100644 --- a/impl/System.cpp +++ b/impl/System.cpp @@ -4,22 +4,22 @@ #include "../include/System.h" void System::addService(const Service& newSvc) { - // ... + // ... } bool System::isSecureQ() const { - // ... + // ... } unordered_set System::getAllDevices() const { - // ... + // ... } unordered_set System::getServices() const { - // ... + // ... } long System::numPasswords() const { - // ... + // ... } diff --git a/include/System.h b/include/System.h index e3ee49607cbaf63c772788a3b8d2d6cc12a9a047..70df97234ca51c8484dc47d15706435011f2fc8a 100644 --- a/include/System.h +++ b/include/System.h @@ -4,11 +4,17 @@ #ifndef HW5_ADMIN_SYSTEM_H #define HW5_ADMIN_SYSTEM_H + #include #include +#include +#include +#include #include "Service.h" +using namespace std; + /** * Represents a system of devices and their servicess. */ diff --git a/tests/Makefile b/tests/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..03212673c654970b917bde484f0fb0ba651bb2d8 --- /dev/null +++ b/tests/Makefile @@ -0,0 +1,45 @@ +# Makefile, generated with support of ChatGPT + +# Compiler and flags +CXX = g++ -O2 +CXXFLAGS = -Wall -std=c++17 + +# Source files +PROBLEM3A_SRCS = test_3a.cpp impl/Device.cpp impl/Password.cpp impl/Service.cpp impl/System.cpp +PROBLEM3B_SRCS = test_3b.cpp impl/Device.cpp impl/Password.cpp impl/Service.cpp impl/System.cpp +PROBLEM3C_SRCS = test_3c.cpp impl/Device.cpp impl/Password.cpp impl/Service.cpp impl/System.cpp +SRCS = $(PROBLEM3A_SRCS) $(PROBLEM3B_SRCS) $(PROBLEM3C_SRCS) + +# Object files +PROBLEM3A_OBJS = test_3a.o impl/Device.o impl/Password.o impl/Service.o impl/System.o +PROBLEM3B_OBJS = test_3b.o impl/Device.o impl/Password.o impl/Service.o impl/System.o +PROBLEM3C_OBJS = test_3c.o impl/Device.o impl/Password.o impl/Service.o impl/System.o +OBJS = $(PROBLEM3A_OBJS) $(PROBLEM3B_OBJS) $(PROBLEM3C_OBJS) + +# Executable names +PROBLEM3A_EXEC = problem3a +PROBLEM3B_EXEC = problem3b +PROBLEM3C_EXEC = problem3c +EXECS = $(PROBLEM3A_EXEC) $(PROBLEM3B_EXEC) $(PROBLEM3C_EXEC) + +# Default target to build the executable +all: $(PROBLEM3A_EXEC) $(PROBLEM3B_EXEC) $(PROBLEM3C_EXEC) + +# Rule to build the executable from object files +$(PROBLEM3A_EXEC): $(PROBLEM3A_OBJS) Makefile + $(CXX) $(CXXFLAGS) -o $(PROBLEM3A_EXEC) $(PROBLEM3A_OBJS) + +$(PROBLEM3B_EXEC): $(PROBLEM3B_OBJS) Makefile + $(CXX) $(CXXFLAGS) -o $(PROBLEM3B_EXEC) $(PROBLEM3B_OBJS) + +$(PROBLEM3C_EXEC): $(PROBLEM3C_OBJS) Makefile + $(CXX) $(CXXFLAGS) -o $(PROBLEM3C_EXEC) $(PROBLEM3C_OBJS) + +# Rules to build object files from source files, with dependency on the Common.h header +%.o: %.cpp Makefile + $(CXX) $(CXXFLAGS) -c $< -o $@ + +# Clean target to remove compiled files +clean: + rm -f $(OBJS) $(EXEC) + diff --git a/tests/impl/placeholder b/tests/impl/placeholder new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/include/Device.h b/tests/include/Device.h new file mode 100644 index 0000000000000000000000000000000000000000..758abb5274a97ac65e5c5407e421de4ec12b1d3d --- /dev/null +++ b/tests/include/Device.h @@ -0,0 +1,101 @@ +// +// Created by Ari Trachtenberg on 3/17/26. +// + +#ifndef HW5_ADMIN_MACHINE_H +#define HW5_ADMIN_MACHINE_H +#include +#include + +#include "Password.h" + +/** + * Represents one machine on the network. + */ +class Device { + public: + // CONSTRUCTORS + explicit Device(Password pwd) : loginPassword(std::move(pwd)) { myID = globalDeviceID++; } + + /** + * Devices cannot be constructed without a password. + */ + Device() = delete; + + /** + * Devices cannot be copied. + */ + Device(const Device &other) = delete; + + // MODIFIERS + /** + * Change the device's password to \ref newPwd. + * @param newPwd The new password for the device. + */ + void changePassword(const Password &newPwd) { loginPassword = newPwd; } + + /** + * Changes the current Device's password to match that of \ref dev. + * @param dev The Device whose password should be copied to this object. + */ + void changePassword(const Device &dev) { loginPassword = dev.loginPassword; } + + // COMPARISONS + /** + * Compares Devices by their unique ID. + * @return true iff this Device equals \ref otherDev + */ + bool operator==(const Device &otherDev) const { return myID == otherDev.myID; } + + /** + * Relatively compares Devices by some fixed, but arbitrary, measure. + * @return iff this Device is less than \ref otherDev by some fixed by arbitrary measure. + */ + bool operator<(const Device &other) const { return myID < other.myID; } + + /** + * @return true iff Device \ref devA and Device \ref devB have the same password. + */ + static bool samePassword(const Device &devA, const Device &devB) { + return devA.loginPassword == devB.loginPassword; + } + + // INFORMATIONAL + /** + * @return A human-readable version of this Device. + */ + [[nodiscard]] string toString() const { + stringstream strS; + strS << "(device #" << myID << ": " << loginPassword << ")"; + return strS.str(); + } + + /** + * Stream access to Device + */ + friend std::ostream &operator<<(std::ostream &os, const Device &dev) { + os << dev.toString(); + return os; + } + + + friend class Password; + + private: + /** + * An ID for this Device that is different from the ID of any other Device. + */ + int myID; + + /** + * A login password for this Device. + */ + Password loginPassword; + + /** + * A unique (among devices) global device ID counter. + */ + static int globalDeviceID; +}; + +#endif // HW5_ADMIN_MACHINE_H diff --git a/tests/include/Password.h b/tests/include/Password.h new file mode 100644 index 0000000000000000000000000000000000000000..fdda8389797587f080e54881323fde4fd40c32a2 --- /dev/null +++ b/tests/include/Password.h @@ -0,0 +1,84 @@ +// +// Created by Ari Trachtenberg on 3/17/26. +// + +#ifndef HW5_ADMIN_PASSWORD_H +#define HW5_ADMIN_PASSWORD_H +#include +#include +#include + +using namespace std; + +/** + * Represents a password that can be used for logging into a Device. + */ +class Password { + public: + /** + * Construct a new Password object. + */ + Password() { + passID = globalPassID++; + myPass = generatePassword(); + } + + /** + * Compares two Passwords by their unique IDs. + * @return true iff this Password and \ref otherPwd have the same ID. + */ + bool operator==(const Password &otherPwd) const { + return passID == otherPwd.passID; + } + + /** + * @return A human-readable version of this Password. + */ + [[nodiscard]] string toString() const { + stringstream strS; + strS << "{ password #" << passID << ": \"" << myPass << "\" }"; + return strS.str(); + } + + /** + * Stream access to Password + */ + friend std::ostream &operator<<(std::ostream &os, const Password &pwd) { + os << pwd.toString(); + return os; + } + + private: + /** + * Generates a random password. + * @return A random password. + */ + static string generatePassword() { + constexpr int PASS_LEN = 5; // default length of a password + static random_device rd; // seed (slow, once per program) + static mt19937 gen(rd()); // fast, deterministic after seeding + static uniform_int_distribution dist(65, 90); // letters A - Z, inclusive + + string str; + for (size_t i = 0; i < PASS_LEN; ++i) { + str.push_back(static_cast(dist(gen))); + } + return str; + } + + /** + * An ID for this Password that is different from the ID of any other Password. + */ + int passID; + + /** + * A plaintext password. + */ + string myPass; + + /** + * Global counter to ensure that each Password has a unique ID. + */ + static int globalPassID; +}; +#endif // HW5_ADMIN_PASSWORD_H \ No newline at end of file diff --git a/tests/include/Service.h b/tests/include/Service.h new file mode 100644 index 0000000000000000000000000000000000000000..03289d467b549e73344800b3d1d5a5fdfacac130 --- /dev/null +++ b/tests/include/Service.h @@ -0,0 +1,106 @@ +// +// Created by Ari Trachtenberg on 3/17/26. +// + +#ifndef HW5_ADMIN_SERVICE_H +#define HW5_ADMIN_SERVICE_H + +#include +#include +#include + +#include "../include/Device.h" + +using namespace std; + +/** + * Represents a Service utilizing two or more devices + */ +class Service { +public: + // CONSTRUCTORS + /** + * Creates a Service utilizing between two or more Devices. + * @requires d1 != d2 + */ + template + Service(Device &d1, Device &d2, Rest &...rest) { + serviceID = globalServiceID++; + addDevice(d1); + addDevice(d2); + (addDevice(rest), ...); + } + + /** + * A Service cannot be created through the default constructor. + */ + Service() = delete; + + /** + * Services cannot be copied. + */ + Service(const Service &otherSvc) = delete; + + + // MODIFIERS + + /** + * Add a Device to this Service. + * @param dev The Device to add. + */ + void addDevice(Device &dev); + + // INFORMATIONAL + /** + * @return A collection of all Device s associated with this Service. + */ + [[nodiscard]] const unordered_set &getDevices() const; + + /** + * @param dev The Device to check in this Service. + * @return true iff this Service involves the given Device + */ + bool hasDevice(Device &dev) const; + + /** + * Compares Service objects by their unique ID. + * @return true if this Service equals \ref otherSvc + */ + bool operator==(const Service &otherSvc) const { return serviceID == otherSvc.serviceID; } + + /** + * @return A human-readable version of this Service. + */ + [[nodiscard]] string toString() const { + stringstream strS; + strS << "[ service #" << serviceID << " ] "; + for (const auto dev : devices) { + strS << *dev << " "; + } + return strS.str(); + } + + + /** + * Stream access to Service. + */ + friend std::ostream &operator<<(std::ostream &os, const Service &svc) { + os << svc.toString(); + return os; + } + +private: + /** + * A collection of Devices associated with this Service + */ + unordered_set devices; + + /** + * The ID of this Service. + */ + int serviceID; + + static int globalServiceID; +}; + +#endif // HW5_ADMIN_SERVICE_H diff --git a/tests/include/System.h b/tests/include/System.h new file mode 100644 index 0000000000000000000000000000000000000000..70df97234ca51c8484dc47d15706435011f2fc8a --- /dev/null +++ b/tests/include/System.h @@ -0,0 +1,106 @@ +// +// Created by Ari Trachtenberg on 3/17/26. +// + +#ifndef HW5_ADMIN_SYSTEM_H +#define HW5_ADMIN_SYSTEM_H + +#include +#include +#include +#include +#include + +#include "Service.h" + +using namespace std; + +/** + * Represents a system of devices and their servicess. + */ +class System { +public: + // MODIFIERS + /** + * Creates a system with an arbitrary number of Devices. + * Each Device is provided by reference. + */ + template + explicit System(const Rest &...rest) { + (addService(rest), ...); + } + + /** + * Adds a new Service to the System + * @param newSvc The new Service to add. + */ + void addService(const Service &newSvc); + + // GETTERS + /** + * @return All unique Device s associated with this System. + */ + [[nodiscard]] unordered_set getAllDevices() const; + + /** + * @return All unique Service s associated with this System. + */ + [[nodiscard]] unordered_set getServices() const; + + // INFORMATIONAL + /** + * + * @return true iff the System is considered secure, meaning that + * any two Devices on the same Service have different Passwords. + */ + [[nodiscard]] bool isSecureQ() const; + + /** + * @return A human-readable version of this System. + */ + [[nodiscard]] string toString() const { + stringstream strS; + for (const auto svc : services) { + strS << '\t' << *svc << endl; + } + strS << '\t' << "Is it secure? " << (isSecureQ()?"yes":"no") << endl; + strS << '\t' << "# of unique passwords: " << numPasswords() << endl; + return strS.str(); +} + + /** + * @return The number of unique Passwords associated with the System. + */ + [[nodiscard]] long numPasswords() const; + + /** + * Stream access to System. + */ + friend std::ostream &operator<<(std::ostream &os, const System &sys) { + os << sys.toString(); + return os; + } + +private: + // Various data structures ... feel free to use whichever you want. + + /** + * Services stored by the system. + */ + unordered_set services; + + /** + * Mapping of Services to Devices + */ + unordered_multimap serviceDevices; + + /** + * Mapping of Device x Device to sets of Services. + */ + std::unordered_map< + const Device*, + std::unordered_map> + > deviceMatrix; +}; + +#endif // HW5_ADMIN_SYSTEM_H \ No newline at end of file diff --git a/tests/test_3a.cpp b/tests/test_3a.cpp new file mode 100644 index 0000000000000000000000000000000000000000..060520f87116e91492cce35f9c3e7eddba351909 --- /dev/null +++ b/tests/test_3a.cpp @@ -0,0 +1,206 @@ +// +// Created by Kevin on 3/19/2026. +// +#include +#include + +#include "include/Password.h" +#include "include/Device.h" +#include "include/Service.h" +#include "include/System.h" + +using namespace std; + +// helper function: consistent failure printing +bool failExample(const char* testName, + const std::string& msg, + const std::string& expected, + const std::string& got) { + std::cerr << "[" << testName << " FAILED] " << msg + << ": expected " << expected << ", got " << got << "\n"; + return false; +} + +// helper: string conversion for bool +static string boolStr(bool x) { + return x ? "true" : "false"; +} + +// -------------------- Initial system should be secure -------------------- +bool test_0() { + const char* T = "Initial system should be secure"; + + Password p0, p1, p2; + Device dev0(p0), dev1(p1), dev2(p2); + + Service svc0(dev1, dev0); + Service svc1(dev1, dev2, dev0); + Service svc2(dev2, dev0); + + System sys(svc0, svc1, svc2); + + if (!sys.isSecureQ()) { + return failExample( + T, + "freshly constructed example system should be secure", + "true", + boolStr(sys.isSecureQ()) + ); + } + + return true; +} + +// -------------------- Initial system should use 3 unique passwords -------------------- +bool test_1() { + const char* T = "Initial system should have 3 unique passwords"; + + Password p0, p1, p2; + Device dev0(p0), dev1(p1), dev2(p2); + + Service svc0(dev1, dev0); + Service svc1(dev1, dev2, dev0); + Service svc2(dev2, dev0); + + System sys(svc0, svc1, svc2); + + long got = sys.numPasswords(); + if (got != 3) { + return failExample( + T, + "numPasswords() on initial secure system", + "3", + to_string(got) + ); + } + + return true; +} + +// -------------------- After setting dev1 to dev0's password, system should be insecure -------------------- +bool test_2() { + const char* T = "Changing dev1 to dev0 password should make system insecure"; + + Password p0, p1, p2; + Device dev0(p0), dev1(p1), dev2(p2); + + Service svc0(dev1, dev0); + Service svc1(dev1, dev2, dev0); + Service svc2(dev2, dev0); + + System sys(svc0, svc1, svc2); + + dev1.changePassword(p0); + + if (sys.isSecureQ()) { + return failExample( + T, + "system should become insecure after dev1 shares password with dev0", + "false", + boolStr(sys.isSecureQ()) + ); + } + + return true; +} + +// -------------------- After setting dev1 to dev0's password, system should use 2 unique passwords -------------------- +bool test_3() { + const char* T = "Changing dev1 to dev0 password should reduce unique passwords to 2"; + + Password p0, p1, p2; + Device dev0(p0), dev1(p1), dev2(p2); + + Service svc0(dev1, dev0); + Service svc1(dev1, dev2, dev0); + Service svc2(dev2, dev0); + + System sys(svc0, svc1, svc2); + + dev1.changePassword(p0); + + long got = sys.numPasswords(); + if (got != 2) { + return failExample( + T, + "numPasswords() after dev1.changePassword(p0)", + "2", + to_string(got) + ); + } + + return true; +} + +// -------------------- System should contain exactly 3 devices -------------------- +bool test_4() { + const char* T = "System should have exactly 3 devices"; + + Password p0, p1, p2; + Device dev0(p0), dev1(p1), dev2(p2); + + Service svc0(dev1, dev0); + Service svc1(dev1, dev2, dev0); + Service svc2(dev2, dev0); + + System sys(svc0, svc1, svc2); + + size_t got = sys.getAllDevices().size(); + if (got != 3) { + return failExample( + T, + "getAllDevices().size()", + "3", + to_string(got) + ); + } + + return true; +} + +// -------------------- System should contain exactly 3 services -------------------- +bool test_5() { + const char* T = "System should have exactly 3 services"; + + Password p0, p1, p2; + Device dev0(p0), dev1(p1), dev2(p2); + + Service svc0(dev1, dev0); + Service svc1(dev1, dev2, dev0); + Service svc2(dev2, dev0); + + System sys(svc0, svc1, svc2); + + size_t got = sys.getServices().size(); + if (got != 3) { + return failExample( + T, + "getServices().size()", + "3", + to_string(got) + ); + } + + return true; +} + +int main() { + bool results[] = { + test_0(), + test_1(), + test_2(), + test_3(), + test_4(), + test_5() + }; + + bool allPassed = true; + for (size_t ii = 0; ii < std::size(results); ii++) { + cout << "Problem 3a Test " << to_string(ii) << ": " + << (results[ii] ? "passed" : "failed") << endl; + allPassed &= results[ii]; + } + + if (allPassed) exit(0); + else exit(-1); +} \ No newline at end of file diff --git a/tests/test_3b.cpp b/tests/test_3b.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d3ba8273c4a8f36e24d7f64360e0ae53e6291656 --- /dev/null +++ b/tests/test_3b.cpp @@ -0,0 +1,255 @@ +// +// Created by Kevin on 3/19/2026. +// +#include +#include + +#include "include/Password.h" +#include "include/Device.h" +#include "include/Service.h" +#include "include/System.h" +#include "impl/MakeSecure.cpp" + +using namespace std; + +// helper function: consistent failure printing +bool failExample(const char* testName, + const std::string& msg, + const std::string& expected, + const std::string& got) { + std::cerr << "[" << testName << " FAILED] " << msg + << ": expected " << expected << ", got " << got << "\n"; + return false; +} + +// helper: string conversion for bool +static string boolStr(bool x) { + return x ? "true" : "false"; +} + +// -------------------- makeSecure should fix a simple insecure system -------------------- +bool test_0() { + const char* T = "makeSecure should fix simple insecure system"; + + Password p0, p1, p2; + Device dev0(p0), dev1(p0), dev2(p2); + + Service svc0(dev1, dev0); + Service svc1(dev1, dev2, dev0); + Service svc2(dev2, dev0); + + System sys(svc0, svc1, svc2); + + if (sys.isSecureQ()) { + return failExample( + T, + "system should start insecure before makeSecure()", + "false", + boolStr(sys.isSecureQ()) + ); + } + + makeSecure(sys); + + if (!sys.isSecureQ()) { + return failExample( + T, + "system should be secure after makeSecure()", + "true", + boolStr(sys.isSecureQ()) + ); + } + + return true; +} + +// -------------------- makeSecure should preserve number of devices -------------------- +bool test_1() { + const char* T = "makeSecure should preserve all devices"; + + Password p0, p1, p2; + Device dev0(p0), dev1(p1), dev2(p2); + + Service svc0(dev1, dev0); + Service svc1(dev1, dev2, dev0); + Service svc2(dev2, dev0); + + System sys(svc0, svc1, svc2); + + dev1.changePassword(p0); + + size_t before = sys.getAllDevices().size(); + makeSecure(sys); + size_t after = sys.getAllDevices().size(); + + if (before != after) { + return failExample( + T, + "number of devices should not change after makeSecure()", + to_string(before), + to_string(after) + ); + } + + return true; +} + +// -------------------- makeSecure should preserve number of services -------------------- +bool test_2() { + const char* T = "makeSecure should preserve all services"; + + Password p0, p1, p2; + Device dev0(p0), dev1(p1), dev2(p2); + + Service svc0(dev1, dev0); + Service svc1(dev1, dev2, dev0); + Service svc2(dev2, dev0); + + System sys(svc0, svc1, svc2); + + dev1.changePassword(p0); + + size_t before = sys.getServices().size(); + makeSecure(sys); + size_t after = sys.getServices().size(); + + if (before != after) { + return failExample( + T, + "number of services should not change after makeSecure()", + to_string(before), + to_string(after) + ); + } + + return true; +} + +// -------------------- makeSecure should keep an already secure system secure -------------------- +bool test_3() { + const char* T = "makeSecure should keep secure system secure"; + + Password p0, p1, p2; + Device dev0(p0), dev1(p1), dev2(p2); + + Service svc0(dev1, dev0); + Service svc1(dev1, dev2, dev0); + Service svc2(dev2, dev0); + + System sys(svc0, svc1, svc2); + + if (!sys.isSecureQ()) { + return failExample( + T, + "system should start secure", + "true", + boolStr(sys.isSecureQ()) + ); + } + + makeSecure(sys); + + if (!sys.isSecureQ()) { + return failExample( + T, + "system should remain secure after makeSecure()", + "true", + boolStr(sys.isSecureQ()) + ); + } + + return true; +} + +// -------------------- makeSecure should fix a system where all devices initially share one password -------------------- +bool test_4() { + const char* T = "makeSecure should fix system with all devices sharing one password"; + + Password shared; + Device dev0(shared), dev1(shared), dev2(shared), dev3(shared); + + Service svc0(dev0, dev1); + Service svc1(dev1, dev2); + Service svc2(dev2, dev3); + Service svc3(dev0, dev2, dev3); + + System sys(svc0, svc1, svc2, svc3); + + if (sys.isSecureQ()) { + return failExample( + T, + "system should start insecure when all devices share one password", + "false", + boolStr(sys.isSecureQ()) + ); + } + + makeSecure(sys); + + if (!sys.isSecureQ()) { + return failExample( + T, + "system should become secure after makeSecure()", + "true", + boolStr(sys.isSecureQ()) + ); + } + + return true; +} + +// -------------------- makeSecure should fix a single-service clique -------------------- +bool test_5() { + const char* T = "makeSecure should fix a clique formed by one service"; + + Password shared; + Device dev0(shared), dev1(shared), dev2(shared); + + // one service containing all 3 devices => all pairwise conflicts + Service svc0(dev0, dev1, dev2); + + System sys(svc0); + + if (sys.isSecureQ()) { + return failExample( + T, + "single-service clique with shared password should start insecure", + "false", + boolStr(sys.isSecureQ()) + ); + } + + makeSecure(sys); + + if (!sys.isSecureQ()) { + return failExample( + T, + "single-service clique should be secure after makeSecure()", + "true", + boolStr(sys.isSecureQ()) + ); + } + + return true; +} + +int main() { + bool results[] = { + test_0(), + test_1(), + test_2(), + test_3(), + test_4(), + test_5() + }; + + bool allPassed = true; + for (size_t ii = 0; ii < std::size(results); ii++) { + cout << "Problem 3b Test " << to_string(ii) << ": " + << (results[ii] ? "passed" : "failed") << endl; + allPassed &= results[ii]; + } + + if (allPassed) exit(0); + else exit(-1); +} \ No newline at end of file diff --git a/tests/test_3c.cpp b/tests/test_3c.cpp new file mode 100644 index 0000000000000000000000000000000000000000..aaa52c7430ee3c9ab20182f18de7e489f168cebc --- /dev/null +++ b/tests/test_3c.cpp @@ -0,0 +1,335 @@ +// +// Created by Kevin on 3/19/2026. +// +#include +#include + +#include "include/Password.h" +#include "include/Device.h" +#include "include/Service.h" +#include "include/System.h" +#include "impl/MakeSecure.cpp" + +using namespace std; + +// helper function: consistent failure printing +bool failExample(const char* testName, + const std::string& msg, + const std::string& expected, + const std::string& got) { + std::cerr << "[" << testName << " FAILED] " << msg + << ": expected " << expected << ", got " << got << "\n"; + return false; +} + +// helper: string conversion for bool +static string boolStr(bool x) { + return x ? "true" : "false"; +} + +// -------------------- Triangle K3 requires exactly 3 passwords -------------------- +bool test_0() { + const char* T = "Triangle should require exactly 3 passwords"; + + Password shared; + Device dev0(shared), dev1(shared), dev2(shared); + + // K3: all pairwise conflicts + Service svc0(dev0, dev1); + Service svc1(dev1, dev2); + Service svc2(dev0, dev2); + + System sys(svc0, svc1, svc2); + + makeSecure(sys); + + if (!sys.isSecureQ()) { + return failExample( + T, + "triangle should be secure after makeSecure()", + "true", + boolStr(sys.isSecureQ()) + ); + } + + long got = sys.numPasswords(); + if (got != 3) { + return failExample( + T, + "triangle graph chromatic number", + "3", + to_string(got) + ); + } + + return true; +} + +// -------------------- Path of length 2 should require exactly 2 passwords -------------------- +bool test_1() { + const char* T = "Path of three devices should require exactly 2 passwords"; + + Password shared; + Device dev0(shared), dev1(shared), dev2(shared); + + // path: dev0 - dev1 - dev2 + Service svc0(dev0, dev1); + Service svc1(dev1, dev2); + + System sys(svc0, svc1); + + makeSecure(sys); + + if (!sys.isSecureQ()) { + return failExample( + T, + "path should be secure after makeSecure()", + "true", + boolStr(sys.isSecureQ()) + ); + } + + long got = sys.numPasswords(); + if (got != 2) { + return failExample( + T, + "path graph chromatic number", + "2", + to_string(got) + ); + } + + return true; +} + +// -------------------- 4-cycle should require exactly 2 passwords -------------------- +bool test_2() { + const char* T = "4-cycle should require exactly 2 passwords"; + + Password shared; + Device dev0(shared), dev1(shared), dev2(shared), dev3(shared); + + // cycle: 0-1-2-3-0 + Service svc0(dev0, dev1); + Service svc1(dev1, dev2); + Service svc2(dev2, dev3); + Service svc3(dev3, dev0); + + System sys(svc0, svc1, svc2, svc3); + + makeSecure(sys); + + if (!sys.isSecureQ()) { + return failExample( + T, + "4-cycle should be secure after makeSecure()", + "true", + boolStr(sys.isSecureQ()) + ); + } + + long got = sys.numPasswords(); + if (got != 2) { + return failExample( + T, + "4-cycle chromatic number", + "2", + to_string(got) + ); + } + + return true; +} + +// -------------------- Star graph should require exactly 2 passwords -------------------- +bool test_3() { + const char* T = "Star graph should require exactly 2 passwords"; + + Password shared; + Device center(shared), leaf1(shared), leaf2(shared), leaf3(shared), leaf4(shared); + + // star centered at center + Service svc0(center, leaf1); + Service svc1(center, leaf2); + Service svc2(center, leaf3); + Service svc3(center, leaf4); + + System sys(svc0, svc1, svc2, svc3); + + makeSecure(sys); + + if (!sys.isSecureQ()) { + return failExample( + T, + "star graph should be secure after makeSecure()", + "true", + boolStr(sys.isSecureQ()) + ); + } + + long got = sys.numPasswords(); + if (got != 2) { + return failExample( + T, + "star graph chromatic number", + "2", + to_string(got) + ); + } + + return true; +} + +// -------------------- Complete graph K4 should require exactly 4 passwords -------------------- +bool test_4() { + const char* T = "K4 should require exactly 4 passwords"; + + Password shared; + Device dev0(shared), dev1(shared), dev2(shared), dev3(shared); + + // One service with all 4 devices => complete graph K4 + Service svc0(dev0, dev1, dev2, dev3); + + System sys(svc0); + + makeSecure(sys); + + if (!sys.isSecureQ()) { + return failExample( + T, + "K4 should be secure after makeSecure()", + "true", + boolStr(sys.isSecureQ()) + ); + } + + long got = sys.numPasswords(); + if (got != 4) { + return failExample( + T, + "K4 chromatic number", + "4", + to_string(got) + ); + } + + return true; +} + +// -------------------- Already secure but non-optimal system should be reduced to optimum -------------------- +bool test_5() { + const char* T = "Already secure but non-optimal system should be improved to optimum"; + + Password p0, p1, p2; + Device dev0(p0), dev1(p1), dev2(p2); + + // path: 0-1-2 + // This starts secure with 3 passwords, but optimum is 2 + Service svc0(dev0, dev1); + Service svc1(dev1, dev2); + + System sys(svc0, svc1); + + if (!sys.isSecureQ()) { + return failExample( + T, + "system should start secure", + "true", + boolStr(sys.isSecureQ()) + ); + } + + if (sys.numPasswords() != 3) { + return failExample( + T, + "initial password count before optimization", + "3", + to_string(sys.numPasswords()) + ); + } + + makeSecure(sys); + + if (!sys.isSecureQ()) { + return failExample( + T, + "system should remain secure after makeSecure()", + "true", + boolStr(sys.isSecureQ()) + ); + } + + long got = sys.numPasswords(); + if (got != 2) { + return failExample( + T, + "optimized password count for path graph", + "2", + to_string(got) + ); + } + + return true; +} + +// -------------------- Example graph should require 3 passwords -------------------- +bool test_6() { + const char* T = "Example graph should require exactly 3 passwords"; + + Password p0, p1, p2; + Device dev0(p0), dev1(p1), dev2(p2); + + Service svc0(dev1, dev0); + Service svc1(dev1, dev2, dev0); + Service svc2(dev2, dev0); + + System sys(svc0, svc1, svc2); + + // make it insecure first + dev1.changePassword(p0); + + makeSecure(sys); + + if (!sys.isSecureQ()) { + return failExample( + T, + "original example graph should be secure after makeSecure()", + "true", + boolStr(sys.isSecureQ()) + ); + } + + long got = sys.numPasswords(); + if (got != 3) { + return failExample( + T, + "original example graph chromatic number", + "3", + to_string(got) + ); + } + + return true; +} + +int main() { + bool results[] = { + test_0(), + test_1(), + test_2(), + test_3(), + test_4(), + test_5(), + test_6() + }; + + bool allPassed = true; + for (size_t ii = 0; ii < std::size(results); ii++) { + cout << "Problem 3c Test " << to_string(ii) << ": " + << (results[ii] ? "passed" : "failed") << endl; + allPassed &= results[ii]; + } + + if (allPassed) exit(0); + else exit(-1); +} \ No newline at end of file