diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 64f88ea..35c9580 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,14 +26,43 @@ jobs: run: cmake . -DCMAKE_BUILD_TYPE=${{ matrix.build }} - name: Build - run: make + run: | + make + ctest + + ubuntu-18_04-gcc-sanitized: + runs-on: ubuntu-18.04 + + strategy: + matrix: + sanitizer: [ADDRESS, THREAD, UNDEFINED] + + steps: + - name: Clone + uses: actions/checkout@v1 + with: + submodules: recursive + + - name: Dependencies + run: | + sudo apt-get update + sudo apt-get install build-essential + sudo apt-get install cmake; + + - name: Configure + run: cmake . -DCMAKE_BUILD_TYPE=Debug -DGGWAVE_BUILD_EXAMPLES=OFF -DGGWAVE_SANITIZE_${{ matrix.sanitizer }} + + - name: Build + run: | + make + ctest ubuntu-18_04-clang: runs-on: ubuntu-18.04 strategy: matrix: - build: [Debug, Release] + build: [Release] steps: - name: Clone @@ -52,14 +81,16 @@ jobs: run: cmake . -DCMAKE_BUILD_TYPE=${{ matrix.build }} -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER=clang - name: Build - run: make + run: | + make + ctest macOS-10_14-clang: runs-on: macOS-10.14 strategy: matrix: - build: [Debug, Release] + build: [Release] steps: - name: Clone @@ -76,14 +107,16 @@ jobs: run: cmake . -DCMAKE_BUILD_TYPE=${{ matrix.build }} - name: Build - run: make + run: | + make + ctest Emscripten: runs-on: ubuntu-18.04 strategy: matrix: - build: [Debug, Release] + build: [Release] steps: - name: Clone @@ -108,4 +141,4 @@ jobs: source ./emsdk_env.sh popd emcmake cmake . -DCMAKE_BUILD_TYPE=${{ matrix.build }} - make + make && ctest diff --git a/CMakeLists.txt b/CMakeLists.txt index d09cad7..11b30b8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,6 +34,7 @@ option(GGWAVE_SANITIZE_UNDEFINED "ggwave: enable undefined sanitizer" OFF) option(GGWAVE_SUPPORT_SDL2 "ggwave: support for libSDL2" ${GGWAVE_SUPPORT_SDL2_DEFAULT}) +option(GGWAVE_BUILD_TESTS "ggwave: build examples" ${GGWAVE_STANDALONE}) option(GGWAVE_BUILD_EXAMPLES "ggwave: build examples" ${GGWAVE_STANDALONE}) # sanitizers @@ -70,6 +71,11 @@ endif() add_subdirectory(src) -if (GGWAVE_STANDALONE AND GGWAVE_BUILD_EXAMPLES) +if (GGWAVE_BUILD_TESTS) + enable_testing() + add_subdirectory(tests) +endif() + +if (GGWAVE_BUILD_EXAMPLES) add_subdirectory(examples) endif() diff --git a/include/ggwave/ggwave.h b/include/ggwave/ggwave.h index 09e3b6b..d4d1d74 100644 --- a/include/ggwave/ggwave.h +++ b/include/ggwave/ggwave.h @@ -125,6 +125,7 @@ class GGWave { public: static constexpr auto kBaseSampleRate = 48000; static constexpr auto kDefaultSamplesPerFrame = 1024; + static constexpr auto kDefaultVolume = 10; static constexpr auto kMaxSamplesPerFrame = 1024; static constexpr auto kMaxDataBits = 256; static constexpr auto kMaxDataSize = 256; @@ -177,7 +178,8 @@ public: ~GGWave(); - bool init(int textLength, const char * stext, const TxProtocol & aProtocol, const int volume); + bool init(int dataSize, const char * dataBuffer, const int volume = kDefaultVolume); + bool init(int dataSize, const char * dataBuffer, const TxProtocol & aProtocol, const int volume = kDefaultVolume); bool encode(const CBEnqueueAudio & cbEnqueueAudio); void decode(const CBDequeueAudio & cbDequeueAudio); diff --git a/src/ggwave.cpp b/src/ggwave.cpp index c7576ef..cb1e6c6 100644 --- a/src/ggwave.cpp +++ b/src/ggwave.cpp @@ -290,17 +290,31 @@ GGWave::GGWave( GGWave::~GGWave() { } -bool GGWave::init(int textLength, const char * stext, const TxProtocol & aProtocol, const int volume) { - if (textLength > kMaxLength) { - fprintf(stderr, "Truncating data from %d to 140 bytes\n", textLength); - textLength = kMaxLength; +bool GGWave::init(int dataSize, const char * dataBuffer, const int volume) { + return init(dataSize, dataBuffer, getDefaultTxProtocol(), volume); +} + +bool GGWave::init(int dataSize, const char * dataBuffer, const TxProtocol & aProtocol, const int volume) { + if (dataSize < 0) { + fprintf(stderr, "Negative data size: %d\n", dataSize); + return false; + } + + if (dataSize > kMaxLength) { + fprintf(stderr, "Truncating data from %d to 140 bytes\n", dataSize); + dataSize = kMaxLength; + } + + if (volume < 0 || volume > 100) { + fprintf(stderr, "Invalid volume: %d\n", volume); + return false; } m_txProtocol = aProtocol; - m_txDataLength = textLength; + m_txDataLength = dataSize; m_sendVolume = ((double)(volume))/100.0f; - const uint8_t * text = reinterpret_cast(stext); + const uint8_t * text = reinterpret_cast(dataBuffer); m_hasNewTxData = false; std::fill(m_txData.begin(), m_txData.end(), 0); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..d10ba4b --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,15 @@ +if (EMSCRIPTEN) + return() +endif() + +set(TEST_TARGET test-ggwave) + +add_executable(${TEST_TARGET} + ${TEST_TARGET}.cpp + ) + +target_link_libraries(${TEST_TARGET} PRIVATE + ggwave + ) + +add_test(NAME ${TEST_TARGET} COMMAND $) diff --git a/tests/test-ggwave.cpp b/tests/test-ggwave.cpp new file mode 100644 index 0000000..24968cd --- /dev/null +++ b/tests/test-ggwave.cpp @@ -0,0 +1,39 @@ +#include "ggwave/ggwave.h" + +#include + +#define CHECK(cond) \ + if (!(cond)) { \ + fprintf(stderr, "[%s:%d] Check failed: %s\n", __FILE__, __LINE__, #cond); \ + exit(1); \ + } + +#define CHECK_T(cond) CHECK(cond) +#define CHECK_F(cond) CHECK(!(cond)) + +int main() { + GGWave instance(48000, 48000, 1024, 4, 2); + + std::string payload = "hello"; + + CHECK(instance.init(payload.size(), payload.c_str())); + + // data + CHECK_F(instance.init(-1, "asd")); + CHECK_T(instance.init(0, nullptr)); + CHECK_T(instance.init(0, "asd")); + CHECK_T(instance.init(1, "asd")); + CHECK_T(instance.init(2, "asd")); + CHECK_T(instance.init(3, "asd")); + + // volume + CHECK_F(instance.init(payload.size(), payload.c_str(), -1)); + CHECK_T(instance.init(payload.size(), payload.c_str(), 0)); + CHECK_T(instance.init(payload.size(), payload.c_str(), 50)); + CHECK_T(instance.init(payload.size(), payload.c_str(), 100)); + CHECK_F(instance.init(payload.size(), payload.c_str(), 101)); + + // todo .. + + return 0; +}