From 9b067c9527e2135a0aecfe3b951b10c7cb0f7347 Mon Sep 17 00:00:00 2001 From: Xingyu Date: Fri, 20 Mar 2026 00:04:30 -0400 Subject: [PATCH 1/3] implement autograder --- ci_cd/.gitlab-ci.yml | 13 ++ ci_cd/problem3a.yml | 59 +++++++ ci_cd/problem3b.yml | 63 ++++++++ ci_cd/problem3c.yml | 63 ++++++++ impl/Service.cpp | 6 +- impl/System.cpp | 10 +- include/System.h | 6 + main.cpp | 4 + tests/.DS_Store | Bin 0 -> 6148 bytes tests/Makefile | 45 ++++++ tests/impl/placeholder | 0 tests/include/Device.h | 101 ++++++++++++ tests/include/Password.h | 84 ++++++++++ tests/include/Service.h | 106 +++++++++++++ tests/include/System.h | 106 +++++++++++++ tests/test_3a.cpp | 206 ++++++++++++++++++++++++ tests/test_3b.cpp | 255 +++++++++++++++++++++++++++++ tests/test_3c.cpp | 335 +++++++++++++++++++++++++++++++++++++++ 18 files changed, 1454 insertions(+), 8 deletions(-) create mode 100644 ci_cd/.gitlab-ci.yml create mode 100644 ci_cd/problem3a.yml create mode 100644 ci_cd/problem3b.yml create mode 100644 ci_cd/problem3c.yml create mode 100644 tests/.DS_Store create mode 100644 tests/Makefile create mode 100644 tests/impl/placeholder create mode 100644 tests/include/Device.h create mode 100644 tests/include/Password.h create mode 100644 tests/include/Service.h create mode 100644 tests/include/System.h create mode 100644 tests/test_3a.cpp create mode 100644 tests/test_3b.cpp create mode 100644 tests/test_3c.cpp diff --git a/ci_cd/.gitlab-ci.yml b/ci_cd/.gitlab-ci.yml new file mode 100644 index 0000000..dd2181c --- /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 0000000..159c3c3 --- /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 0000000..ce52531 --- /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 0000000..c40b3a4 --- /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 c3bc6b7..817b209 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 3d0251c..4605444 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 e3ee496..70df972 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/main.cpp b/main.cpp index 1d5d95c..c1febb2 100644 --- a/main.cpp +++ b/main.cpp @@ -7,6 +7,7 @@ #include "include/Device.h" #include "include/Password.h" #include "include/System.h" +#include "impl/MakeSecure.cpp" int main() { Password p1, p2, p3; @@ -20,4 +21,7 @@ int main() { dev1.changePassword(p1); cout << "Changed password of dev1:" << endl << sys << endl; + + makeSecure(sys); + cout << "After makeSecure:" << endl << sys << endl; } diff --git a/tests/.DS_Store b/tests/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..52ffe9b972addf774f19cea7d8e56d89bffeb170 GIT binary patch literal 6148 zcmZQzU|@7AO)+F(5MW?n;9!8zEL;p&0Z1N%F(jFwA|OddkQtsi`N>H+`AJX%q0*z& zXb6mkz#t6)P~K%>$YjW6C}7Az%fF5c3=Dez!2s-fxVohB;sR(s-;q?1lUZD1U~r9* ziJ66!jh%y?gPS8ZI3vG2xFoTpwAd-JC>q2I$i(fItta6Mg}?x zCdOv9wVWKH%KFwp@!2`KdHG#n7c($2LTCnFC=H{!85kJg{wWJC%FD^mO9z#U3=9nH z44Djh49N^R45bVy45?_LvK}o|I2e2x5*e}?QW?@1G8uBv)ieG?Q_sy%!jQ^P%uvD* z&tS}u$e_oN%uv8kfNBe)EM8qnICO!0g&bq3b|qui#RT#i +#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 0000000..fdda838 --- /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 0000000..03289d4 --- /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 0000000..70df972 --- /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 0000000..060520f --- /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 0000000..d3ba827 --- /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 0000000..aaa52c7 --- /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 -- GitLab From 9ea3489d8bee1f19d3c847e735a503f59c4597a1 Mon Sep 17 00:00:00 2001 From: Xingyu Date: Fri, 20 Mar 2026 00:04:56 -0400 Subject: [PATCH 2/3] implement autograder --- tests/.DS_Store | Bin 6148 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 tests/.DS_Store diff --git a/tests/.DS_Store b/tests/.DS_Store deleted file mode 100644 index 52ffe9b972addf774f19cea7d8e56d89bffeb170..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmZQzU|@7AO)+F(5MW?n;9!8zEL;p&0Z1N%F(jFwA|OddkQtsi`N>H+`AJX%q0*z& zXb6mkz#t6)P~K%>$YjW6C}7Az%fF5c3=Dez!2s-fxVohB;sR(s-;q?1lUZD1U~r9* ziJ66!jh%y?gPS8ZI3vG2xFoTpwAd-JC>q2I$i(fItta6Mg}?x zCdOv9wVWKH%KFwp@!2`KdHG#n7c($2LTCnFC=H{!85kJg{wWJC%FD^mO9z#U3=9nH z44Djh49N^R45bVy45?_LvK}o|I2e2x5*e}?QW?@1G8uBv)ieG?Q_sy%!jQ^P%uvD* z&tS}u$e_oN%uv8kfNBe)EM8qnICO!0g&bq3b|qui#RT#i Date: Fri, 20 Mar 2026 00:20:47 -0400 Subject: [PATCH 3/3] minor fix --- main.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/main.cpp b/main.cpp index c1febb2..1d5d95c 100644 --- a/main.cpp +++ b/main.cpp @@ -7,7 +7,6 @@ #include "include/Device.h" #include "include/Password.h" #include "include/System.h" -#include "impl/MakeSecure.cpp" int main() { Password p1, p2, p3; @@ -21,7 +20,4 @@ int main() { dev1.changePassword(p1); cout << "Changed password of dev1:" << endl << sys << endl; - - makeSecure(sys); - cout << "After makeSecure:" << endl << sys << endl; } -- GitLab