Skip to content

Commit

Permalink
Allow mocking preferences and AudioIO in unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
crsib committed Apr 3, 2023
1 parent 52b88fd commit 614db70
Show file tree
Hide file tree
Showing 7 changed files with 285 additions and 5 deletions.
23 changes: 20 additions & 3 deletions cmake-proxies/cmake-modules/AudacityTesting.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@ if( ${_OPT}has_tests )
enable_testing()

#[[
add_unit_test(NAME name SOURCES file1 ... LIBRARIES lib1 ...)
add_unit_test(NAME name [MOCK_PREFS] [MOCK_AUDIO] SOURCES file1 ... LIBRARIES lib1 ...)

If MOCK_PREFS is specified, a test can instantiate a mocked Prefs object.
If MOCK_AUDIO is specified, a test will initialize PortAudio.

Audio mocking is a subject to change when Audio I/O is refactored.

Creates an executable called ${name}-test from the source files ${file1}, ... and linked
to libraries ${lib1}, ... Catch2 is linked implicitly.
Expand All @@ -19,7 +24,7 @@ if( ${_OPT}has_tests )
function( add_unit_test )
cmake_parse_arguments(
ADD_UNIT_TEST # Prefix
"" # Options
"MOCK_PREFS;MOCK_AUDIO" # Options
"NAME" # One value keywords
"SOURCES;LIBRARIES"
${ARGN}
Expand All @@ -34,7 +39,19 @@ if( ${_OPT}has_tests )
# Create test executable

add_executable( ${test_executable_name} ${ADD_UNIT_TEST_SOURCES} "${CMAKE_SOURCE_DIR}/tests/Catch2Main.cpp")
target_link_libraries( ${test_executable_name} ${ADD_UNIT_TEST_LIBRARIES} Catch2::Catch2 )
target_link_libraries( ${test_executable_name} PRIVATE ${ADD_UNIT_TEST_LIBRARIES} Catch2::Catch2 )

if (ADD_UNIT_TEST_MOCK_PREFS)
target_compile_definitions( ${test_executable_name} PRIVATE MOCK_PREFS )
target_sources( ${test_executable_name} PRIVATE "${CMAKE_SOURCE_DIR}/tests/MockedPrefs.cpp" "${CMAKE_SOURCE_DIR}/tests/MockedPrefs.h" )
target_include_directories( ${test_executable_name} PRIVATE "${CMAKE_SOURCE_DIR}/tests" )
target_link_libraries( ${test_executable_name} PRIVATE lib-preferences-interface )
endif()

if (ADD_UNIT_TEST_MOCK_AUDIO)
target_compile_definitions( ${test_executable_name} PRIVATE MOCK_AUDIO )
target_sources( ${test_executable_name} PRIVATE "${CMAKE_SOURCE_DIR}/tests/MockedAudio.cpp" "${CMAKE_SOURCE_DIR}/tests/MockedAudio.h" )
endif()

set( OPTIONS )
audacity_append_common_compiler_options( OPTIONS NO )
Expand Down
6 changes: 4 additions & 2 deletions libraries/lib-audio-devices/AudioIOBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ Paul Licameli split from AudioIO.cpp

#include "AudioIOBase.h"

#include <cassert>

#include <wx/log.h>
#include <wx/sstream.h>
#include <wx/txtstrm.h>
Expand Down Expand Up @@ -628,7 +630,7 @@ int AudioIOBase::getPlayDevIndex(const wxString &devNameArg)
// And I can't imagine how far we'll get specifying an "invalid" index later
// on...are we certain "0" even exists?
if (deviceNum < 0) {
wxASSERT(false);
assert(false);
deviceNum = 0;
}

Expand Down Expand Up @@ -685,7 +687,7 @@ int AudioIOBase::getRecordDevIndex(const wxString &devNameArg)
// JKC: This ASSERT will happen if you run with no config file
// This happens once. Config file will exist on the next run.
// TODO: Look into this a bit more. Could be relevant to blank Device Toolbar.
wxASSERT(false);
assert(false);
deviceNum = 0;
}

Expand Down
5 changes: 5 additions & 0 deletions libraries/lib-numeric-formats/tests/NumericConverterTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "formatters/ParsedNumericConverterFormatter.h"
#include "formatters/BeatsNumericConverterFormatter.h"


TEST_CASE("ParsedNumericConverterFormatter", "")
{
auto hhmmssFormatter = CreateParsedNumericConverterFormatter(
Expand Down Expand Up @@ -56,6 +57,7 @@ TEST_CASE("ParsedNumericConverterFormatter", "")
Approx(60 * 60 + 30 * 60 + 15));
}


TEST_CASE("BeatsNumericConverterFormatter", "")
{
auto basicFormatter = CreateBeatsNumericConverterFormatter(120, 3, 4);
Expand Down Expand Up @@ -129,4 +131,7 @@ TEST_CASE("BeatsNumericConverterFormatter", "")
REQUIRE(fracFormatter->SingleStep(0.0, 1, true) == Approx(15.0));
REQUIRE(fracFormatter->SingleStep(0.0, 4, true) == Approx(0.5));
REQUIRE(fracFormatter->SingleStep(0.0, 3, true) == Approx(5));

}


23 changes: 23 additions & 0 deletions tests/MockedAudio.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*!********************************************************************
Audacity: A Digital Audio Editor
MockedAudio.cpp
Dmitry Vedenko
**********************************************************************/
#include "MockedAudio.h"

#include <portaudio.h>

MockedAudio::MockedAudio()
{
Pa_Initialize();
}

MockedAudio::~MockedAudio()
{
Pa_Terminate();
}
22 changes: 22 additions & 0 deletions tests/MockedAudio.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*!********************************************************************
Audacity: A Digital Audio Editor
MockedAudio.h
Dmitry Vedenko
**********************************************************************/
#pragma once

struct MockedAudio final
{
MockedAudio();
~MockedAudio();

MockedAudio(const MockedAudio&) = delete;
MockedAudio& operator=(const MockedAudio&) = delete;
MockedAudio(MockedAudio&&) = delete;
MockedAudio& operator=(MockedAudio&&) = delete;
};
182 changes: 182 additions & 0 deletions tests/MockedPrefs.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*!********************************************************************
Audacity: A Digital Audio Editor
MockedPrefs.cpp
Dmitry Vedenko
**********************************************************************/
#include "MockedPrefs.h"

#include "Prefs.h"

#include <unordered_map>

class MockedFileConfig final : public FileConfig
{
public:
void SetPath(const wxString& path) override
{
// To make GetPath to work consistently
mPath = path;
}

const wxString& GetPath() const override
{
return mPath;
}

bool GetFirstGroup(wxString& str, long& lIndex) const override
{
return false;
}

bool GetNextGroup(wxString& str, long& lIndex) const override
{
return false;
}

bool GetFirstEntry(wxString& str, long& lIndex) const override
{
return false;
}

bool GetNextEntry(wxString& str, long& lIndex) const override
{
return false;
}

size_t GetNumberOfEntries(bool bRecursive = false) const override
{
return 0;
}

size_t GetNumberOfGroups(bool bRecursive = false) const override
{
return 0;
}

bool HasGroup(const wxString& strName) const override
{
return true;
}

bool HasEntry(const wxString& strName) const override
{
return true;
}

bool Flush(bool bCurrentOnly = false) override
{
return false;
}

bool RenameEntry(const wxString& oldName, const wxString& newName)
override
{
return false;
}

bool RenameGroup(const wxString& oldName, const wxString& newName)
override
{
return false;
}

bool DeleteEntry(const wxString& key, bool bDeleteGroupIfEmpty = true)
override
{
return false;
}

bool DeleteGroup(const wxString& key) override
{
return false;
}

bool DeleteAll() override
{
return false;
}

bool
DoReadString(const wxString& key, wxString* pStr) const override
{
auto it = mStringValues.find(key);

if (it == mStringValues.end())
return false;

*pStr = it->second;
return true;
}

bool DoReadLong(const wxString& key, long* pl) const override
{
auto it = mLongValues.find(key);

if (it == mLongValues.end())
return false;

*pl = it->second;
return true;
}

bool DoReadBinary(const wxString& key, wxMemoryBuffer* buf) const
override
{
auto it = mBinaryValues.find(key);

if (it == mBinaryValues.end())
return false;

*buf = it->second;
return true;
}

bool DoWriteString(const wxString& key, const wxString& szValue)
override
{
mStringValues[key] = szValue;
return true;
}

bool DoWriteLong(const wxString& key, long lValue) override
{
mLongValues[key] = lValue;
return true;
}

bool DoWriteBinary(const wxString& key, const wxMemoryBuffer& buf)
override
{
mBinaryValues[key] = buf;
return true;
}

void Warn() override
{
}

private:
wxString mPath;

std::unordered_map<wxString, wxString> mStringValues;
std::unordered_map<wxString, long> mLongValues;
std::unordered_map<wxString, wxMemoryBuffer> mBinaryValues;

};

MockedPrefs::MockedPrefs()
: mConfig { std::make_unique<MockedFileConfig>() }
{
// MockedPrefs destructor will delete this object
gPrefs = mConfig.get();
}

MockedPrefs::~MockedPrefs ()
{
gPrefs = nullptr;
}
29 changes: 29 additions & 0 deletions tests/MockedPrefs.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*!********************************************************************
Audacity: A Digital Audio Editor
MockedPrefs.h
Dmitry Vedenko
**********************************************************************/
#pragma once

#include <memory>

class MockedFileConfig;

struct MockedPrefs final
{
MockedPrefs();
~MockedPrefs();

MockedPrefs(const MockedPrefs&) = delete;
MockedPrefs& operator=(const MockedPrefs&) = delete;
MockedPrefs(MockedPrefs&&) = delete;
MockedPrefs& operator=(MockedPrefs&&) = delete;

private:
std::unique_ptr<MockedFileConfig> mConfig;
};

0 comments on commit 614db70

Please sign in to comment.