init rework
This commit is contained in:
parent
96e6514337
commit
a292364745
13 changed files with 26 additions and 509 deletions
|
@ -201,7 +201,8 @@ if(${SVA_BUILD_TEST})
|
||||||
GIT_REPOSITORY https://github.com/google/googletest.git
|
GIT_REPOSITORY https://github.com/google/googletest.git
|
||||||
GIT_TAG release-1.12.1
|
GIT_TAG release-1.12.1
|
||||||
)
|
)
|
||||||
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) # Prevent shared CRT issues on Windows
|
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) # Prevent shared CRT issues on Windows
|
||||||
|
set(INSTALL_GTEST OFF CACHE BOOL "" FORCE) # Prevents installation of gtest on the system
|
||||||
FetchContent_MakeAvailable(googletest)
|
FetchContent_MakeAvailable(googletest)
|
||||||
|
|
||||||
# Enable testing support
|
# Enable testing support
|
||||||
|
@ -209,10 +210,11 @@ if(${SVA_BUILD_TEST})
|
||||||
|
|
||||||
# --- Create Test Executable
|
# --- Create Test Executable
|
||||||
set(TEST_TARGET_NAME "${GUI_TARGET_NAME}_test")
|
set(TEST_TARGET_NAME "${GUI_TARGET_NAME}_test")
|
||||||
|
|
||||||
|
file(GLOB TEST_SOURCES "tests/*.cpp")
|
||||||
add_executable(
|
add_executable(
|
||||||
${TEST_TARGET_NAME}
|
${TEST_TARGET_NAME}
|
||||||
tests/test_main.cpp # Test entry point
|
${TEST_SOURCES}
|
||||||
tests/test_sorting_functions.cpp
|
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(
|
target_link_libraries(
|
||||||
|
@ -258,6 +260,5 @@ endif()
|
||||||
put_targets_into_folder(
|
put_targets_into_folder(
|
||||||
FOLDER "sva"
|
FOLDER "sva"
|
||||||
TARGETS
|
TARGETS
|
||||||
${GUI_TARGET_NAME}
|
|
||||||
${TEST_TARGET_NAME}
|
${TEST_TARGET_NAME}
|
||||||
)
|
)
|
||||||
|
|
0
src/Profiler/Profiler.cpp
Normal file
0
src/Profiler/Profiler.cpp
Normal file
0
src/Profiler/Profiler.hpp
Normal file
0
src/Profiler/Profiler.hpp
Normal file
|
@ -1 +0,0 @@
|
||||||
#include "Renderer.h"
|
|
|
@ -1,7 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
class Renderer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
};
|
|
|
@ -1,47 +0,0 @@
|
||||||
#include "SortingTester.h"
|
|
||||||
|
|
||||||
|
|
||||||
SortingTester::SortingTester() : list(10)
|
|
||||||
{
|
|
||||||
std::cout << "Loading lua" << std::endl;
|
|
||||||
|
|
||||||
lua.lua.open_libraries(sol::lib::coroutine, sol::lib::table);
|
|
||||||
lua.lua.new_usertype<SortingTester>("List",
|
|
||||||
"size", &SortingTester::list_size,
|
|
||||||
"swap", &SortingTester::list_swap,
|
|
||||||
"greater", &SortingTester::list_greater,
|
|
||||||
"less", &SortingTester::list_less,
|
|
||||||
"equal", &SortingTester::list_equal
|
|
||||||
);
|
|
||||||
|
|
||||||
lua.lua["list"] = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SortingTester::load(const std::string& script)
|
|
||||||
{
|
|
||||||
if(lua.load(script) != safesol::LuaResultType::SUCCESS)
|
|
||||||
{
|
|
||||||
std::cerr << "Error loading file" << std::endl;
|
|
||||||
std::terminate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#include "internal_script.hpp"
|
|
||||||
|
|
||||||
void SortingTester::load_internal()
|
|
||||||
{
|
|
||||||
if (lua.script(std_intern_sorting_script) != safesol::LuaResultType::SUCCESS)
|
|
||||||
{
|
|
||||||
std::cerr << "Error loading internal script" << std::endl;
|
|
||||||
std::terminate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SortingTester::populate(const populate_function& f)
|
|
||||||
{
|
|
||||||
for (size_t i = 0; i < list.size(); i++)
|
|
||||||
{
|
|
||||||
f(i, list[i]);
|
|
||||||
}
|
|
||||||
std::cout << list << std::endl;
|
|
||||||
}
|
|
|
@ -1,143 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#define SVA_LIST_CALLBACKS
|
|
||||||
#include <std.hpp>
|
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
#include "lua/safesol.h"
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
|
|
||||||
class SortingTester
|
|
||||||
{
|
|
||||||
List<u64> list;
|
|
||||||
|
|
||||||
safesol lua;
|
|
||||||
|
|
||||||
Timer timer = Timer(Timer::State::paused);
|
|
||||||
|
|
||||||
using populate_function = std::function<void(size_t, u64&)>;
|
|
||||||
|
|
||||||
template<typename ...Args>
|
|
||||||
sol::protected_function_result run_function(const sol::protected_function& function, Args&&... args)
|
|
||||||
{
|
|
||||||
timer.set_state(Timer::State::running);
|
|
||||||
sol::protected_function_result result = function(std::forward<Args>(args)...);
|
|
||||||
timer.set_state(Timer::State::paused);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool list_greater(size_t index1, size_t index2)
|
|
||||||
{
|
|
||||||
if (active_result)
|
|
||||||
++active_result->count_greater;
|
|
||||||
return list[index1] > list[index2];
|
|
||||||
}
|
|
||||||
|
|
||||||
bool list_less(size_t index1, size_t index2)
|
|
||||||
{
|
|
||||||
if (active_result)
|
|
||||||
++active_result->count_less;
|
|
||||||
return list[index1] < list[index2];
|
|
||||||
}
|
|
||||||
|
|
||||||
bool list_equal(size_t index1, size_t index2)
|
|
||||||
{
|
|
||||||
if (active_result)
|
|
||||||
++active_result->count_equal;
|
|
||||||
return list[index1] == list[index2];
|
|
||||||
}
|
|
||||||
|
|
||||||
void list_swap(size_t index1, size_t index2)
|
|
||||||
{
|
|
||||||
if (active_result)
|
|
||||||
++active_result->count_swaps;
|
|
||||||
// list[index1] ^= list[index2];
|
|
||||||
// list[index2] ^= list[index1];
|
|
||||||
// list[index1] ^= list[index2];
|
|
||||||
auto tmp = list[index1];
|
|
||||||
list[index1] = list[index2];
|
|
||||||
list[index2] = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t list_size() const
|
|
||||||
{
|
|
||||||
return list.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_sorted()
|
|
||||||
{
|
|
||||||
if (list.size() <= 1)
|
|
||||||
return true;
|
|
||||||
for (size_t i = 1; i < list.size(); i++)
|
|
||||||
{
|
|
||||||
if (list[i - 1] > list[i])
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public:
|
|
||||||
struct run_result
|
|
||||||
{
|
|
||||||
timer_result timer;
|
|
||||||
// counts
|
|
||||||
u64 count_swaps = 0;
|
|
||||||
u64 count_greater = 0;
|
|
||||||
u64 count_less = 0;
|
|
||||||
u64 count_equal = 0;
|
|
||||||
// counts for comparisons
|
|
||||||
u64 count_comparisons = 0;
|
|
||||||
};
|
|
||||||
private:
|
|
||||||
run_result* active_result = nullptr;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
SortingTester();
|
|
||||||
|
|
||||||
void load(const std::string& script);
|
|
||||||
void load_internal();
|
|
||||||
|
|
||||||
template<typename ...Args>
|
|
||||||
run_result run(const std::string& function_name, Args&&... args)
|
|
||||||
{
|
|
||||||
run_result result;
|
|
||||||
timer.result.emplace(&result.timer);
|
|
||||||
timer.reset(Timer::State::paused);
|
|
||||||
active_result = &result;
|
|
||||||
|
|
||||||
// Create a bound member function using std::bind
|
|
||||||
auto bound_run_function = std::bind(&SortingTester::run_function<Args...>,
|
|
||||||
this,
|
|
||||||
std::placeholders::_1,
|
|
||||||
std::forward<Args>(args)...);
|
|
||||||
|
|
||||||
safesol::LuaResultType runstate = lua.run_on_caller(function_name, bound_run_function);
|
|
||||||
|
|
||||||
if (runstate != safesol::LuaResultType::SUCCESS)
|
|
||||||
{
|
|
||||||
std::cerr << "Error running function \"" << function_name << "\": " << static_cast<u16>(runstate) << std::endl;
|
|
||||||
exit(static_cast<int>(runstate));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_sorted())
|
|
||||||
{
|
|
||||||
std::cerr << "[ERROR] The algorithm \"" << function_name << "\" did not sort the list" << std::endl;
|
|
||||||
std::cerr << list << std::endl;
|
|
||||||
std::flush(std::cerr);
|
|
||||||
std::terminate();
|
|
||||||
}
|
|
||||||
|
|
||||||
result.count_comparisons = result.count_equal + result.count_greater + result.count_less;
|
|
||||||
|
|
||||||
active_result = nullptr;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void populate(const populate_function& f);
|
|
||||||
};
|
|
|
@ -1,27 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
inline const char* std_intern_sorting_script = R"(
|
|
||||||
-- bubble sort
|
|
||||||
function bubble_sort()
|
|
||||||
local isSorted = false
|
|
||||||
while not isSorted do
|
|
||||||
local movedElements = 0
|
|
||||||
for x = 0, list:size() - 2 do
|
|
||||||
if list:greater(x, x+1) then
|
|
||||||
movedElements = movedElements + 1
|
|
||||||
list:swap(x, x+1)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if movedElements == 0 then
|
|
||||||
isSorted = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function do_nothing()
|
|
||||||
end
|
|
||||||
|
|
||||||
-- other functions
|
|
||||||
|
|
||||||
|
|
||||||
)";
|
|
228
src/main.cpp
228
src/main.cpp
|
@ -1,213 +1,25 @@
|
||||||
|
/**************************************************************
|
||||||
|
* Sortiva - Sorting Visualisation Tool
|
||||||
|
*
|
||||||
|
* This project falls under the MIT Licence.
|
||||||
|
* But I would find it very nice of you,
|
||||||
|
* if you could add my name and the project
|
||||||
|
* to some kind of credits.
|
||||||
|
* Thank you.
|
||||||
|
*
|
||||||
|
* Kind regards,
|
||||||
|
* Jann Hoffmann.
|
||||||
|
*
|
||||||
|
* Signature: "Darling, I'm Home!" - Jann Hoffmann
|
||||||
|
**************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <raylib.h>
|
|
||||||
#include <raylibs/raygui.h>
|
|
||||||
#include "sva.hpp"
|
|
||||||
|
|
||||||
|
int main(void)
|
||||||
#include "gui/drawing_helper.hpp"
|
|
||||||
|
|
||||||
#define RESOURCES_PATH "G:/School/Belegarbeit/sortiva/res/"
|
|
||||||
|
|
||||||
#define SETTINGS_PATH "./.config.cfg"
|
|
||||||
|
|
||||||
#define NO_GUI
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef NO_GUI
|
|
||||||
|
|
||||||
int screenWidth = 1280;
|
|
||||||
int screenHeight = 720;
|
|
||||||
|
|
||||||
enum GAME_STATES
|
|
||||||
{
|
{
|
||||||
SVA_STATE_TITLE,
|
std::cout << "Darling, I'm Home!\n";
|
||||||
SVA_STATE_GAMEPLAY,
|
|
||||||
};
|
|
||||||
|
|
||||||
GAME_STATES gameState = SVA_STATE_TITLE;
|
|
||||||
|
|
||||||
void UpdateDrawFrame(); // Update and Draw one frame
|
|
||||||
|
|
||||||
#include "gui/themes/dark.h"
|
|
||||||
#include "gui/Views/ViewManager.h"
|
|
||||||
#endif
|
|
||||||
#include <filesystem>
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef NO_GUI
|
|
||||||
#include "SortingTester.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#include <random>
|
|
||||||
|
|
||||||
std::random_device rd;
|
|
||||||
std::mt19937 gen(rd());
|
|
||||||
std::uniform_int_distribution<u64> dis(0, 10);
|
|
||||||
|
|
||||||
void populate(size_t index, u64& value)
|
|
||||||
{
|
|
||||||
value = dis(gen);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
|
||||||
{
|
|
||||||
sva_console_init();
|
|
||||||
|
|
||||||
#ifdef NO_GUI
|
|
||||||
|
|
||||||
std::cout << "Sortiva - Sorting Algorithm Visualizer" << std::endl;
|
|
||||||
std::cout << "No GUI mode" << std::endl;
|
|
||||||
|
|
||||||
SortingTester tester;
|
|
||||||
tester.populate( populate );
|
|
||||||
|
|
||||||
tester.load_internal();
|
|
||||||
|
|
||||||
SortingTester::run_result result = tester.run("bubble_sort");
|
|
||||||
|
|
||||||
std::cout << "Time: " << result.timer.seconds << " seconds" << std::endl;
|
|
||||||
std::cout << "Difftime: " << result.timer.difftime << std::endl;
|
|
||||||
|
|
||||||
|
|
||||||
std::cout << "Count swaps: " << result.count_swaps << std::endl;
|
|
||||||
std::cout << "Count comparisons: " << result.count_comparisons << std::endl;
|
|
||||||
std::cout << "\tCount greater: " << result.count_greater << std::endl;
|
|
||||||
std::cout << "\tCount less: " << result.count_less << std::endl;
|
|
||||||
std::cout << "\tCount equal: " << result.count_equal << std::endl;
|
|
||||||
|
|
||||||
std::cout << "\n\n";
|
|
||||||
|
|
||||||
|
|
||||||
std::cout << "\nPress any key to exit..." << std::endl;
|
|
||||||
// getchar();
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef NO_GUI
|
|
||||||
if(!DirectoryExists(RESOURCES_PATH))
|
|
||||||
{
|
|
||||||
std::cerr << "Resources folder not found!" << std::endl;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
InitWindow(screenWidth, screenHeight, "Sortiva - Sorting Algorithm Visualizer");
|
|
||||||
|
|
||||||
SetWindowIcon(LoadImage(RESOURCES_PATH "/images/sva-logo.png"));
|
|
||||||
|
|
||||||
SetWindowMinSize(screenWidth, screenHeight);
|
|
||||||
SetWindowState(FLAG_WINDOW_RESIZABLE);
|
|
||||||
SetExitKey(0);
|
|
||||||
|
|
||||||
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
|
|
||||||
//--------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
GuiLoadStyleDark();
|
|
||||||
GuiSetStyle(DEFAULT, TEXT_SIZE, 20);
|
|
||||||
|
|
||||||
// Main game loop
|
|
||||||
while (!WindowShouldClose())
|
|
||||||
{
|
|
||||||
if (IsWindowResized())
|
|
||||||
{
|
|
||||||
screenWidth = GetScreenWidth();
|
|
||||||
screenHeight = GetScreenHeight();
|
|
||||||
}
|
|
||||||
UpdateDrawFrame();
|
|
||||||
}
|
|
||||||
CloseWindow();
|
|
||||||
#endif
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef NO_GUI
|
|
||||||
//----------------------------------------------------------------------------------
|
|
||||||
// Render Functions
|
|
||||||
//----------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
void RenderGameplayState();
|
|
||||||
|
|
||||||
|
|
||||||
Rectangle ButtonRects[] = {
|
|
||||||
{50, 150, 150, 40},
|
|
||||||
{50, 200, 150, 40},
|
|
||||||
{50, 300, 150, 40},
|
|
||||||
};
|
|
||||||
|
|
||||||
void UpdateDrawFrame()
|
|
||||||
{
|
|
||||||
BeginDrawing();
|
|
||||||
|
|
||||||
ClearBackground(GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR)));
|
|
||||||
|
|
||||||
switch (gameState)
|
|
||||||
{
|
|
||||||
case SVA_STATE_TITLE:
|
|
||||||
sva::DrawText("Sortiva", {screenWidth/2, 20}, 100, sva::GetThemeColor(TEXT_COLOR_NORMAL), sva::TEXT_ALIGN_CENTER);
|
|
||||||
|
|
||||||
if(GuiButton(ButtonRects[0], "Öffnen"))
|
|
||||||
{
|
|
||||||
gameState = SVA_STATE_GAMEPLAY;
|
|
||||||
}
|
|
||||||
if(GuiButton(ButtonRects[1], "Speichern"))
|
|
||||||
{
|
|
||||||
gameState = SVA_STATE_GAMEPLAY;
|
|
||||||
}
|
|
||||||
if(GuiButton(ButtonRects[2], "Schließen"))
|
|
||||||
{
|
|
||||||
CloseWindow();
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
case SVA_STATE_GAMEPLAY:
|
|
||||||
if(IsKeyPressed(KEY_ESCAPE))
|
|
||||||
{
|
|
||||||
gameState = SVA_STATE_TITLE;
|
|
||||||
}
|
|
||||||
RenderGameplayState();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
EndDrawing();
|
|
||||||
}
|
|
||||||
#include <numbers>
|
|
||||||
|
|
||||||
#define PI_2 std::numbers::pi_v<float> / 2
|
|
||||||
|
|
||||||
#define sin3(x) pow((-cos(PI_2+ ##x)),3)
|
|
||||||
|
|
||||||
#include <cmath>
|
|
||||||
|
|
||||||
Vector2 heart_position = Vector2{static_cast<float>(screenWidth)/2, static_cast<float>(screenHeight)/2.5f};
|
|
||||||
Vector2 heart_function(float t, float scale)
|
|
||||||
{
|
|
||||||
return Vector2{
|
|
||||||
((16 * static_cast<float>(sin3(t))) * scale) + heart_position.x,
|
|
||||||
((13 * cos(t) - 5 * cos(2*t) - 2 * cos(3*t) - cos(4*t)) * -scale) + heart_position.y
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#define STEPS 1000
|
|
||||||
#define STEP_SIZE 0.01f
|
|
||||||
|
|
||||||
void RenderGameplayState()
|
|
||||||
{
|
|
||||||
for (int i = 0; i < STEPS; i++)
|
|
||||||
{
|
|
||||||
Vector2 pos = heart_function(static_cast<float>(i) * STEP_SIZE, 20);
|
|
||||||
Vector2 pos2 = heart_function(static_cast<float>(i) * STEP_SIZE + STEP_SIZE, 20);
|
|
||||||
DrawLine(
|
|
||||||
pos.x,
|
|
||||||
pos.y,
|
|
||||||
pos2.x,
|
|
||||||
pos2.y,
|
|
||||||
GetColor(GuiGetStyle(DEFAULT, LINE_COLOR))
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
17
src/sva.hpp
17
src/sva.hpp
|
@ -1,17 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#include <iostream>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
inline const char* ASCII_TITLE = R"(
|
|
||||||
__
|
|
||||||
(_ _ ___|_ o _
|
|
||||||
__)(_) | |_ | \_/(_|
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
)";
|
|
||||||
|
|
||||||
inline void sva_console_init()
|
|
||||||
{
|
|
||||||
std::cout << ASCII_TITLE;
|
|
||||||
}
|
|
0
tests/lua.cpp
Normal file
0
tests/lua.cpp
Normal file
|
@ -1,54 +0,0 @@
|
||||||
// tests/sorting_tests.cpp
|
|
||||||
#include <gtest/gtest.h>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void swap(T& a, T& b) noexcept
|
|
||||||
{
|
|
||||||
T temp = a;
|
|
||||||
a = b;
|
|
||||||
b = temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void bubble_sort(std::vector<T>& in)
|
|
||||||
{
|
|
||||||
for (size_t i = 0; i < in.size(); ++i)
|
|
||||||
{
|
|
||||||
for (size_t j = 0; j < in.size() - 1; ++j)
|
|
||||||
{
|
|
||||||
if (in[j] > in[j + 1])
|
|
||||||
{
|
|
||||||
swap(in[j], in[j + 1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TEST(BubbleSortTest, CorrectlySortsVector) {
|
|
||||||
std::vector input = {5, 2, 8, 1, 9};
|
|
||||||
std::vector expected = {1, 2, 5, 8, 9};
|
|
||||||
|
|
||||||
bubble_sort(input);
|
|
||||||
|
|
||||||
EXPECT_EQ(input, expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(BubbleSortTest, HandlesEmptyVector) {
|
|
||||||
std::vector<int> input = {};
|
|
||||||
std::vector<int> expected = {};
|
|
||||||
|
|
||||||
bubble_sort(input);
|
|
||||||
|
|
||||||
EXPECT_EQ(input, expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(BubbleSortTest, HandlesSingleElement) {
|
|
||||||
std::vector input = {1};
|
|
||||||
std::vector expected = {1};
|
|
||||||
|
|
||||||
bubble_sort(input);
|
|
||||||
|
|
||||||
EXPECT_EQ(input, expected);
|
|
||||||
}
|
|
Loading…
Reference in a new issue