Debug and fixing of unit tests (#1023)

* Add tooling to enable debugging of hung tests.

* Force rebuild.

* Revert "Force rebuild."

This reverts commit 6f13e7382b.

* Add five minute timeout for unit tests.

* Fix max time calculation and increase to 10mins.

* Increase timeout to 20 minutes.

* We were actually calculating the counter wrong.

* Use atomics for UT related globals.

* Force build for more testing.

* Revert README change.

* Add retry logic for hdiutil due to known bugs.

* Encode EOO as a single unit.

* Revert GitHub debugging.

* Add PR #1023 to changelog.
ms-hamlib-465
Mooneer Salem 2025-09-14 00:52:50 -07:00 committed by GitHub
parent 955985f66e
commit 6dbb3b7f8f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 110 additions and 28 deletions

View File

@ -849,6 +849,7 @@ LDPC | Low Density Parity Check Codes - a family of powerful FEC codes
* macOS: Fix spurious error on startup when changing locales. (PR #1010)
* Prevent lockup/crash when testing Hamlib in PTT Config window. (PR #1016)
* Linux: fix rendering bug for mic/speaker slider when transitioning from TX to RX. (PR #1021)
* Various unit test fixes to reduce failure rate in CI environment. (PR #1023)
2. Enhancements:
* Add Mic/Speaker volume control to main window. (PR #980)
* Move less used Spectrum plot configuration to free up space on main window. (PR #996)

View File

@ -0,0 +1,36 @@
#! /bin/bash
# Simple Utility Script for allowing debug of hardened macOS apps.
# This is useful mostly for plug-in developer that would like keep developing without turning SIP off.
# Credit for idea goes to (McMartin): https://forum.juce.com/t/apple-gatekeeper-notarised-distributables/29952/57?u=ttg
# Update 2022-03-10: Based on Fabian's feedback, add capability to inject DYLD for sanitizers.
#
# Please note:
# - Modern Logic (on M1s) uses `AUHostingService` which resides within the system thus not patchable and REQUIRES to turn-off SIP.
# - Some hosts uses separate plug-in scanning or sandboxing.
# if that's the case, it's required to patch those (if needed) and attach debugger to them instead.
#
# If you see `operation not permitted`, make sure the calling process has Full Disk Access.
# For example Terminal.app is showing and has Full Disk Access under System Preferences -> Privacy & Security
#
app_path=$1
if [ -z "$app_path" ];
then
echo "You need to specify app to re-codesign!"
exit 0
fi
# This uses local codesign. so it'll be valid ONLY on the machine you've re-signed with.
entitlements_plist=/tmp/debug_entitlements.plist
echo "Grabbing entitlements from app..."
codesign -d --entitlements - "$app_path" --xml >> $entitlements_plist || { exit 1; }
echo "Patch entitlements (if missing)..."
/usr/libexec/PlistBuddy -c "Add :com.apple.security.cs.disable-library-validation bool true" $entitlements_plist
/usr/libexec/PlistBuddy -c "Add :com.apple.security.cs.allow-unsigned-executable-memory bool true" $entitlements_plist
/usr/libexec/PlistBuddy -c "Add :com.apple.security.get-task-allow bool true" $entitlements_plist
# allow custom dyld for sanitizers...
/usr/libexec/PlistBuddy -c "Add :com.apple.security.cs.allow-dyld-environment-variables bool true" $entitlements_plist
echo "Re-applying entitlements (if missing)..."
codesign --force --options runtime --sign - --entitlements $entitlements_plist "$app_path" || { echo "codesign failed!"; }
echo "Removing temporary plist..."
rm $entitlements_plist

View File

@ -0,0 +1,42 @@
#!/bin/sh
SOURCE_FOLDER=$1
# Note: retry logic from https://github.com/darktable-org/darktable/pull/16394/files
# When building on github runner, 'hdiutil create' occasionally fails (resource busy)
# so we make several retries
try_count=0
hdiutil_success=0
while [ $hdiutil_success -ne 1 -a $try_count -lt 8 ]; do
# Create temporary rw image
if hdiutil create -srcfolder $SOURCE_FOLDER/ -volname FreeDV -format UDZO -fs HFS+ ./FreeDV.dmg
then
hdiutil_success=1
break
fi
try_count=$(( $try_count + 1 ))
echo "'hdiutil create' failed (attempt ${try_count}). Retrying..."
sleep 1
done
if [ $hdiutil_success -ne 1 -a -n "${GITHUB_RUN_ID}" ]; then
# Still no success after 8 attempts.
# If we are on github runner, kill the Xprotect service and make one
# final attempt.
# see https://github.com/actions/runner-images/issues/7522
echo "Killing XProtect..."
sudo pkill -9 XProtect >/dev/null || true;
sleep 3
if hdiutil create -srcfolder $SOURCE_FOLDER/ -volname FreeDV -format UDZO -fs HFS+ ./FreeDV.dmg
then
hdiutil_success=1
fi
fi
if [ $hdiutil_success -ne 1 ]; then
echo "FATAL: 'hdiutil create' FAILED!"
exit 1
fi

View File

@ -168,14 +168,14 @@ if(APPLE)
COMMAND xcrun stapler staple ./FreeDV.app
COMMAND mkdir dist_tmp
COMMAND cp -a FreeDV.app dist_tmp
COMMAND hdiutil create -srcfolder dist_tmp/ -volname FreeDV -format UDZO -fs HFS+ ./FreeDV.dmg
COMMAND ${CMAKE_SOURCE_DIR}/cmake/macos_build_dmg.sh dist_tmp/
COMMAND rm -rf dist_tmp
DEPENDS FreeDV)
else(MACOS_CODESIGN_KEYCHAIN_PROFILE)
add_custom_target(release
COMMAND mkdir dist_tmp
COMMAND cp -a FreeDV.app dist_tmp
COMMAND hdiutil create -srcfolder dist_tmp/ -volname FreeDV -format UDZO -fs HFS+ ./FreeDV.dmg
COMMAND ${CMAKE_SOURCE_DIR}/cmake/macos_build_dmg.sh dist_tmp/
COMMAND rm -rf dist_tmp
DEPENDS FreeDV)
endif (MACOS_CODESIGN_KEYCHAIN_PROFILE)

View File

@ -150,7 +150,7 @@ int g_AEstatus2[4];
// playing and recording from sound files
extern SNDFILE *g_sfPlayFile;
extern bool g_playFileToMicIn;
extern std::atomic<bool> g_playFileToMicIn;
extern bool g_loopPlayFileToMicIn;
extern int g_playFileToMicInEventId;
@ -160,7 +160,7 @@ extern unsigned int g_recFromRadioSamples;
extern int g_recFileFromRadioEventId;
extern SNDFILE *g_sfPlayFileFromRadio;
extern bool g_playFileFromRadio;
extern std::atomic<bool> g_playFileFromRadio;
extern int g_sfFs;
extern int g_sfTxFs;
extern bool g_loopPlayFileFromRadio;
@ -335,6 +335,7 @@ void MainApp::UnitTest_()
std::this_thread::sleep_for(20ms);
}
constexpr int MAX_TIME_AS_COUNTER = 12000; // 20 minutes
if (testName == "tx")
{
log_info("Transmitting %d times", utTxAttempts);
@ -361,9 +362,10 @@ void MainApp::UnitTest_()
g_sfPlayFile = sf_open((const char*)utTxFile.ToUTF8(), SFM_READ, &sfInfo);
g_sfTxFs = sfInfo.samplerate;
g_loopPlayFileToMicIn = false;
g_playFileToMicIn = true;
g_playFileToMicIn.store(true, std::memory_order_release);
while (g_playFileToMicIn)
int counter = 0;
while (g_playFileToMicIn.load(std::memory_order_acquire) && (counter++) < MAX_TIME_AS_COUNTER)
{
std::this_thread::sleep_for(100ms);
}
@ -403,10 +405,11 @@ void MainApp::UnitTest_()
g_sfPlayFileFromRadio = sf_open((const char*)utRxFile.ToUTF8(), SFM_READ, &sfInfo);
g_sfFs = sfInfo.samplerate;
g_loopPlayFileFromRadio = false;
g_playFileFromRadio = true;
g_playFileFromRadio.store(true, std::memory_order_release);
auto sync = 0;
while (g_playFileFromRadio)
int counter = 0;
while (g_playFileFromRadio.load(std::memory_order_acquire) && (counter++) < MAX_TIME_AS_COUNTER)
{
std::this_thread::sleep_for(100ms);
auto newSync = freedvInterface.getSync();
@ -1151,14 +1154,14 @@ MainFrame::MainFrame(wxWindow *parent) : TopFrame(parent, wxID_ANY, _("FreeDV ")
#endif //_USE_ONIDLE
g_sfPlayFile = NULL;
g_playFileToMicIn = false;
g_playFileToMicIn.store(false, std::memory_order_release);
g_loopPlayFileToMicIn = false;
g_sfRecFile = NULL;
g_recFileFromRadio = false;
g_sfPlayFileFromRadio = NULL;
g_playFileFromRadio = false;
g_playFileFromRadio.store(false, std::memory_order_release);
g_loopPlayFileFromRadio = false;
g_sfRecFileFromModulator = NULL;
@ -1799,7 +1802,7 @@ void MainFrame::OnTimer(wxTimerEvent &evt)
pendingSnr,
freqLongLong);
if (!g_playFileFromRadio)
if (!g_playFileFromRadio.load(std::memory_order_acquire))
{
for (auto& obj : wxGetApp().m_reporters)
{
@ -1828,7 +1831,7 @@ void MainFrame::OnTimer(wxTimerEvent &evt)
if (freq > 0 && wxGetApp().m_reportCounter == 0)
{
wxGetApp().m_reportCounter = 0;
if (!g_playFileFromRadio)
if (!g_playFileFromRadio.load(std::memory_order_acquire))
{
wxGetApp().m_sharedReporterObject->addReceiveRecord(
"",

View File

@ -134,7 +134,7 @@ short* RADETransmitStep::execute(short* inputSamples, int numInputSamples, int*
if (numInputSamples == 0)
{
// Special case logic for EOO
*numOutputSamples = std::min(outputSampleFifo_.numUsed(), (FRAME_DURATION_MS * getOutputSampleRate()) / MS_TO_SEC);
*numOutputSamples = outputSampleFifo_.numUsed();
if (*numOutputSamples > 0)
{
outputSampleFifo_.read(outputSamples_.get(), *numOutputSamples);

View File

@ -72,7 +72,7 @@ extern std::atomic<bool> g_half_duplex;
extern std::atomic<int> g_tx;
extern int g_dump_fifo_state;
extern bool endingTx;
extern bool g_playFileToMicIn;
extern std::atomic<bool> g_playFileToMicIn;
extern int g_sfTxFs;
extern bool g_loopPlayFileToMicIn;
extern float g_TxFreqOffsetHz;
@ -88,7 +88,7 @@ extern bool g_queueResync;
extern int g_resyncs;
extern bool g_recFileFromRadio;
extern unsigned int g_recFromRadioSamples;
extern bool g_playFileFromRadio;
extern std::atomic<bool> g_playFileFromRadio;
extern int g_sfFs;
extern bool g_loopPlayFileFromRadio;
extern int g_SquelchActive;
@ -166,7 +166,7 @@ void TxRxThread::initializePipeline_()
auto playMicIn = new PlaybackStep(
inputSampleRate_,
[]() { return g_sfTxFs; },
[]() { return g_playFileToMicIn ? g_sfPlayFile : nullptr; },
[]() { return g_playFileToMicIn.load(std::memory_order_acquire) ? g_sfPlayFile : nullptr; },
[]() {
if (g_loopPlayFileToMicIn)
sf_seek(g_sfPlayFile, 0, SEEK_SET);
@ -179,7 +179,7 @@ void TxRxThread::initializePipeline_()
eitherOrPlayMicIn->appendPipelineStep(playMicIn);
auto eitherOrPlayStep = new EitherOrStep(
[]() { return g_playFileToMicIn && (g_sfPlayFile != NULL); },
[]() { return g_playFileToMicIn.load(std::memory_order_acquire) && (g_sfPlayFile != NULL); },
eitherOrPlayMicIn,
eitherOrBypassPlay);
pipeline_->appendPipelineStep(eitherOrPlayStep);
@ -325,7 +325,7 @@ void TxRxThread::initializePipeline_()
auto eitherOrPlayRadioStep = new EitherOrStep(
[]() {
auto result = g_playFileFromRadio && (g_sfPlayFileFromRadio != NULL);
auto result = g_playFileFromRadio.load(std::memory_order_acquire) && (g_sfPlayFileFromRadio != NULL);
return result;
},
eitherOrPlayRadio,

View File

@ -9,7 +9,7 @@
extern wxMutex g_mutexProtectingCallbackData;
SNDFILE *g_sfPlayFile;
bool g_playFileToMicIn;
std::atomic<bool> g_playFileToMicIn;
bool g_loopPlayFileToMicIn;
int g_playFileToMicInEventId;
@ -22,7 +22,7 @@ SNDFILE *g_sfRecMicFile;
bool g_recFileFromMic;
SNDFILE *g_sfPlayFileFromRadio;
bool g_playFileFromRadio;
std::atomic<bool> g_playFileFromRadio;
int g_sfFs;
int g_sfTxFs;
bool g_loopPlayFileFromRadio;
@ -57,9 +57,9 @@ static wxWindow* createMyExtraPlayFilePanel(wxWindow *parent)
void MainFrame::StopPlayFileToMicIn(void)
{
g_mutexProtectingCallbackData.Lock();
if (g_playFileToMicIn)
if (g_playFileToMicIn.load(std::memory_order_acquire))
{
g_playFileToMicIn = false;
g_playFileToMicIn.store(false, std::memory_order_release);
sf_close(g_sfPlayFile);
g_sfPlayFile = nullptr;
SetStatusText(wxT(""));
@ -71,7 +71,7 @@ void MainFrame::StopPlayFileToMicIn(void)
void MainFrame::StopPlaybackFileFromRadio()
{
g_mutexProtectingCallbackData.Lock();
g_playFileFromRadio = false;
g_playFileFromRadio.store(false, std::memory_order_release);
sf_close(g_sfPlayFileFromRadio);
g_sfPlayFileFromRadio = nullptr;
SetStatusText(wxT(""));
@ -88,8 +88,8 @@ void MainFrame::OnPlayFileFromRadio(wxCommandEvent& event)
{
wxUnusedVar(event);
log_debug("OnPlayFileFromRadio:: %d", (int)g_playFileFromRadio);
if (g_playFileFromRadio)
log_debug("OnPlayFileFromRadio:: %d", (int)g_playFileFromRadio.load(std::memory_order_acquire));
if (g_playFileFromRadio.load(std::memory_order_acquire))
{
log_debug("OnPlayFileFromRadio:: Stop");
StopPlaybackFileFromRadio();
@ -161,7 +161,7 @@ void MainFrame::OnPlayFileFromRadio(wxCommandEvent& event)
SetStatusText(statusText, 0);
log_debug("OnPlayFileFromRadio:: Playing File Fs = %d", (int)sfInfo.samplerate);
m_menuItemPlayFileFromRadio->SetItemLabel(wxString(_("Stop Play File - From Radio...")));
g_playFileFromRadio = true;
g_playFileFromRadio.store(true, std::memory_order_release);
}
}

View File

@ -201,7 +201,7 @@ void MainFrame::OnSetMonitorVKAudioVol( wxCommandEvent& event )
}
extern SNDFILE *g_sfPlayFile;
extern bool g_playFileToMicIn;
extern std::atomic<bool> g_playFileToMicIn;
extern bool g_loopPlayFileToMicIn;
extern FreeDVInterface freedvInterface;
extern int g_sfTxFs;
@ -239,7 +239,7 @@ int MainFrame::VoiceKeyerStartTx(void)
SetStatusText(wxT("Voice Keyer: Playing file ") + wxString::FromUTF8(vkFileName_.c_str()) + wxT(" to mic input") , 0);
g_loopPlayFileToMicIn = false;
g_playFileToMicIn = true;
g_playFileToMicIn.store(true, std::memory_order_release);
m_btnTogPTT->SetValue(true); togglePTT();
next_state = VK_TX;