Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 20 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:

env:
PRESET_NAME: ${{ matrix.os == 'ubuntu-latest' && format('linux-{0}', matrix.build_type) || matrix.os == 'windows-latest' && format('windows-{0}', matrix.build_type) || format('macos-{0}', matrix.build_type) }}
VCPKG_TRIPLET: ${{ matrix.os == 'ubuntu-latest' && 'x64-linux' || matrix.os == 'windows-latest' && 'x64-windows' || 'arm64-osx' }}
VCPKG_TRIPLET: ${{ matrix.os == 'ubuntu-latest' && 'x64-linux' || matrix.os == 'windows-latest' && 'x64-windows' || 'x64-osx' }}

steps:
- name: Checkout repository
Expand Down Expand Up @@ -78,6 +78,19 @@ jobs:
- name: Build project
run: cmake --build --preset=${{ env.PRESET_NAME }}

- name: Verify build output
if: success()
shell: bash
run: |
echo "Checking build output directory structure..."
if [ -d "bin/build/${{ env.PRESET_NAME }}" ]; then
echo "✅ Build directory exists"
find bin/build/${{ env.PRESET_NAME }} -type f -name "*.dll" -o -name "*.so" -o -name "*.dylib" -o -name "*.exe" -o -name "*.a" | head -20 || true
else
echo "⚠️ Build directory not found at expected location"
ls -la bin/ || echo "bin/ directory not found"
fi

- name: Test library linkage (Unix)
if: runner.os != 'Windows'
run: |
Expand All @@ -93,12 +106,15 @@ jobs:

- name: Upload build artifacts
uses: actions/upload-artifact@v4
if: success() || failure()
with:
name: binancecpp-${{ matrix.os }}-${{ matrix.build_type }}
path: |
build/${{ env.PRESET_NAME }}/src/
build/${{ env.PRESET_NAME }}/example/
bin/build/${{ env.PRESET_NAME }}/src/
bin/build/${{ env.PRESET_NAME }}/example/
bin/build/${{ env.PRESET_NAME }}/tests/
retention-days: 5
if-no-files-found: warn

code-quality:
name: Code Quality
Expand All @@ -118,7 +134,7 @@ jobs:
- name: Find C++ source files
id: find-files
run: |
find src example -name "*.cpp" -o -name "*.h" -o -name "*.hpp" -o -name "*.cc" -o -name "*.cxx" > cpp_files.txt
find src example \( -name "*.cpp" -o -name "*.h" -o -name "*.hpp" -o -name "*.cc" -o -name "*.cxx" \) > cpp_files.txt
echo "file-count=$(wc -l < cpp_files.txt)" >> $GITHUB_OUTPUT

- name: Check code formatting
Expand Down
16 changes: 14 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.20)
cmake_minimum_required(VERSION 3.21)

project(binancecpp
VERSION 1.0.0
Expand All @@ -9,6 +9,11 @@ project(binancecpp
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# Windows-specific: Use MultiThreadedDLL runtime library for MSVC
if(MSVC)
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
endif()

# Configure RPATH
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
if(APPLE)
Expand All @@ -20,11 +25,13 @@ endif()

# Options
option(BINANCECPP_BUILD_EXAMPLES "Build examples" ON)
option(BINANCECPP_BUILD_TESTS "Build tests" ON)
option(BINANCECPP_DEPLOY_MODE "Enable deployment mode, disables examples" OFF)

# Deployment mode overrides examples setting
# Deployment mode overrides examples and tests settings
if(BINANCECPP_DEPLOY_MODE)
set(BINANCECPP_BUILD_EXAMPLES OFF CACHE BOOL "Deploy mode disables examples" FORCE)
set(BINANCECPP_BUILD_TESTS OFF CACHE BOOL "Deploy mode disables tests" FORCE)
endif()


Expand All @@ -44,6 +51,11 @@ if(BINANCECPP_BUILD_EXAMPLES)
add_subdirectory(example)
endif()

# Conditionally build tests
if(BINANCECPP_BUILD_TESTS)
add_subdirectory(tests)
endif()


# Installation
include(GNUInstallDirs)
Expand Down
18 changes: 18 additions & 0 deletions example/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,24 @@ foreach(EXAMPLE_NAME ${EXAMPLES})
$<$<CXX_COMPILER_ID:Clang>:-fconcepts-ts>
)

# On Windows, copy the DLL and its dependencies to the example executable directory
if(WIN32)
add_custom_command(TARGET ${EXAMPLE_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
$<TARGET_FILE:binancecpp>
$<TARGET_FILE_DIR:${EXAMPLE_NAME}>
COMMENT "Copying binancecpp.dll to example directory"
)
# Copy runtime DLLs for dependencies (JsonCpp, CURL, websockets)
add_custom_command(TARGET ${EXAMPLE_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
$<TARGET_RUNTIME_DLLS:${EXAMPLE_NAME}>
$<TARGET_FILE_DIR:${EXAMPLE_NAME}>
COMMAND_EXPAND_LISTS
COMMENT "Copying dependency DLLs to example directory"
)
endif()

# Install example binaries
install(TARGETS ${EXAMPLE_NAME}
RUNTIME DESTINATION bin/examples
Expand Down
20 changes: 20 additions & 0 deletions src/core/binance_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,26 @@ void BinanceAPI::Cleanup() noexcept
curl_global_cleanup();
}

void BinanceAPI::SetAPIKey(std::string_view api_key)
{
api_key_ = std::string(api_key);
}

void BinanceAPI::SetSecretKey(std::string_view secret_key)
{
secret_key_ = std::string(secret_key);
}

const std::string& BinanceAPI::GetAPIKey() noexcept
{
return api_key_;
}

const std::string& BinanceAPI::GetSecretKey() noexcept
{
return secret_key_;
}

size_t BinanceAPI::CurlCallback(void* content,
size_t size,
size_t nmemb,
Expand Down
Binary file removed test_api_structure
Binary file not shown.
89 changes: 89 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# CMakeLists.txt for tests directory
cmake_minimum_required(VERSION 3.21)

# Find required packages for tests
find_package(jsoncpp CONFIG REQUIRED)
find_package(CURL REQUIRED)
find_package(libwebsockets CONFIG REQUIRED)

# Add src/ as include directory for tests
include_directories(../src)

# Get all test source files recursively
file(GLOB_RECURSE TEST_SOURCES "test_*.cpp")

# Create executable for each test file
foreach(TEST_SOURCE ${TEST_SOURCES})
# Get the filename without extension
get_filename_component(TEST_NAME ${TEST_SOURCE} NAME_WE)

# Get relative path for unique naming in case of conflicts
file(RELATIVE_PATH REL_PATH ${CMAKE_CURRENT_SOURCE_DIR} ${TEST_SOURCE})
get_filename_component(REL_DIR ${REL_PATH} DIRECTORY)

# Create unique test name by including directory path
if(REL_DIR)
string(REPLACE "/" "_" DIR_PREFIX ${REL_DIR})
string(REPLACE "\\" "_" DIR_PREFIX ${DIR_PREFIX})
set(UNIQUE_TEST_NAME ${DIR_PREFIX}_${TEST_NAME})
else()
set(UNIQUE_TEST_NAME ${TEST_NAME})
endif()

# Create executable
add_executable(${UNIQUE_TEST_NAME} ${TEST_SOURCE})

# Link against the main library and dependencies
target_link_libraries(${UNIQUE_TEST_NAME}
binancecpp
JsonCpp::JsonCpp
CURL::libcurl
$<IF:$<TARGET_EXISTS:websockets>,websockets,websockets_shared>
)

# Set C++ standard
set_property(TARGET ${UNIQUE_TEST_NAME} PROPERTY CXX_STANDARD 20)
set_property(TARGET ${UNIQUE_TEST_NAME} PROPERTY CXX_STANDARD_REQUIRED ON)

# On Windows, copy the DLL and its dependencies to the test executable directory
if(WIN32)
add_custom_command(TARGET ${UNIQUE_TEST_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
$<TARGET_FILE:binancecpp>
$<TARGET_FILE_DIR:${UNIQUE_TEST_NAME}>
COMMENT "Copying binancecpp.dll to test directory"
)
# Copy runtime DLLs for dependencies (JsonCpp, CURL, websockets)
add_custom_command(TARGET ${UNIQUE_TEST_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
$<TARGET_RUNTIME_DLLS:${UNIQUE_TEST_NAME}>
$<TARGET_FILE_DIR:${UNIQUE_TEST_NAME}>
COMMAND_EXPAND_LISTS
COMMENT "Copying dependency DLLs to test directory"
)
endif()

# Add to test suite (optional)
# add_test(NAME ${UNIQUE_TEST_NAME} COMMAND ${UNIQUE_TEST_NAME})
endforeach()

# Create a convenience target to build all tests
add_custom_target(build_all_tests)
foreach(TEST_SOURCE ${TEST_SOURCES})
get_filename_component(TEST_NAME ${TEST_SOURCE} NAME_WE)
file(RELATIVE_PATH REL_PATH ${CMAKE_CURRENT_SOURCE_DIR} ${TEST_SOURCE})
get_filename_component(REL_DIR ${REL_PATH} DIRECTORY)

if(REL_DIR)
string(REPLACE "/" "_" DIR_PREFIX ${REL_DIR})
string(REPLACE "\\" "_" DIR_PREFIX ${DIR_PREFIX})
set(UNIQUE_TEST_NAME ${DIR_PREFIX}_${TEST_NAME})
else()
set(UNIQUE_TEST_NAME ${TEST_NAME})
endif()

add_dependencies(build_all_tests ${UNIQUE_TEST_NAME})
endforeach()

message(STATUS "Found ${CMAKE_CURRENT_SOURCE_DIR} test files")
message(STATUS "Test executables will be built for each test_*.cpp file")
143 changes: 143 additions & 0 deletions tests/core/test_binance_api.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
/*
Unit test for binance_api.cpp (core)
Tests the BinanceAPI core functionality
*/

#include <iostream>
#include <string>

#include "binance_api.h"

int main()
{
std::cout << "=== Testing BinanceAPI Core ===" << std::endl;

// Test 1: Basic initialization
std::cout << "\n1. Testing Init..." << std::endl;
try
{
binance_cpp::core::BinanceAPI::Init();
std::cout << "✅ Init() successful" << std::endl;
}
catch (const std::exception& e)
{
std::cout << "❌ Init() failed: " << e.what() << std::endl;
return 1;
}

// Test 2: Initialization with API keys
std::cout << "\n2. Testing Init with API keys..." << std::endl;
try
{
binance_cpp::core::BinanceAPI::Init("test_api_key", "test_secret_key");
std::cout << "✅ Init(api_key, secret_key) successful" << std::endl;
}
catch (const std::exception& e)
{
std::cout << "❌ Init(api_key, secret_key) failed: " << e.what() << std::endl;
return 1;
}

// Test 3: API key management
std::cout << "\n3. Testing API key management..." << std::endl;
try
{
binance_cpp::core::BinanceAPI::SetAPIKey("new_test_api_key");
const std::string& api_key = binance_cpp::core::BinanceAPI::GetAPIKey();

if (api_key == "new_test_api_key")
{
std::cout << "✅ SetAPIKey/GetAPIKey successful" << std::endl;
}
else
{
std::cout << "❌ SetAPIKey/GetAPIKey failed: " << api_key << std::endl;
return 1;
}
}
catch (const std::exception& e)
{
std::cout << "❌ API key management failed: " << e.what() << std::endl;
return 1;
}

// Test 4: Secret key management
std::cout << "\n4. Testing Secret key management..." << std::endl;
try
{
binance_cpp::core::BinanceAPI::SetSecretKey("new_test_secret_key");
const std::string& secret_key = binance_cpp::core::BinanceAPI::GetSecretKey();

if (secret_key == "new_test_secret_key")
{
std::cout << "✅ SetSecretKey/GetSecretKey successful" << std::endl;
}
else
{
std::cout << "❌ SetSecretKey/GetSecretKey failed: " << secret_key << std::endl;
return 1;
}
}
catch (const std::exception& e)
{
std::cout << "❌ Secret key management failed: " << e.what() << std::endl;
return 1;
}

// Test 5: CurlCallback function
std::cout << "\n5. Testing CurlCallback..." << std::endl;
try
{
std::string buffer;
const char* test_data = "test response data";
size_t result = binance_cpp::core::BinanceAPI::CurlCallback(
const_cast<char*>(test_data), 1, strlen(test_data), &buffer);

if (result == strlen(test_data) && buffer == test_data)
{
std::cout << "✅ CurlCallback successful" << std::endl;
}
else
{
std::cout << "❌ CurlCallback failed: " << buffer << std::endl;
return 1;
}
}
catch (const std::exception& e)
{
std::cout << "❌ CurlCallback failed: " << e.what() << std::endl;
return 1;
}

// Test 6: Cleanup
std::cout << "\n6. Testing Cleanup..." << std::endl;
try
{
binance_cpp::core::BinanceAPI::Cleanup();
std::cout << "✅ Cleanup() successful" << std::endl;
}
catch (const std::exception& e)
{
std::cout << "❌ Cleanup() failed: " << e.what() << std::endl;
return 1;
}

// Test 7: Re-initialization after cleanup
std::cout << "\n7. Testing re-initialization after cleanup..." << std::endl;
try
{
binance_cpp::core::BinanceAPI::Init("test_api", "test_secret");
std::cout << "✅ Re-initialization successful" << std::endl;

// Final cleanup
binance_cpp::core::BinanceAPI::Cleanup();
}
catch (const std::exception& e)
{
std::cout << "❌ Re-initialization failed: " << e.what() << std::endl;
return 1;
}

std::cout << "\n=== BinanceAPI Core Tests Complete ===" << std::endl;
return 0;
}
Loading
Loading