diff --git a/score/evaluation/BUILD b/score/evaluation/BUILD new file mode 100644 index 0000000..cd69e00 --- /dev/null +++ b/score/evaluation/BUILD @@ -0,0 +1,93 @@ + +load("@rules_cc//cc:defs.bzl", "cc_binary") + +alias( + name = "solid_violations", + actual = "//score/evaluation/solid:solid_violations", +) + +alias( + name = "memory_issues", + actual = "//score/evaluation/memory:memory_issues", +) + +alias( + name = "non_idiomatic_cpp", + actual = "//score/evaluation/idiomatic:non_idiomatic_cpp", +) + +alias( + name = "modern_cpp_syntax", + actual = "//score/evaluation/modern_cpp:modern_cpp_syntax", +) + +alias( + name = "design_patterns", + actual = "//score/evaluation/patterns:design_patterns", +) + +alias( + name = "code_optimization", + actual = "//score/evaluation/optimization:code_optimization", +) + +alias( + name = "safe_cpp", + actual = "//score/evaluation/safety:safe_cpp", +) + +alias( + name = "security_issues", + actual = "//score/evaluation/security:security_issues", +) + +alias( + name = "design_faults", + actual = "//score/evaluation/design:design_faults", +) + +alias( + name = "concurrency_issues", + actual = "//score/evaluation/concurrency:concurrency_issues", +) + +alias( + name = "exception_error_handling", + actual = "//score/evaluation/exception_handling:exception_error_handling", +) + +alias( + name = "unit_test_quality", + actual = "//score/evaluation/unit_test_quality:unit_test_quality", +) + +alias( + name = "template_metaprogramming_issues", + actual = "//score/evaluation/template_metaprogramming:template_metaprogramming_issues", +) + +alias( + name = "unit_test_quality_test", + actual = "//score/evaluation/unit_test_quality:unit_test_quality_test", +) + +cc_binary( + name = "evaluation_all", + srcs = ["evaluation_main.cpp"], + visibility = ["//visibility:public"], + deps = [ + "//score/evaluation/concurrency:concurrency_issues", + "//score/evaluation/design:design_faults", + "//score/evaluation/exception_handling:exception_error_handling", + "//score/evaluation/idiomatic:non_idiomatic_cpp", + "//score/evaluation/memory:memory_issues", + "//score/evaluation/modern_cpp:modern_cpp_syntax", + "//score/evaluation/optimization:code_optimization", + "//score/evaluation/patterns:design_patterns", + "//score/evaluation/security:security_issues", + "//score/evaluation/safety:safe_cpp", + "//score/evaluation/solid:solid_violations", + "//score/evaluation/template_metaprogramming:template_metaprogramming_issues", + "//score/evaluation/unit_test_quality:unit_test_quality", + ], +) diff --git a/score/evaluation/concurrency/BUILD b/score/evaluation/concurrency/BUILD new file mode 100644 index 0000000..448dbbe --- /dev/null +++ b/score/evaluation/concurrency/BUILD @@ -0,0 +1,8 @@ + +load("@rules_cc//cc:defs.bzl", "cc_library") + +cc_library( + name = "concurrency_issues", + hdrs = ["concurrency_issues.h"], + visibility = ["//visibility:public"], +) diff --git a/score/evaluation/concurrency/concurrency_issues.h b/score/evaluation/concurrency/concurrency_issues.h new file mode 100644 index 0000000..8d4b14a --- /dev/null +++ b/score/evaluation/concurrency/concurrency_issues.h @@ -0,0 +1,148 @@ + + +#ifndef SCORE_EVALUATION_CONCURRENCY_ISSUES_H +#define SCORE_EVALUATION_CONCURRENCY_ISSUES_H + +#include +#include +#include +#include +#include + +namespace score +{ +namespace evaluation +{ + +class UnsafeCounter +{ +public: + void Increment() { ++count_; } + int Get() const { return count_; } + +private: + int count_{0}; +}; + +class ManualLockUser +{ +public: + void Write(const std::string& data) + { + mtx_.lock(); + ProcessData(data); + mtx_.unlock(); + } + +private: + static void ProcessData(const std::string& /*data*/) + { + } + + std::mutex mtx_; +}; + +class BrokenLazyInit +{ +public: + void EnsureInitialised() + { + if (!initialised_) + { + std::lock_guard lk(mtx_); + if (!initialised_) + { + resource_ = 42; + initialised_ = true; + } + } + } + + int GetResource() const { return resource_; } + +private: + bool initialised_{false}; + int resource_{0}; + std::mutex mtx_; +}; + +class DeadlockProne +{ +public: + void TransferAB(int amount) + { + std::lock_guard la(mtx_a_); + std::lock_guard lb(mtx_b_); + balance_a_ -= amount; + balance_b_ += amount; + } + + void TransferBA(int amount) + { + std::lock_guard lb(mtx_b_); + std::lock_guard la(mtx_a_); + balance_b_ -= amount; + balance_a_ += amount; + } + +private: + std::mutex mtx_a_; + std::mutex mtx_b_; + int balance_a_{1000}; + int balance_b_{1000}; +}; + +class SpuriousWakeupBug +{ +public: + void Wait() + { + std::unique_lock lk(mtx_); + if (!ready_) cv_.wait(lk); + } + + void Signal() + { + std::lock_guard lk(mtx_); + ready_ = true; + cv_.notify_one(); + } + +private: + std::mutex mtx_; + std::condition_variable cv_; + bool ready_{false}; +}; + +inline void StartDetachedThread() +{ + std::string local_config = "config_data"; + + std::thread t([&local_config]() + { + (void)local_config; + }); + t.detach(); +} + +class TocTouAtomic +{ +public: + void ConsumeIfAvailable() + { + if (count_.load() > 0) + { + --count_; + } + } + + void Produce() { ++count_; } + +private: + std::atomic count_{0}; +}; + +} +} + +#endif // SCORE_EVALUATION_CONCURRENCY_ISSUES_H diff --git a/score/evaluation/design/BUILD b/score/evaluation/design/BUILD new file mode 100644 index 0000000..22b2eac --- /dev/null +++ b/score/evaluation/design/BUILD @@ -0,0 +1,8 @@ + +load("@rules_cc//cc:defs.bzl", "cc_library") + +cc_library( + name = "design_faults", + hdrs = ["design_faults.h"], + visibility = ["//visibility:public"], +) diff --git a/score/evaluation/design/design_faults.h b/score/evaluation/design/design_faults.h new file mode 100644 index 0000000..eb70159 --- /dev/null +++ b/score/evaluation/design/design_faults.h @@ -0,0 +1,182 @@ + + +#ifndef SCORE_EVALUATION_DESIGN_FAULTS_H +#define SCORE_EVALUATION_DESIGN_FAULTS_H + +#include +#include +#include + +namespace score +{ +namespace evaluation +{ + +struct Order +{ + int id{0}; + double total{0.0}; + bool is_paid{false}; + std::string customer_name; +}; + +class OrderProcessor +{ +public: + void Apply10PercentDiscount(Order& o) { o.total *= 0.90; } + void MarkAsPaid(Order& o) { o.is_paid = true; } + bool IsFreeShippingEligible(const Order& o) { return o.total > 100.0; } +}; + +class PipelineStage +{ +public: + PipelineStage() : initialised_(false) {} + + void Init() + { + initialised_ = true; + } + + void Process(const std::string& data) + { + if (!initialised_) + { + throw std::runtime_error("PipelineStage: Init() not called"); + } + (void)data; + } + +private: + bool initialised_; +}; + +struct Canvas +{ + int width{0}; + int height{0}; + std::vector pixels; +}; + +class Renderer +{ +public: + void Fill(Canvas& c, int colour) + { + c.pixels.assign(static_cast(c.width * c.height), colour); + } + + void Resize(Canvas& c, int w, int h) + { + c.width = w; + c.height = h; + c.pixels.resize(static_cast(w * h), 0); + } +}; + +class NetworkClient +{ + static constexpr int kTimeoutMs = 5000; +public: + void Connect() { /* uses kTimeoutMs */ } +}; + +class FileWatcher +{ + static constexpr int kTimeoutMs = 5000; +public: + void Watch() { /* uses kTimeoutMs */ } +}; + +class HealthCheck +{ + static constexpr int kTimeoutMs = 5000; +public: + void Ping() { /* uses kTimeoutMs */ } +}; + +class List +{ +public: + virtual ~List() = default; + virtual void Add(int value) = 0; + virtual void Remove(int value) = 0; + virtual int Get(int index) const = 0; + virtual int Size() const = 0; +}; + +class ReadOnlyList : public List +{ +public: + void Add(int /*value*/) override + { + throw std::runtime_error("ReadOnlyList: Add not supported"); + } + void Remove(int /*value*/) override + { + throw std::runtime_error("ReadOnlyList: Remove not supported"); + } + int Get(int /*index*/) const override { return 0; } + int Size() const override { return static_cast(data_.size()); } + +private: + std::vector data_; +}; + +class AccountLedger; + +class AuditEngine +{ +public: + void Audit(AccountLedger& ledger); +}; + +class AccountLedger +{ + friend class AuditEngine; +private: + double balance_{0.0}; + std::vector transactions_; +}; + +inline void AuditEngine::Audit(AccountLedger& ledger) +{ + if (ledger.balance_ < 0) { /* flag */ } + for (auto t : ledger.transactions_) { (void)t; } +} + +inline void ConfigureConnection( + const std::string& host, + int port, + int timeout_ms, + int retry_count, + bool use_tls, + bool verify_cert, + const std::string& username, + const std::string& password) +{ + (void)host; (void)port; (void)timeout_ms; (void)retry_count; + (void)use_tls; (void)verify_cert; (void)username; (void)password; +} + +inline double CalculateScore(int raw) +{ + if (raw > 255) return 0.0; + double norm = raw / 128.0; + if (norm > 1.75) return 100.0; + return norm * 57.14; +} + +inline void ExportData( + const std::string& filename, + bool compress, + bool encrypt, + bool overwrite) +{ + (void)filename; (void)compress; (void)encrypt; (void)overwrite; +} + +} +} + +#endif // SCORE_EVALUATION_DESIGN_FAULTS_H diff --git a/score/evaluation/evaluation_main.cpp b/score/evaluation/evaluation_main.cpp new file mode 100644 index 0000000..c83f564 --- /dev/null +++ b/score/evaluation/evaluation_main.cpp @@ -0,0 +1,95 @@ + + +#include "score/evaluation/concurrency/concurrency_issues.h" +#include "score/evaluation/design/design_faults.h" +#include "score/evaluation/exception_handling/exception_error_handling.h" +#include "score/evaluation/idiomatic/non_idiomatic_cpp.h" +#include "score/evaluation/memory/memory_issues.h" +#include "score/evaluation/modern_cpp/modern_cpp_syntax.h" +#include "score/evaluation/optimization/code_optimization.h" +#include "score/evaluation/patterns/design_patterns.h" +#include "score/evaluation/security/security_issues.h" +#include "score/evaluation/safety/safe_cpp.h" +#include "score/evaluation/solid/solid_violations.h" +#include "score/evaluation/template_metaprogramming/template_metaprogramming_issues.h" + +int main() +{ + score::evaluation::LogManager lm; + lm.LoadConfig("cfg.json"); + lm.RouteMessage("hello"); + + score::evaluation::AreaCalculator calc; + score::evaluation::Shape s{"circle", 3.0, 0.0}; + calc.Calculate(s); + + score::evaluation::HighLevelProcessor hlp; + hlp.Process("data"); + + score::evaluation::RawOwner ro(10); + ro.At(0) = 42; + + score::evaluation::VectorPointerInvalidation vpi; + vpi.DemonstrateUAF(); + + score::evaluation::FillBuffer(2, 'X'); + + score::evaluation::SelfAssignBroken sab(4); + sab = sab; + + score::evaluation::ToDouble(5); + score::evaluation::SumVector({1, 2, 3}); + score::evaluation::HasPrefix("hello world", "hello"); + score::evaluation::PrintLines({"line1", "line2"}); + + score::evaluation::StringList sl{"alpha", "beta", "gamma_long_string"}; + score::evaluation::CountNonEmpty(sl); + score::evaluation::FilterLong(sl); + score::evaluation::MakeAdder(10)(5); + score::evaluation::InitialiseSubsystem(0); + + score::evaluation::BrokenSingleton::GetInstance()->DoWork(); + + score::evaluation::WidgetFactory wf; + score::evaluation::Widget* w = wf.Create("button"); + delete w; + + score::evaluation::BubbleSort bs; + score::evaluation::Sorter sorter(&bs); + std::vector nums{3, 1, 4, 1, 5}; + sorter.Sort(nums); + + score::evaluation::SumLargeVector({1, 2, 3, 4, 5}); + score::evaluation::IsError("ERROR"); + score::evaluation::JoinLines({"a", "b", "c"}); + score::evaluation::SortAndSlice({5, 3, 1, 4, 2}); + + std::vector v{1, 2, 3}; + score::evaluation::IndexInRange(-1, v); + score::evaluation::MultiplyUnchecked(100000, 100000); + score::evaluation::TruncateToByte(300); + score::evaluation::CountFlags(true, false, true); + + score::evaluation::EvaluateSecuritySamples(); + + score::evaluation::Order order{1, 150.0, false, "Alice"}; + score::evaluation::OrderProcessor op; + op.Apply10PercentDiscount(order); + + score::evaluation::CalculateScore(200); + score::evaluation::ExportData("out.bin", true, false, true); + + score::evaluation::UnsafeCounter uc; + uc.Increment(); + + score::evaluation::SpuriousWakeupBug swb; + swb.Signal(); + + score::evaluation::CatchByValue(); + score::evaluation::LoadFile("nonexistent.txt"); + score::evaluation::InitDevice(0); + + score::evaluation::EvaluateTemplateMetaprogrammingSamples(); + + return 0; +} diff --git a/score/evaluation/exception_handling/BUILD b/score/evaluation/exception_handling/BUILD new file mode 100644 index 0000000..a66e353 --- /dev/null +++ b/score/evaluation/exception_handling/BUILD @@ -0,0 +1,8 @@ + +load("@rules_cc//cc:defs.bzl", "cc_library") + +cc_library( + name = "exception_error_handling", + hdrs = ["exception_error_handling.h"], + visibility = ["//visibility:public"], +) diff --git a/score/evaluation/exception_handling/exception_error_handling.h b/score/evaluation/exception_handling/exception_error_handling.h new file mode 100644 index 0000000..31ce5b8 --- /dev/null +++ b/score/evaluation/exception_handling/exception_error_handling.h @@ -0,0 +1,119 @@ + + +#ifndef SCORE_EVALUATION_EXCEPTION_ERROR_HANDLING_H +#define SCORE_EVALUATION_EXCEPTION_ERROR_HANDLING_H + +#include +#include +#include +#include + +namespace score +{ +namespace evaluation +{ + +inline void CatchByValue() +{ + try + { + throw std::runtime_error("something went wrong"); + } + catch (const std::exception& ex) + { + (void)ex.what(); + } +} + +inline bool LoadFile(const std::string& path) +{ + try + { + std::ifstream f(path); + if (!f.is_open()) throw std::runtime_error("cannot open"); + return true; + } + catch (...) + { + return false; + } +} + +inline void ParseConfig(const std::string& cfg) +{ + if (cfg.empty()) + { + throw "config must not be empty"; + } + if (cfg.size() > 4096) + { + throw 42; + } +} + +inline int FindIndex(const std::vector& v, int target) +{ + for (std::size_t i = 0; i < v.size(); ++i) + { + if (v[i] == target) return static_cast(i); + } + throw std::runtime_error("element not found"); +} + +inline std::string ReadLine(const std::string& path) noexcept +{ + std::ifstream f(path); + std::string line; + std::getline(f, line); + return line; +} + +inline void Process() +{ + try + { + throw std::runtime_error("base error"); + } + catch (const std::exception& ex) + { + std::runtime_error copy = std::runtime_error(ex.what()); + throw copy; + } +} + +int InitDevice(int device_id); + +inline int InitDevice(int /*device_id*/) +{ + return -1; +} + +class SilentlyFailingResource +{ +public: + explicit SilentlyFailingResource(const std::string& path) + { + file_.open(path); + if (!file_.is_open()) + { + } + } + + bool IsValid() const { return file_.is_open(); } + + std::string ReadLine() + { + if (!IsValid()) return {}; + std::string line; + std::getline(file_, line); + return line; + } + +private: + std::ifstream file_; +}; + +} +} + +#endif // SCORE_EVALUATION_EXCEPTION_ERROR_HANDLING_H diff --git a/score/evaluation/idiomatic/BUILD b/score/evaluation/idiomatic/BUILD new file mode 100644 index 0000000..5187b1b --- /dev/null +++ b/score/evaluation/idiomatic/BUILD @@ -0,0 +1,8 @@ + +load("@rules_cc//cc:defs.bzl", "cc_library") + +cc_library( + name = "non_idiomatic_cpp", + hdrs = ["non_idiomatic_cpp.h"], + visibility = ["//visibility:public"], +) diff --git a/score/evaluation/idiomatic/non_idiomatic_cpp.h b/score/evaluation/idiomatic/non_idiomatic_cpp.h new file mode 100644 index 0000000..d393fd6 --- /dev/null +++ b/score/evaluation/idiomatic/non_idiomatic_cpp.h @@ -0,0 +1,99 @@ + + +#ifndef SCORE_EVALUATION_NON_IDIOMATIC_CPP_H +#define SCORE_EVALUATION_NON_IDIOMATIC_CPP_H + +#include +#include +#include +#include +#include + +namespace score +{ +namespace evaluation +{ + +#define MAX_ITEMS 64 // [NI-03]: prefer constexpr int kMaxItems = 64; +#define PI_VALUE 3.14159265 // [NI-03]: prefer constexpr double kPi = 3.14159265; + +int g_request_count = 0; + +inline double ToDouble(int value) +{ + return (double)value; +} + +inline void* GetRawPtr(int* p) +{ + return (void*)p; +} + +inline bool IsNull(int* ptr) +{ + return ptr == NULL; +} + +void SetPointer(int** pp) +{ + *pp = NULL; +} + +struct LegacyBuffer +{ + int data[MAX_ITEMS]; + int size; + + void Clear() + { + for (int i = 0; i < MAX_ITEMS; ++i) + { + data[i] = 0; + } + } +}; + +inline std::string FormatMessage(int code, const std::string& text) +{ + char buf[256]; + sprintf(buf, "Code=%d Msg=%s", code, text.c_str()); + return std::string(buf); +} + +inline int SumVector(const std::vector& vec) +{ + int total = 0; + for (int i = 0; i < (int)vec.size(); ++i) + { + total += vec[i]; + } + return total; +} + +inline bool HasPrefix(std::string text, std::string prefix) +{ + return text.substr(0, prefix.size()) == prefix; +} + +inline void PrintLines(const std::vector& lines) +{ + for (const auto& line : lines) + { + std::cout << line << std::endl; + } +} + +inline bool ParseInt(const std::string& str, int* result) +{ + if (str.empty() || result == NULL) + { + return false; + } + *result = std::stoi(str); + return true; +} + +} +} + +#endif // SCORE_EVALUATION_NON_IDIOMATIC_CPP_H diff --git a/score/evaluation/memory/BUILD b/score/evaluation/memory/BUILD new file mode 100644 index 0000000..3848a3b --- /dev/null +++ b/score/evaluation/memory/BUILD @@ -0,0 +1,8 @@ + +load("@rules_cc//cc:defs.bzl", "cc_library") + +cc_library( + name = "memory_issues", + hdrs = ["memory_issues.h"], + visibility = ["//visibility:public"], +) diff --git a/score/evaluation/memory/memory_issues.h b/score/evaluation/memory/memory_issues.h new file mode 100644 index 0000000..a28d3fb --- /dev/null +++ b/score/evaluation/memory/memory_issues.h @@ -0,0 +1,164 @@ + + +#ifndef SCORE_EVALUATION_MEMORY_ISSUES_H +#define SCORE_EVALUATION_MEMORY_ISSUES_H + +#include +#include +#include +#include + +namespace score +{ +namespace evaluation +{ + +class RawOwner +{ +public: + explicit RawOwner(int size) : data_(new int[size]), size_(size) {} + + ~RawOwner() + { + delete[] data_; + } + + int& At(int index) { return data_[index]; } + +private: + int* data_; + int size_; +}; + +class ResourceBase +{ +public: + ~ResourceBase() {} + + virtual void Process() = 0; +}; + +class ResourceDerived : public ResourceBase +{ +public: + explicit ResourceDerived(int n) : buf_(new char[n]) {} + ~ResourceDerived() { delete[] buf_; } + + void Process() override {} + +private: + char* buf_; +}; + +class DoubleFreeProne +{ +public: + explicit DoubleFreeProne(std::size_t n) : ptr_(new int[n]) {} + ~DoubleFreeProne() { delete[] ptr_; } + +private: + int* ptr_; +}; + +class LeakyConstructor +{ +public: + LeakyConstructor() + { + buffer_ = new char[1024]; + another_ = new char[512]; + MightThrow(); + } + + ~LeakyConstructor() + { + delete[] buffer_; + delete[] another_; + } + +private: + static void MightThrow() { throw std::runtime_error("simulated failure"); } + + char* buffer_{nullptr}; + char* another_{nullptr}; +}; + +class VectorPointerInvalidation +{ +public: + void AddElement(int value) + { + items_.push_back(value); + } + + void DemonstrateUAF() + { + items_.reserve(4); + items_.push_back(10); + + const int* ptr = &items_[0]; + + for (int i = 0; i < 100; ++i) { items_.push_back(i); } + + int val = *ptr; + (void)val; + } + +private: + std::vector items_; +}; + +constexpr int kBufSize = 16; + +void FillBuffer(int user_index, char value) +{ + char buf[kBufSize]; + if (user_index < 0 || user_index >= kBufSize) + { + return; + } + buf[user_index] = value; + (void)buf[user_index]; +} + +std::string GetLabel() +{ + std::string local = "transient_label"; + return local; +} + +class SelfAssignBroken +{ +public: + explicit SelfAssignBroken(std::size_t n) + : size_(n), data_(new int[n]) + { + std::memset(data_, 0, n * sizeof(int)); + } + + SelfAssignBroken(const SelfAssignBroken& other) + : size_(other.size_), data_(new int[other.size_]) + { + std::memcpy(data_, other.data_, size_ * sizeof(int)); + } + + SelfAssignBroken& operator=(const SelfAssignBroken& other) + { + delete[] data_; + size_ = other.size_; + data_ = new int[size_]; + std::memcpy(data_, other.data_, size_ * sizeof(int)); + return *this; + } + + ~SelfAssignBroken() { delete[] data_; } + +private: + std::size_t size_; + int* data_; +}; + +} +} + +#endif // SCORE_EVALUATION_MEMORY_ISSUES_H diff --git a/score/evaluation/modern_cpp/BUILD b/score/evaluation/modern_cpp/BUILD new file mode 100644 index 0000000..e237f87 --- /dev/null +++ b/score/evaluation/modern_cpp/BUILD @@ -0,0 +1,8 @@ + +load("@rules_cc//cc:defs.bzl", "cc_library") + +cc_library( + name = "modern_cpp_syntax", + hdrs = ["modern_cpp_syntax.h"], + visibility = ["//visibility:public"], +) diff --git a/score/evaluation/modern_cpp/modern_cpp_syntax.h b/score/evaluation/modern_cpp/modern_cpp_syntax.h new file mode 100644 index 0000000..26afe5a --- /dev/null +++ b/score/evaluation/modern_cpp/modern_cpp_syntax.h @@ -0,0 +1,122 @@ + + +#ifndef SCORE_EVALUATION_MODERN_CPP_SYNTAX_H +#define SCORE_EVALUATION_MODERN_CPP_SYNTAX_H + +#include +#include +#include +#include +#include +#include +#include + +namespace score +{ +namespace evaluation +{ + +typedef std::vector StringList; +typedef std::map CounterMap; + +inline int CountNonEmpty(const StringList& items) +{ + int count = 0; + for (StringList::const_iterator it = items.begin(); it != items.end(); ++it) + { + if (!it->empty()) + { + ++count; + } + } + return count; +} + +struct IsLongString +{ + bool operator()(const std::string& s) const { return s.size() > 10; } +}; + +inline StringList FilterLong(const StringList& items) +{ + StringList result; + std::copy_if(items.begin(), items.end(), std::back_inserter(result), IsLongString{}); + return result; +} + +enum class ValueType { INT, DOUBLE, STRING }; + +struct TaggedValue +{ + ValueType tag; + int int_val; + double dbl_val; + std::string str_val; +}; + +inline void PrintTaggedValue(const TaggedValue& v) +{ + if (v.tag == ValueType::INT) { /* use v.int_val */ } + else if (v.tag == ValueType::DOUBLE) { /* use v.dbl_val */ } + else { /* use v.str_val */ } +} + +class MovableResource +{ +public: + explicit MovableResource(std::size_t n) : size_(n), data_(new int[n]) {} + + MovableResource(MovableResource&& other) + : size_(other.size_), data_(other.data_) + { + other.data_ = nullptr; + other.size_ = 0; + } + + MovableResource& operator=(MovableResource&& other) + { + delete[] data_; + data_ = other.data_; + size_ = other.size_; + other.data_ = nullptr; + other.size_ = 0; + return *this; + } + + ~MovableResource() { delete[] data_; } + +private: + std::size_t size_; + int* data_; +}; + +inline std::pair FindFirst(const std::vector& vec, int target) +{ + for (std::size_t i = 0; i < vec.size(); ++i) + { + if (vec[i] == target) return std::make_pair(true, static_cast(i)); + } + return std::make_pair(false, -1); +} + +inline int GetCount(CounterMap& counters, const std::string& key) +{ + return counters[key]; +} + +inline std::function MakeAdder(int offset) +{ + return std::bind(std::plus(), std::placeholders::_1, offset); +} + +bool InitialiseSubsystem(int flags); + +inline bool InitialiseSubsystem(int /*flags*/) +{ + return false; +} + +} +} + +#endif // SCORE_EVALUATION_MODERN_CPP_SYNTAX_H diff --git a/score/evaluation/optimization/BUILD b/score/evaluation/optimization/BUILD new file mode 100644 index 0000000..07d082d --- /dev/null +++ b/score/evaluation/optimization/BUILD @@ -0,0 +1,8 @@ + +load("@rules_cc//cc:defs.bzl", "cc_library") + +cc_library( + name = "code_optimization", + hdrs = ["code_optimization.h"], + visibility = ["//visibility:public"], +) diff --git a/score/evaluation/optimization/code_optimization.h b/score/evaluation/optimization/code_optimization.h new file mode 100644 index 0000000..3e53d17 --- /dev/null +++ b/score/evaluation/optimization/code_optimization.h @@ -0,0 +1,106 @@ + + +#ifndef SCORE_EVALUATION_CODE_OPTIMIZATION_H +#define SCORE_EVALUATION_CODE_OPTIMIZATION_H + +#include +#include +#include +#include +#include + +namespace score +{ +namespace evaluation +{ + +inline int SumLargeVector(std::vector data) +{ + int sum = 0; + for (auto v : data) sum += v; + return sum; +} + +inline void ProcessItems(const std::vector& items) +{ + for (std::size_t i = 0; i < items.size(); ++i) + { + (void)items[i]; + } +} + +inline bool IsError(const std::string& level) +{ + return level == std::string("ERROR"); +} + +inline void IncrementCounter(std::map& counters, const std::string& key) +{ + if (counters.find(key) != counters.end()) + { + counters[key]++; + } + else + { + counters[key] = 1; + } +} + +inline std::vector BuildRange(int n) +{ + std::vector result; + for (int i = 0; i < n; ++i) result.push_back(i); + + std::vector copy = result; + return copy; +} + +inline std::list CollectSamples(int n) +{ + std::list samples; + for (int i = 0; i < n; ++i) samples.push_back(i); + return samples; +} + +class ITransform +{ +public: + virtual ~ITransform() = default; + virtual int Apply(int x) const = 0; +}; + +class DoubleTransform : public ITransform +{ +public: + int Apply(int x) const override { return x * 2; } +}; + +inline void ApplyTransforms(std::vector& data, const ITransform* transform) +{ + for (auto& v : data) v = transform->Apply(v); +} + +inline std::vector SortAndSlice(std::vector data) +{ + std::sort(data.begin(), data.end()); + + std::sort(data.begin(), data.end()); + + if (data.size() > 10) data.resize(10); + return data; +} + +inline std::string JoinLines(const std::vector& lines) +{ + std::string result; + for (const auto& line : lines) + { + result = result + line + "\n"; + } + return result; +} + +} +} + +#endif // SCORE_EVALUATION_CODE_OPTIMIZATION_H diff --git a/score/evaluation/patterns/BUILD b/score/evaluation/patterns/BUILD new file mode 100644 index 0000000..d86cf2c --- /dev/null +++ b/score/evaluation/patterns/BUILD @@ -0,0 +1,8 @@ + +load("@rules_cc//cc:defs.bzl", "cc_library") + +cc_library( + name = "design_patterns", + hdrs = ["design_patterns.h"], + visibility = ["//visibility:public"], +) diff --git a/score/evaluation/patterns/design_patterns.h b/score/evaluation/patterns/design_patterns.h new file mode 100644 index 0000000..2b50e8b --- /dev/null +++ b/score/evaluation/patterns/design_patterns.h @@ -0,0 +1,169 @@ + + +#ifndef SCORE_EVALUATION_DESIGN_PATTERNS_H +#define SCORE_EVALUATION_DESIGN_PATTERNS_H + +#include +#include +#include + +namespace score +{ +namespace evaluation +{ + +class BrokenSingleton +{ +public: + static BrokenSingleton* GetInstance() + { + if (instance_ == nullptr) + { + if (instance_ == nullptr) + { + instance_ = new BrokenSingleton(); + } + } + return instance_; + } + + void DoWork() {} + +private: + BrokenSingleton() = default; + static BrokenSingleton* instance_; +}; + +BrokenSingleton* BrokenSingleton::instance_ = nullptr; + +class IEventListener +{ +public: + virtual ~IEventListener() = default; + virtual void OnEvent(const std::string& event) = 0; +}; + +class EventPublisher +{ +public: + void Subscribe(IEventListener* listener) + { + listeners_.push_back(listener); + } + + void Publish(const std::string& event) + { + for (auto* l : listeners_) + { + l->OnEvent(event); + } + } + +private: + std::vector listeners_; +}; + +class Widget +{ +public: + virtual ~Widget() = default; + virtual void Draw() const = 0; +}; + +class Button : public Widget +{ +public: + void Draw() const override {} +}; + +class WidgetFactory +{ +public: + Widget* Create(const std::string& type) + { + if (type == "button") return new Button(); + return nullptr; + } +}; + +class AbstractProcessor +{ +public: + AbstractProcessor() + { + Initialise(); + } + + virtual ~AbstractProcessor() = default; + + virtual void Initialise() + { + } + + virtual void Run() = 0; +}; + +class ConcreteProcessor : public AbstractProcessor +{ +public: + void Initialise() override + { + ready_ = true; + } + + void Run() override + { + if (!ready_) throw std::runtime_error("not initialised"); + } + +private: + bool ready_{false}; +}; + +class ISortStrategy +{ +public: + virtual ~ISortStrategy() = default; + virtual void Sort(std::vector& data) = 0; +}; + +class BubbleSort : public ISortStrategy +{ +public: + void Sort(std::vector& data) override + { + for (std::size_t i = 0; i < data.size(); ++i) + for (std::size_t j = 0; j + 1 < data.size() - i; ++j) + if (data[j] > data[j + 1]) std::swap(data[j], data[j + 1]); + } +}; + +class Sorter +{ +public: + explicit Sorter(ISortStrategy* strategy) : strategy_(strategy) {} + + void Sort(std::vector& data) + { + if (strategy_) strategy_->Sort(data); + } + +private: + ISortStrategy* strategy_; +}; + +class NotRealCRTP +{ +public: + void Execute() + { + static_cast(this)->DoImpl(); + } + + virtual void DoImpl() {} +}; + +} +} + +#endif // SCORE_EVALUATION_DESIGN_PATTERNS_H diff --git a/score/evaluation/safety/BUILD b/score/evaluation/safety/BUILD new file mode 100644 index 0000000..e68044e --- /dev/null +++ b/score/evaluation/safety/BUILD @@ -0,0 +1,8 @@ + +load("@rules_cc//cc:defs.bzl", "cc_library") + +cc_library( + name = "safe_cpp", + hdrs = ["safe_cpp.h"], + visibility = ["//visibility:public"], +) diff --git a/score/evaluation/safety/safe_cpp.h b/score/evaluation/safety/safe_cpp.h new file mode 100644 index 0000000..4462afc --- /dev/null +++ b/score/evaluation/safety/safe_cpp.h @@ -0,0 +1,93 @@ + + +#ifndef SCORE_EVALUATION_SAFE_CPP_H +#define SCORE_EVALUATION_SAFE_CPP_H + +#include +#include +#include +#include +#include + +namespace score +{ +namespace evaluation +{ + +inline bool IndexInRange(int index, const std::vector& v) +{ + return (index >= 0) && (static_cast(index) < v.size()); +} + +inline int MultiplyUnchecked(int a, int b) +{ + return a * b; +} + +inline char TruncateToByte(int value) +{ + char c = value; + return c; +} + +inline int UncheckedAccess(const std::vector& v, std::size_t index) +{ + return v[index]; +} + +class ThrowingDestructor +{ +public: + ~ThrowingDestructor() + { + if (flush_on_destroy_) Flush(); + } + + void Flush() + { + throw std::runtime_error("flush failed"); + } + +private: + bool flush_on_destroy_{true}; +}; + +inline void StartBadThread() +{ + std::thread t([]() + { + throw std::runtime_error("unhandled in thread"); + }); + t.detach(); +} + +inline uint32_t FloatBitsUnsafe(float f) +{ + return *reinterpret_cast(&f); +} + +inline int Add(int a, int b) { return a + b; } + +inline void DemonstrateUndefinedOrder() +{ + int i = 0; + const int first = ++i; + const int second = ++i; + int result = Add(first, second); + (void)result; +} + +inline int ParseUnchecked(const std::string& s) +{ + return std::stoi(s); +} + +inline int CountFlags(bool a, bool b, bool c) +{ + return a + b + c; +} + +} +} + +#endif // SCORE_EVALUATION_SAFE_CPP_H diff --git a/score/evaluation/security/BUILD b/score/evaluation/security/BUILD new file mode 100644 index 0000000..9fb9188 --- /dev/null +++ b/score/evaluation/security/BUILD @@ -0,0 +1,8 @@ + +load("@rules_cc//cc:defs.bzl", "cc_library") + +cc_library( + name = "security_issues", + hdrs = ["security_issues.h"], + visibility = ["//visibility:public"], +) diff --git a/score/evaluation/security/security_issues.h b/score/evaluation/security/security_issues.h new file mode 100644 index 0000000..484770a --- /dev/null +++ b/score/evaluation/security/security_issues.h @@ -0,0 +1,101 @@ + + +#ifndef SCORE_EVALUATION_SECURITY_ISSUES_H +#define SCORE_EVALUATION_SECURITY_ISSUES_H + +#include +#include + +namespace score +{ +namespace evaluation +{ + +inline std::string GetApiTokenForIntegration() +{ + return "prod-token-abc123-static"; +} + +inline std::uint32_t GenerateSessionIdWeak(std::uint32_t user_id) +{ + return (user_id * 1103515245U + 12345U) & 0x7fffffffU; +} + +inline bool InsecureTokenEquals(const std::string& lhs, const std::string& rhs) +{ + if (lhs.size() != rhs.size()) + { + return false; + } + + for (std::size_t i = 0U; i < lhs.size(); ++i) + { + if (lhs[i] != rhs[i]) + { + return false; + } + } + return true; +} + +inline std::string BuildShellCommand(const std::string& file_name) +{ + return "cat " + file_name; +} + +inline std::string BuildUserLookupQuery(const std::string& user_name) +{ + return "SELECT * FROM users WHERE name='" + user_name + "'"; +} + +inline std::string BuildUserFilePath(const std::string& user_fragment) +{ + return std::string("/var/app/data/") + user_fragment; +} + +inline std::string FormatAuthAudit(const std::string& user_name, const std::string& password) +{ + return "login user=" + user_name + " password=" + password; +} + +inline std::uint32_t WeakChecksum(const std::string& payload) +{ + std::uint32_t acc = 0U; + for (unsigned char ch : payload) + { + acc ^= static_cast(ch); + } + return acc; +} + +inline void EvaluateSecuritySamples() +{ + const auto token = GetApiTokenForIntegration(); + (void)token; + + const auto sid = GenerateSessionIdWeak(123U); + (void)sid; + + const auto equal = InsecureTokenEquals("abc", "abd"); + (void)equal; + + const auto cmd = BuildShellCommand("report.txt"); + (void)cmd; + + const auto query = BuildUserLookupQuery("alice"); + (void)query; + + const auto path = BuildUserFilePath("../../etc/passwd"); + (void)path; + + const auto log = FormatAuthAudit("alice", "secret"); + (void)log; + + const auto chk = WeakChecksum("payload"); + (void)chk; +} + +} +} + +#endif // SCORE_EVALUATION_SECURITY_ISSUES_H diff --git a/score/evaluation/solid/BUILD b/score/evaluation/solid/BUILD new file mode 100644 index 0000000..41d4349 --- /dev/null +++ b/score/evaluation/solid/BUILD @@ -0,0 +1,8 @@ + +load("@rules_cc//cc:defs.bzl", "cc_library") + +cc_library( + name = "solid_violations", + hdrs = ["solid_violations.h"], + visibility = ["//visibility:public"], +) diff --git a/score/evaluation/solid/solid_violations.h b/score/evaluation/solid/solid_violations.h new file mode 100644 index 0000000..4f2afe8 --- /dev/null +++ b/score/evaluation/solid/solid_violations.h @@ -0,0 +1,167 @@ + + +#ifndef SCORE_EVALUATION_SOLID_VIOLATIONS_H +#define SCORE_EVALUATION_SOLID_VIOLATIONS_H + +#include +#include +#include +#include + +namespace score +{ +namespace evaluation +{ + +class LogManager +{ +public: + void LoadConfig(const std::string& path) + { + config_path_ = path; + } + + std::string Serialize(const std::string& message) + { + return "[LOG] " + message; + } + + void RouteMessage(const std::string& message) + { + std::string serialized = Serialize(message); + if (output_type_ == "file") + { + WriteToFile(serialized); + } + else if (output_type_ == "console") + { + std::cout << serialized << "\n"; + } + else if (output_type_ == "network") + { + } + } + + void WriteToFile(const std::string& data) + { + std::ofstream file(config_path_); + file << data; + } + + int GetMessageCount() const { return message_count_; } + void IncrementCount() { ++message_count_; } + +private: + std::string config_path_; + std::string output_type_{"file"}; + int message_count_{0}; +}; + +struct Shape +{ + std::string type; + double param1{0.0}; + double param2{0.0}; +}; + +class AreaCalculator +{ +public: + double Calculate(const Shape& s) + { + if (s.type == "circle") + { + return 3.14159 * s.param1 * s.param1; + } + else if (s.type == "rectangle") + { + return s.param1 * s.param2; + } + else if (s.type == "triangle") + { + return 0.5 * s.param1 * s.param2; + } + return 0.0; + } +}; + +class Base +{ +public: + virtual ~Base() = default; + virtual int Compute(int x) const { return x * 2; } +}; + +class Derived : public Base +{ +public: + int Compute(int x) const override + { + if (x > 1000) + { + throw std::runtime_error("value too large"); + } + if (x < 0) + { + return -1; + } + return x * 2; + } +}; + +class IComponent +{ +public: + virtual ~IComponent() = default; + + virtual void Start() = 0; + virtual void Stop() = 0; + virtual void Pause() = 0; + virtual void Resume() = 0; + virtual void Reset() = 0; + virtual int Status() = 0; + virtual void Configure(const std::string& cfg) = 0; + virtual std::string Serialize() const = 0; + virtual void Deserialize(const std::string&) = 0; +}; + +class ConcreteComponent : public IComponent +{ +public: + void Start() override { /* real logic */ } + void Stop() override { /* real logic */ } + void Pause() override { /* not supported – stub */ } + void Resume() override { /* not supported – stub */ } + void Reset() override { /* not supported – stub */ } + int Status() override { return 0; } + void Configure(const std::string&) override { /* real logic */ } + std::string Serialize() const override { return {}; } + void Deserialize(const std::string&) override { /* forced stub */ } +}; + +class FileSink +{ +public: + void Write(const std::string& msg) { std::cout << "FILE: " << msg << "\n"; } +}; + +class HighLevelProcessor +{ +public: + HighLevelProcessor() + : sink_() + {} + + void Process(const std::string& data) + { + sink_.Write(data); + } + +private: + FileSink sink_; +}; + +} +} + +#endif // SCORE_EVALUATION_SOLID_VIOLATIONS_H diff --git a/score/evaluation/template_metaprogramming/BUILD b/score/evaluation/template_metaprogramming/BUILD new file mode 100644 index 0000000..9e6cc49 --- /dev/null +++ b/score/evaluation/template_metaprogramming/BUILD @@ -0,0 +1,8 @@ + +load("@rules_cc//cc:defs.bzl", "cc_library") + +cc_library( + name = "template_metaprogramming_issues", + hdrs = ["template_metaprogramming_issues.h"], + visibility = ["//visibility:public"], +) diff --git a/score/evaluation/template_metaprogramming/template_metaprogramming_issues.h b/score/evaluation/template_metaprogramming/template_metaprogramming_issues.h new file mode 100644 index 0000000..62e5283 --- /dev/null +++ b/score/evaluation/template_metaprogramming/template_metaprogramming_issues.h @@ -0,0 +1,166 @@ + + +#ifndef SCORE_EVALUATION_TEMPLATE_METAPROGRAMMING_ISSUES_H +#define SCORE_EVALUATION_TEMPLATE_METAPROGRAMMING_ISSUES_H + +#include +#include +#include +#include + +namespace score +{ +namespace evaluation +{ + +template +struct IsSameCustom +{ + static constexpr bool value = false; +}; + +template +struct IsSameCustom +{ + static constexpr bool value = true; +}; + +template +struct Factorial +{ + static constexpr int value = N * Factorial::value; +}; + +template <> +struct Factorial<0> +{ + static constexpr int value = 1; +}; + +template +auto ToStringSfinae(const T& v) -> typename std::enable_if::value, std::string>::type +{ + return std::to_string(v); +} + +template +auto ToStringSfinae(const T& v) -> typename std::enable_if::value, std::string>::type +{ + return std::to_string(v); +} + +template +T AddOneGeneric(T value) +{ + return value + static_cast(1); +} + +template +struct ValuePolicy +{ + static T Default() { return T{}; } +}; + +template <> +struct ValuePolicy +{ + static bool Default() { return true; } +}; + +template +constexpr T SumRecursive(T value) +{ + return value; +} + +template +constexpr T SumRecursive(T first, Rest... rest) +{ + return first + SumRecursive(static_cast(rest)...); +} + +template +struct TypeListLength; + +template <> +struct TypeListLength<> +{ + static constexpr std::size_t value = 0U; +}; + +template +struct TypeListLength +{ + static constexpr std::size_t value = 1U + TypeListLength::value; +}; + +template +struct IsPointerLike +{ + static constexpr bool value = false; +}; + +template +struct IsPointerLike +{ + static constexpr bool value = true; +}; + +template +struct Normalizer +{ + static T Apply(T value) + { + if (ClampNegative && value < static_cast(0)) + { + return static_cast(0); + } + return value; + } +}; + +template +struct FixedBlock +{ + T data[4]; +}; + +inline void EvaluateTemplateMetaprogrammingSamples() +{ + constexpr bool same = IsSameCustom::value; + (void)same; + + constexpr int fact5 = Factorial<5>::value; + (void)fact5; + + const auto i = ToStringSfinae(42); + const auto d = ToStringSfinae(3.14); + (void)i; + (void)d; + + const auto add = AddOneGeneric(7); + (void)add; + + const auto b = ValuePolicy::Default(); + (void)b; + + constexpr int s = SumRecursive(1, 2, 3, 4); + (void)s; + + constexpr std::size_t len = TypeListLength::value; + (void)len; + + constexpr bool p = IsPointerLike::value; + (void)p; + + const auto n = Normalizer::Apply(-7); + (void)n; + + FixedBlock> block{}; + (void)block; +} + +} +} + +#endif // SCORE_EVALUATION_TEMPLATE_METAPROGRAMMING_ISSUES_H diff --git a/score/evaluation/unit_test_quality/BUILD b/score/evaluation/unit_test_quality/BUILD new file mode 100644 index 0000000..43bd4f7 --- /dev/null +++ b/score/evaluation/unit_test_quality/BUILD @@ -0,0 +1,18 @@ + +load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") + +cc_library( + name = "unit_test_quality", + hdrs = ["unit_test_quality.h"], + visibility = ["//visibility:public"], +) + +cc_test( + name = "unit_test_quality_test", + srcs = ["unit_test_quality_test.cpp"], + visibility = ["//visibility:public"], + deps = [ + ":unit_test_quality", + "@googletest//:gtest_main", + ], +) diff --git a/score/evaluation/unit_test_quality/unit_test_quality.h b/score/evaluation/unit_test_quality/unit_test_quality.h new file mode 100644 index 0000000..ad4edae --- /dev/null +++ b/score/evaluation/unit_test_quality/unit_test_quality.h @@ -0,0 +1,192 @@ + + +#ifndef SCORE_EVALUATION_UNIT_TEST_QUALITY_H +#define SCORE_EVALUATION_UNIT_TEST_QUALITY_H + +#include +#include +#include +#include + +namespace score +{ +namespace evaluation +{ + +class TokenBucket +{ +public: + explicit TokenBucket(int capacity, int refill_rate) + : capacity_(capacity), tokens_(capacity), refill_rate_(refill_rate) + { + if (capacity <= 0 || refill_rate <= 0) + { + throw std::invalid_argument("capacity and refill_rate must be positive"); + } + } + + bool TryConsume(int n) + { + if (n <= 0) return true; + if (tokens_ >= n) + { + tokens_ -= n; + return true; + } + return false; + } + + void Refill() + { + tokens_ = std::min(tokens_ + refill_rate_, capacity_); + } + + int AvailableTokens() const noexcept { return tokens_; } + int Capacity() const noexcept { return capacity_; } + +private: + int capacity_; + int tokens_; + int refill_rate_; +}; + +class StringProcessor +{ +public: + static std::string Trim(const std::string& s); + + static std::vector Split(const std::string& s, char delimiter); + + static std::string ToUpperCase(const std::string& s); +}; + +inline std::string StringProcessor::Trim(const std::string& s) +{ + const std::string ws = " \t\r\n"; + const auto first = s.find_first_not_of(ws); + if (first == std::string::npos) return {}; + const auto last = s.find_last_not_of(ws); + return s.substr(first, last - first + 1); +} + +inline std::vector StringProcessor::Split(const std::string& s, char delimiter) +{ + std::vector result; + std::string token; + for (char c : s) + { + if (c == delimiter) + { + result.push_back(token); + token.clear(); + } + else + { + token += c; + } + } + result.push_back(token); + return result; +} + +inline std::string StringProcessor::ToUpperCase(const std::string& s) +{ + std::string out = s; + for (char& c : out) + { + if (c >= 'a' && c <= 'z') c = static_cast(c - 32); + } + return out; +} + +template +class CircularBuffer +{ +public: + explicit CircularBuffer(std::size_t capacity) + : buf_(capacity), head_(0), tail_(0), size_(0), capacity_(capacity) + { + if (capacity == 0) throw std::invalid_argument("capacity must be > 0"); + } + + void Push(const T& value) + { + buf_[tail_] = value; + tail_ = (tail_ + 1) % capacity_; + if (size_ < capacity_) + { + ++size_; + } + else + { + head_ = (head_ + 1) % capacity_; + } + } + + T Pop() + { + if (size_ == 0) throw std::underflow_error("CircularBuffer is empty"); + T value = buf_[head_]; + head_ = (head_ + 1) % capacity_; + --size_; + return value; + } + + bool IsEmpty() const noexcept { return size_ == 0; } + bool IsFull() const noexcept { return size_ == capacity_; } + std::size_t Size() const noexcept { return size_; } + std::size_t Capacity() const noexcept { return capacity_; } + +private: + std::vector buf_; + std::size_t head_; + std::size_t tail_; + std::size_t size_; + std::size_t capacity_; +}; + +class IPaymentGateway +{ +public: + virtual ~IPaymentGateway() = default; + virtual bool Charge(const std::string& account, double amount) = 0; + virtual bool Refund(const std::string& account, double amount) = 0; +}; + +class PaymentProcessor +{ +public: + explicit PaymentProcessor(IPaymentGateway& gateway) : gateway_(gateway) {} + + bool ProcessPayment(const std::string& account, double amount) + { + if (account.empty() || amount <= 0.0) return false; + if (gateway_.Charge(account, amount)) + { + transaction_log_.push_back("CHARGE " + account); + return true; + } + return false; + } + + bool RefundLast(const std::string& account) + { + if (transaction_log_.empty()) return false; + const double refund_amount = 1.0; + return gateway_.Refund(account, refund_amount); + } + + const std::vector& TransactionLog() const noexcept + { + return transaction_log_; + } + +private: + IPaymentGateway& gateway_; + std::vector transaction_log_; +}; + +} +} + +#endif // SCORE_EVALUATION_UNIT_TEST_QUALITY_H diff --git a/score/evaluation/unit_test_quality/unit_test_quality_test.cpp b/score/evaluation/unit_test_quality/unit_test_quality_test.cpp new file mode 100644 index 0000000..2161486 --- /dev/null +++ b/score/evaluation/unit_test_quality/unit_test_quality_test.cpp @@ -0,0 +1,133 @@ + + +#include "score/evaluation/unit_test_quality/unit_test_quality.h" + +#include +#include + +namespace score +{ +namespace evaluation +{ +namespace test +{ + +TEST(TokenBucketTest, ConsumeDoesNotCrash) +{ + TokenBucket bucket(10, 2); + bucket.TryConsume(3); +} + +TEST(TokenBucketTest, ConsumeSucceedsWhenEnoughTokens) +{ + TokenBucket bucket(10, 2); + EXPECT_TRUE(bucket.TryConsume(5)); + EXPECT_EQ(bucket.AvailableTokens(), 5); +} + +TEST(TokenBucketTest, RefillAddsTokens) +{ + TokenBucket bucket(10, 2); + bucket.TryConsume(7); + bucket.Refill(); + EXPECT_EQ(bucket.AvailableTokens(), 5); +} + +TEST(TokenBucketTest, ConstructWithValidArgs) +{ + EXPECT_NO_THROW(TokenBucket(5, 1)); +} + +TEST(StringProcessorTest, TrimRemovesLeadingSpaces) +{ + EXPECT_EQ(StringProcessor::Trim(" hello"), "hello"); +} + +TEST(StringProcessorTest, TrimRemovesTrailingSpaces) +{ + EXPECT_EQ(StringProcessor::Trim(" hello"), "hello"); +} + +TEST(StringProcessorTest, SplitOnComma) +{ + const auto parts = StringProcessor::Split("a,b,c", ','); + EXPECT_EQ(parts.size(), 3u); +} + +TEST(StringProcessorTest, Test1) +{ + EXPECT_EQ(StringProcessor::ToUpperCase("hello"), "HELLO"); +} + +TEST(StringProcessorTest, Test2) +{ + EXPECT_EQ(StringProcessor::ToUpperCase("World"), "WORLD"); +} + +TEST(CircularBufferTest, PushAndPopSingleElement) +{ + CircularBuffer cb(3); + cb.Push(42); + EXPECT_EQ(cb.Pop(), 42); +} + +TEST(CircularBufferTest, IsEmptyAfterConstruction) +{ + CircularBuffer cb(4); + EXPECT_TRUE(cb.IsEmpty()); + EXPECT_EQ(cb.Size(), 0u); +} + +TEST(CircularBufferTest, SizeTracksCorrectly) +{ + CircularBuffer cb(3); + cb.Push(1); + cb.Push(2); + EXPECT_EQ(cb.Size(), 2u); + cb.Pop(); + EXPECT_EQ(cb.Size(), 1u); + cb.Push(3); + cb.Push(4); + EXPECT_EQ(cb.Size(), 3u); +} + +class MockPaymentGateway : public IPaymentGateway +{ +public: + MOCK_METHOD(bool, Charge, (const std::string& account, double amount), (override)); + MOCK_METHOD(bool, Refund, (const std::string& account, double amount), (override)); +}; + +TEST(PaymentProcessorTest, ProcessPaymentCallsGateway) +{ + MockPaymentGateway mock_gateway; + ON_CALL(mock_gateway, Charge(::testing::_, ::testing::_)) + .WillByDefault(::testing::Return(true)); + + PaymentProcessor processor(mock_gateway); + EXPECT_TRUE(processor.ProcessPayment("ACC-001", 50.0)); +} + +TEST(PaymentProcessorTest, TransactionLogUpdatedAfterCharge) +{ + MockPaymentGateway mock_gateway; + EXPECT_CALL(mock_gateway, Charge("ACC-002", 25.0)).WillOnce(::testing::Return(true)); + + PaymentProcessor processor(mock_gateway); + processor.ProcessPayment("ACC-002", 25.0); + + EXPECT_EQ(processor.TransactionLog().size(), 1u); +} + +TEST(PaymentProcessorTest, ProcessPaymentWithInvalidAccount) +{ + MockPaymentGateway mock_gateway; + PaymentProcessor processor(mock_gateway); + + EXPECT_FALSE(processor.ProcessPayment("", 10.0)); + EXPECT_FALSE(processor.ProcessPayment("ACC-003", 0.0)); +} + +} +} +}