diff --git a/MathLab/MathLab.cpp b/MathLab/MathLab.cpp new file mode 100644 index 0000000..eac9488 Binary files /dev/null and b/MathLab/MathLab.cpp differ diff --git a/MathLab/MathLab.sln b/MathLab/MathLab.sln new file mode 100644 index 0000000..077bff1 --- /dev/null +++ b/MathLab/MathLab.sln @@ -0,0 +1,41 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.28010.2041 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MathLab", "MathLab.vcxproj", "{3314C41D-F478-4CB2-A408-F0B113C7AF2C}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MathLabTests", "..\MathLabTests\MathLabTests.vcxproj", "{5DBEC0A4-DD81-4B5D-81E7-22940933E8F3}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3314C41D-F478-4CB2-A408-F0B113C7AF2C}.Debug|x64.ActiveCfg = Debug|x64 + {3314C41D-F478-4CB2-A408-F0B113C7AF2C}.Debug|x64.Build.0 = Debug|x64 + {3314C41D-F478-4CB2-A408-F0B113C7AF2C}.Debug|x86.ActiveCfg = Debug|Win32 + {3314C41D-F478-4CB2-A408-F0B113C7AF2C}.Debug|x86.Build.0 = Debug|Win32 + {3314C41D-F478-4CB2-A408-F0B113C7AF2C}.Release|x64.ActiveCfg = Release|x64 + {3314C41D-F478-4CB2-A408-F0B113C7AF2C}.Release|x64.Build.0 = Release|x64 + {3314C41D-F478-4CB2-A408-F0B113C7AF2C}.Release|x86.ActiveCfg = Release|Win32 + {3314C41D-F478-4CB2-A408-F0B113C7AF2C}.Release|x86.Build.0 = Release|Win32 + {5DBEC0A4-DD81-4B5D-81E7-22940933E8F3}.Debug|x64.ActiveCfg = Debug|x64 + {5DBEC0A4-DD81-4B5D-81E7-22940933E8F3}.Debug|x64.Build.0 = Debug|x64 + {5DBEC0A4-DD81-4B5D-81E7-22940933E8F3}.Debug|x86.ActiveCfg = Debug|Win32 + {5DBEC0A4-DD81-4B5D-81E7-22940933E8F3}.Debug|x86.Build.0 = Debug|Win32 + {5DBEC0A4-DD81-4B5D-81E7-22940933E8F3}.Release|x64.ActiveCfg = Release|x64 + {5DBEC0A4-DD81-4B5D-81E7-22940933E8F3}.Release|x64.Build.0 = Release|x64 + {5DBEC0A4-DD81-4B5D-81E7-22940933E8F3}.Release|x86.ActiveCfg = Release|Win32 + {5DBEC0A4-DD81-4B5D-81E7-22940933E8F3}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {9A5CE5FA-B1B1-4AF3-9E24-6C95A4696F5F} + EndGlobalSection +EndGlobal diff --git a/MathLab/MathLab.vcxproj b/MathLab/MathLab.vcxproj new file mode 100644 index 0000000..4c761d5 --- /dev/null +++ b/MathLab/MathLab.vcxproj @@ -0,0 +1,176 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {3314C41D-F478-4CB2-A408-F0B113C7AF2C} + Win32Proj + MathLab + 10.0.17134.0 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + NotUsing + Level3 + Disabled + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + stdcpp17 + + + Console + true + + + + + NotUsing + Level3 + Disabled + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + stdcpp17 + + + Console + true + + + + + NotUsing + Level3 + MaxSpeed + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + stdcpp17 + + + Console + true + true + true + + + + + NotUsing + Level3 + MaxSpeed + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + stdcpp17 + + + Console + true + true + true + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/MathLab/MathLab.vcxproj.filters b/MathLab/MathLab.vcxproj.filters new file mode 100644 index 0000000..8495eb4 --- /dev/null +++ b/MathLab/MathLab.vcxproj.filters @@ -0,0 +1,45 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/MathLab/block_sequence.cpp b/MathLab/block_sequence.cpp new file mode 100644 index 0000000..6f90347 --- /dev/null +++ b/MathLab/block_sequence.cpp @@ -0,0 +1,77 @@ +#include "block_sequence.h" +#include +#include + +namespace { + std::pair extract_type(const std::string& line) + { + std::string block_type; + std::istringstream line_stream(line); + line_stream >> block_type; + return std::make_pair(std::move(block_type), std::move(line_stream)); + } +} + +namespace mathlab { + + block_sequence::block_sequence(factory& factory) : factory_(factory) {} + + std::string block_sequence::append_from(std::istream& input_stream) { + std::string block_type; + std::istringstream constants; + std::string invalid_lines; + std::string line; + while (!input_stream.eof()) { + std::getline(input_stream, line); + std::tie(block_type, constants) = extract_type(line); + if (!block_type.empty()) { + std::unique_ptr block; + try { + block = factory_.create(block_type, constants); + } + catch (std::invalid_argument&) { + // Deliberately empty - handle same as unknown command + } + if (block != nullptr) + blocks_.push_back(std::make_pair(std::move(block_type), std::move(block))); + else + invalid_lines += line + "\n"; + } + } + return invalid_lines; + } + + std::string block_sequence::load_from(std::istream& input_stream) { + blocks_.clear(); + return append_from(input_stream); + } + + std::ostream& block_sequence::dump(std::ostream& to_stream, bool with_line_numbers) const { + int position = 1; + for (auto& name_with_block : blocks_) { + if (with_line_numbers) + to_stream << position++ << ": "; + to_stream << name_with_block.first << ' ' << *name_with_block.second << std::endl; + } + return to_stream; + } + + double block_sequence::eval(double input) const { + return std::accumulate( + blocks_.cbegin(), + blocks_.cend(), + input, + [](double val, const std::pair> &name_with_block) { return name_with_block.second->eval(val); }); + } + + void block_sequence::remove_at(unsigned index) { + if (index < blocks_.size()) + blocks_.erase(blocks_.begin() + index); + } + + void block_sequence::move_to_beginning(unsigned index) { + if (index < blocks_.size() && index > 0) { + std::swap(blocks_[0], blocks_[index]); + } + } +} diff --git a/MathLab/block_sequence.h b/MathLab/block_sequence.h new file mode 100644 index 0000000..ce810a2 --- /dev/null +++ b/MathLab/block_sequence.h @@ -0,0 +1,24 @@ +#pragma once +#include "factory.h" +#include +#include +#include + +namespace mathlab { + class block_sequence { + std::vector>> blocks_; + factory& factory_; + public: + block_sequence(factory& factory); + // Appends one or more block from supplied stream + // Returns invalid lines + std::string append_from(std::istream& input_stream); + // Load sequence from supplied stream + // Returns invalid lines + std::string load_from(std::istream& input_stream); + std::ostream& dump(std::ostream& to_stream, bool with_line_numbers) const; + double eval(double input) const; + void remove_at(unsigned index); + void move_to_beginning(unsigned index); + }; +} diff --git a/MathLab/blocks.cpp b/MathLab/blocks.cpp new file mode 100644 index 0000000..46892ad --- /dev/null +++ b/MathLab/blocks.cpp @@ -0,0 +1,9 @@ +#include "blocks.h" + +namespace mathlab +{ + std::ostream& operator<<(std::ostream& stream, const block& block) { + block.dump(stream); + return stream; + } +} \ No newline at end of file diff --git a/MathLab/blocks.h b/MathLab/blocks.h new file mode 100644 index 0000000..a200c30 --- /dev/null +++ b/MathLab/blocks.h @@ -0,0 +1,69 @@ +#pragma once +#include +#include +#include "tuple_serialization.h" + +namespace mathlab +{ + // block interface + struct block { + virtual double eval(double input) const = 0; + virtual void dump(std::ostream& to_stream) const = 0; + virtual ~block() = default; + }; + + template + struct block_with_constants : block { + double eval(double input) const override { + auto input_with_constants = std::tuple_cat(std::tuple(input), constants_); + return std::apply(callable_, input_with_constants); + } + // Serializes all constants to stream + void dump(std::ostream& to) const override { + tuple_serialization::serialize(to, constants_); + } + // Creates new block from stream + // throws invalid_argument if stream cannot be converted + template + static std::unique_ptr create_from_stream(std::istream& input_stream) { + std::tuple to; + tuple_serialization::deserialize(input_stream, to); + TBlock* block = std::apply(create, to); + return std::unique_ptr(block); + } + protected: + block_with_constants(TCallable callable, TArgs... args) : callable_(std::move(callable)), constants_(std::tie(args...)) {} + private: + std::tuple constants_; + TCallable callable_; + }; + + std::ostream& operator<<(std::ostream& stream, const block& block); + + // Supported blocks: + + struct identity final : block_with_constants> { + identity() : block_with_constants([](auto input) { return input; }) {} + }; + + struct addition final : block_with_constants, double> { + addition(double constant) : block_with_constants(std::plus(), constant) {} + }; + + struct multiplication final : block_with_constants, double> { + multiplication(double constant) : block_with_constants(std::multiplies(), constant) {} + }; + + struct power final : block_with_constants, double> { + power(double constant) : block_with_constants([](double input, double other) { return std::pow(input, other); }, constant) {} + }; + + struct condition final : block_with_constants, double> { + condition(double constant) : + block_with_constants([](double input, double other) { return input < other ? -1 : (input == other ? 0 : 1); }, constant) {} + }; + + struct limit final : block_with_constants, double, double> { + limit(double lower, double upper) : block_with_constants(std::clamp, lower, upper) {} + }; +} diff --git a/MathLab/factory.cpp b/MathLab/factory.cpp new file mode 100644 index 0000000..10c9810 --- /dev/null +++ b/MathLab/factory.cpp @@ -0,0 +1,32 @@ +#include "factory.h" + +namespace mathlab +{ + template + void factory::register_block(const std::string& type_name) { + types_[type_name] = TBlock::template create_from_stream; + } + + std::unique_ptr factory::create(const std::string& type_name, std::istream& stream) const { + const auto found = types_.find(type_name); + if (found != types_.end()) + return found->second(stream); + return std::unique_ptr(); + } + + std::ostream& factory::dump_registered(std::ostream& to_stream) { + for (auto pair : types_) + to_stream << pair.first << ' '; + return to_stream; + } + + void register_all_blocks(factory& factory) { + factory.register_block("identity"); + factory.register_block("addition"); + factory.register_block("multiplication"); + factory.register_block("power"); + factory.register_block("condition"); + factory.register_block("addition"); + factory.register_block("limit"); + } +} diff --git a/MathLab/factory.h b/MathLab/factory.h new file mode 100644 index 0000000..5ced743 --- /dev/null +++ b/MathLab/factory.h @@ -0,0 +1,20 @@ +#pragma once +#include "blocks.h" +#include +#include +#include + +namespace mathlab +{ + struct factory { + template void register_block(const std::string& type_name); + // Returns nullptr if block with given type is not registered + // Throws invalid_argument if block is known but input stream does not contain expected number of constants + std::unique_ptr create(const std::string& type_name, std::istream& stream) const; + std::ostream& dump_registered(std::ostream &to_stream); + private: + std::map(std::istream&)>> types_; + }; + + void register_all_blocks(factory& factory); +} \ No newline at end of file diff --git a/MathLab/tuple_serialization.h b/MathLab/tuple_serialization.h new file mode 100644 index 0000000..cbe7c8f --- /dev/null +++ b/MathLab/tuple_serialization.h @@ -0,0 +1,48 @@ +#pragma once +#include +#include +#include + +namespace mathlab { + + // Tuple serialization / de-serialization + // Type arguments: + // N - number of tuple members that are considered + // TArgs - tuple types (e.g. double, int => tuple + template + struct tuple_serialization { + + // Serializes first N tuple members to output stream + // A space is added after each member + static void serialize(std::ostream& to, const std::tuple& from) { + tuple_serialization::serialize(to, from); + to << std::get(from) << ' '; + } + + // De-serializes first N tuple members from input stream + // Throws std::invalid_argument when reading from stream fails + static void deserialize(std::istream& from, std::tuple& to) { + tuple_serialization::deserialize(from, to); + using type_n = typename std::tuple_element>::type; + type_n object; + from >> object; + if (! from) // bad-bit or eof + throw std::invalid_argument(std::string("Unable to read object from stream. Object type: ") + typeid(type_n).name()); + std::get(to) = object; + } + }; + + // Specialization for empty tuple, doing nothing + template + struct tuple_serialization<0, TArgs...> { + static void serialize(std::ostream& to, const std::tuple& from) {} + static void deserialize(std::istream& from, std::tuple& to) {} + }; + + // Helper function for constructing an object of type TBlock, using variable constructor arguments + template + T* create(TArgs ...args) { + return new T(args...); + } + +} \ No newline at end of file diff --git a/MathLabTests/MathLabTests.vcxproj b/MathLabTests/MathLabTests.vcxproj new file mode 100644 index 0000000..96b6ee8 --- /dev/null +++ b/MathLabTests/MathLabTests.vcxproj @@ -0,0 +1,179 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {5DBEC0A4-DD81-4B5D-81E7-22940933E8F3} + Win32Proj + MathLabTests + 10.0.17134.0 + NativeUnitTestProject + + + + DynamicLibrary + true + v141 + Unicode + false + + + DynamicLibrary + false + v141 + true + Unicode + false + + + DynamicLibrary + true + v141 + Unicode + false + + + DynamicLibrary + false + v141 + true + Unicode + false + + + + + + + + + + + + + + + + + + + + + true + + + true + + + true + + + true + + + + NotUsing + Level3 + Disabled + $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;%(PreprocessorDefinitions) + true + + stdcpp17 + + + Windows + $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) + + + + + NotUsing + Level3 + Disabled + $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) + _DEBUG;%(PreprocessorDefinitions) + true + + stdcpp17 + + + Windows + $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) + + + + + Level3 + NotUsing + MaxSpeed + true + true + $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;%(PreprocessorDefinitions) + true + + stdcpp17 + + + Windows + true + true + $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) + + + + + Level3 + NotUsing + MaxSpeed + true + true + $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) + NDEBUG;%(PreprocessorDefinitions) + true + + stdcpp17 + + + Windows + true + true + $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/MathLabTests/MathLabTests.vcxproj.filters b/MathLabTests/MathLabTests.vcxproj.filters new file mode 100644 index 0000000..8ef9a66 --- /dev/null +++ b/MathLabTests/MathLabTests.vcxproj.filters @@ -0,0 +1,51 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/MathLabTests/blockTests.cpp b/MathLabTests/blockTests.cpp new file mode 100644 index 0000000..0331543 --- /dev/null +++ b/MathLabTests/blockTests.cpp @@ -0,0 +1,138 @@ +#include "CppUnitTest.h" +#include "../MathLab/blocks.h" +#include + +using namespace Microsoft::VisualStudio::CppUnitTestFramework; + +namespace MathLabTests +{ + TEST_CLASS(block_types_directly_constructed) + { + public: + TEST_METHOD(identity_returns_input) + { + auto identity = mathlab::identity(); + Assert::AreEqual(3.14, identity.eval(3.14)); + } + + TEST_METHOD(addition_works) + { + auto addition = mathlab::addition(100.0); + Assert::AreEqual(103.14, addition.eval(3.14)); + } + + TEST_METHOD(multiplication_works) + { + auto multiplication = mathlab::multiplication(100.0); + Assert::AreEqual(314., multiplication.eval(3.14)); + } + + TEST_METHOD(power_works) + { + auto power = mathlab::power(3.); + Assert::AreEqual(15.625, power.eval(2.5)); + } + + TEST_METHOD(condition_returns_0_if_same_as_input) + { + auto condition = mathlab::condition(3.14); + Assert::AreEqual(0., condition.eval(3.14)); + } + + TEST_METHOD(condition_returns_minus_1_if_less_than_input) + { + auto condition = mathlab::condition(3.14); + Assert::AreEqual(-1., condition.eval(0.5)); + } + + TEST_METHOD(condition_returns_1_if_greater_than_input) + { + auto condition = mathlab::condition(3.14); + Assert::AreEqual(1., condition.eval(5.)); + } + + TEST_METHOD(limit_returns_value_if_within_limits) + { + auto limit = mathlab::limit(1., 100.); + Assert::AreEqual(50., limit.eval(50.)); + } + + TEST_METHOD(limit_returns_lower_if_value_is_lower) + { + auto limit = mathlab::limit(1., 100.); + Assert::AreEqual(1., limit.eval(-50.)); + } + + TEST_METHOD(limit_returns_upper_if_value_is_above) + { + auto limit = mathlab::limit(1., 100.); + Assert::AreEqual(100., limit.eval(200.)); + } + }; + + TEST_CLASS(blocks_can_be_created_from_stream_and_dumped_to_stream) + { + public: + TEST_METHOD(block_without_constants) + { + auto identity = mathlab::identity(); + std::ostringstream output_stream; + output_stream << identity; + Assert::AreEqual(std::string(""), output_stream.str()); + } + + TEST_METHOD(block_with_one_constant) + { + std::istringstream input("123.45"); + auto block = mathlab::addition::create_from_stream(input); + std::ostringstream output_stream; + output_stream << *block; + Assert::AreEqual(std::string("123.45 "), output_stream.str()); + } + + TEST_METHOD(block_with_one_constant_throws_if_no_input) + { + Assert::ExpectException([]() + { + std::istringstream input(""); + mathlab::multiplication::create_from_stream(input); + }); + } + + TEST_METHOD(block_with_one_constant_throws_in_case_of_wrong_input_type) + { + Assert::ExpectException([]() + { + std::istringstream input("Hello"); + mathlab::multiplication::create_from_stream(input); + }); + } + + TEST_METHOD(block_with_two_constants) + { + std::istringstream input("-123.45 666.66"); + auto limit = mathlab::limit::create_from_stream(input); + std::ostringstream output_stream; + output_stream << *limit; + Assert::AreEqual(std::string("-123.45 666.66 "), output_stream.str()); + } + + TEST_METHOD(block_with_two_constants_throws_if_no_input) + { + Assert::ExpectException([]() + { + std::istringstream input(""); + mathlab::limit::create_from_stream(input); + }); + } + + TEST_METHOD(block_with_two_constants_throws_in_case_of_wrong_input_type) + { + Assert::ExpectException([]() + { + std::istringstream input("Hello World"); + mathlab::multiplication::create_from_stream(input); + }); + } + }; +} \ No newline at end of file diff --git a/MathLabTests/block_sequenceTests.cpp b/MathLabTests/block_sequenceTests.cpp new file mode 100644 index 0000000..88145e1 --- /dev/null +++ b/MathLabTests/block_sequenceTests.cpp @@ -0,0 +1,102 @@ +#include "CppUnitTest.h" +#include "../MathLab/block_sequence.h" + +using namespace Microsoft::VisualStudio::CppUnitTestFramework; + +namespace MathLabTests +{ + TEST_CLASS(block_sequence_tests) + { + public: + TEST_METHOD(dump_empty_sequence) + { + mathlab::factory factory; + mathlab::block_sequence sequence(factory); + std::ostringstream output; + sequence.dump(output, false); + Assert::AreEqual(std::string(""), output.str()); + } + + TEST_METHOD(dump_sequence_with_line_numbers) + { + mathlab::factory factory; + factory.register_block("addition"); + mathlab::block_sequence sequence(factory); + std::istringstream input("addition 100\n"); + sequence.append_from(input); + + std::ostringstream output; + sequence.dump(output, true); + Assert::AreEqual(std::string("1: addition 100 \n"), output.str()); + } + + TEST_METHOD(append_multiple_and_eval) + { + mathlab::factory factory; + factory.register_block("add"); + factory.register_block("multiply"); + mathlab::block_sequence sequence(factory); + std::istringstream input("add 100.\nmultiply 2.\n"); + auto invalid_lines = sequence.append_from(input); + Assert::IsTrue(invalid_lines.empty()); + Assert::AreEqual(400., sequence.eval(100.)); + } + + TEST_METHOD(append_skips_invalid_input_and_returns_invalid_lines) + { + mathlab::factory factory; + factory.register_block("addition"); + factory.register_block("multiplication"); + mathlab::block_sequence sequence(factory); + std::istringstream input("dummy\naddition 100.\ndummy\nmultiplication 2.\ndummy\n"); + const auto invalid_lines = sequence.append_from(input); + Assert::AreEqual(std::string("dummy\ndummy\ndummy\n"), invalid_lines); + Assert::AreEqual(400., sequence.eval(100.)); + } + + TEST_METHOD(load_clears_sequence_and_appends) + { + mathlab::factory factory; + factory.register_block("addition"); + factory.register_block("multiplication"); + mathlab::block_sequence sequence(factory); + std::istringstream input1("addition 100.\n"); + sequence.append_from(input1); + Assert::AreEqual(103., sequence.eval(3.)); + std::istringstream input2("multiplication 2.\n"); + sequence.load_from(input2); + Assert::AreEqual(6., sequence.eval(3.)); + } + + TEST_METHOD(remove_from_sequence) + { + mathlab::factory factory; + factory.register_block("addition"); + factory.register_block("multiplication"); + mathlab::block_sequence sequence(factory); + std::istringstream input("addition 100.\nmultiplication 2.\n"); + sequence.append_from(input); + sequence.remove_at(0); + + std::ostringstream output; + sequence.dump(output, false); + Assert::AreEqual(std::string("multiplication 2 \n"), output.str()); + } + + TEST_METHOD(move_to_beginning_works) + { + mathlab::factory factory; + factory.register_block("addition"); + factory.register_block("multiplication"); + mathlab::block_sequence sequence(factory); + std::istringstream input("addition 100.\nmultiplication 2.\n"); + sequence.append_from(input); + sequence.move_to_beginning(1); + + std::ostringstream output; + sequence.dump(output, false); + // ReSharper restore CppExpressionWithoutSideEffects + Assert::AreEqual(std::string("multiplication 2 \naddition 100 \n"), output.str()); + } + }; +} \ No newline at end of file diff --git a/MathLabTests/factoryTests.cpp b/MathLabTests/factoryTests.cpp new file mode 100644 index 0000000..4a710cf --- /dev/null +++ b/MathLabTests/factoryTests.cpp @@ -0,0 +1,48 @@ +#include "CppUnitTest.h" +#include "../MathLab/factory.h" + +using namespace Microsoft::VisualStudio::CppUnitTestFramework; + +namespace MathLabTests +{ + TEST_CLASS(factory_tests) + { + public: + TEST_METHOD(dump_after_register) + { + std::ostringstream output; + auto factory = mathlab::factory(); + factory.dump_registered(output); + Assert::AreEqual(std::string(""), output.str()); + factory.register_block("dummy1"); + factory.register_block("dummy2"); + factory.dump_registered(output); + Assert::AreEqual(std::string("dummy1 dummy2 "), output.str()); + } + + TEST_METHOD(create_for_non_registered_fails) + { + std::istringstream input; + auto factory = mathlab::factory(); + const auto ptr = factory.create("dummy", input); + Assert::IsFalse(static_cast(ptr)); + } + + TEST_METHOD(create_after_registration) + { + std::istringstream input("100"); + auto factory = mathlab::factory(); + factory.register_block("dummy"); + auto ptr = factory.create("dummy", input); + Assert::AreEqual(223.45, ptr->eval(123.45)); + } + + TEST_METHOD(create_after_registration_with_wrong_number_of_constants_throws) + { + std::istringstream input; + auto factory = mathlab::factory(); + factory.register_block("dummy"); + Assert::ExpectException([&factory, &input]() { factory.create("dummy", input); }); + } + }; +} \ No newline at end of file diff --git a/MathLabTests/targetver.h b/MathLabTests/targetver.h new file mode 100644 index 0000000..90e767b --- /dev/null +++ b/MathLabTests/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include