Skip to content

Setting up GitHub Actions #90

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 20 commits into from
Jan 12, 2024
Merged
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
41 changes: 36 additions & 5 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -1,15 +1,46 @@
name: Build and Test
on: [workflow_dispatch]
on: [push, workflow_dispatch]
jobs:
build:
runs-on: [macos-13]
unit-tests:
runs-on: macos-13
env:
XC_VERSION: ${{ '15.1' }}
CCACHE_COMPRESS: "true"
CCACHE_COMPRESSLEVEL: "10"
CCACHE_MAXSIZE: "400M"
strategy:
matrix:
configuration: ["Debug", "Release"]
steps:
- name: Select latest Xcode
run: "sudo xcode-select -s /Applications/Xcode_$XC_VERSION.app"
- name: Install ccache
run: |
brew install ccache
which ccache
- name: Cache ccache
uses: actions/cache@v2
with:
path: ~/Library/Caches/ccache
key: ccache-${{ github.job }}-${{ matrix.configuration }}-${{ github.run_id }}
restore-keys: ccache-${{ github.job }}-${{ matrix.configuration }}
- uses: actions/checkout@v2
- name: Build and run unit tests
run: "cd Scripts && ./run_all_unit_tests.sh ${{ matrix.configuration }}"
- name: Show ccache stats
run: "ccache --show-stats --verbose"
- name: Remove old ccache caches
run: |
cd Scripts
./run_all_unit_tests.sh
gh extension install actions/gh-actions-cache
echo "Fetching list of cache key"
cacheKeys=$(gh actions-cache list --key ccache-${{ github.job }}-${{ matrix.configuration }}- | cut -f 1 )
## Setting this to not fail the workflow while deleting cache keys.
set +e
echo "Deleting caches..."
for cacheKey in $cacheKeys
do
gh actions-cache delete $cacheKey --confirm
done
echo "Done"
env:
GH_TOKEN: ${{ github.token }}
52 changes: 28 additions & 24 deletions Scripts/run_all_unit_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,12 @@ BUILD_DIR=$(mktemp -d ${SCRIPTS_DIR}/build.XXXXXXXXX)

ROOT_DIR=$(cd "$SCRIPTS_DIR/.." && pwd)

XCODEPROJ="../Source/NimbleCommander/NimbleCommander.xcodeproj"

LOG_FILE=${BUILD_DIR}/xcodebuild.log

if type -p /usr/local/bin/ccache >/dev/null 2>&1; then
echo Using ccache
export CCACHE_BASEDIR="${ROOT_DIR}"
export CCACHE_SLOPPINESS=time_macros,include_file_mtime,include_file_ctime,file_stat_matches
export CC="${SCRIPTS_DIR}/ccache-clang"
Expand All @@ -34,7 +37,7 @@ build_target()
CONFIGURATION=$2
echo building ${TARGET} - ${CONFIGURATION}
XC="xcodebuild \
-project ../Source/NimbleCommander/NimbleCommander.xcodeproj \
-project ${XCODEPROJ} \
-scheme ${TARGET} \
-configuration ${CONFIGURATION} \
SYMROOT=${BUILD_DIR} \
Expand All @@ -48,35 +51,36 @@ build_target()
}

# list of targets to build
tests=(\
BaseUT \
ConfigUT \
UtilityUT \
VFSIconUT \
VFSUT \
OperationsUT \
ViewerUT \
TermUT \
PanelUT \
NimbleCommanderUT \
)
tests=$(xcodebuild -project ${XCODEPROJ} -list | awk -v word="Schemes:" 'BEGIN {found=0} found {if ($0 ~ /UT$/) print} $0 ~ word {found=1}' | sed 's/^[[:space:]]*//')
echo Building these unit tests: ${tests}

# list of configurations to build the targets with
configurations=(\
Debug \
Release \
)

# run N * M binaries
for configuration in ${configurations[@]}; do
for test in ${tests[@]}; do
if [ -n "$1" ]; then
configurations="$1"
else
configurations="Debug Release"
fi
echo Building these configurations: ${configurations}

# a list of binaries of UTs to execute
binary_paths=()

# build N * M binaries
for configuration in ${configurations}; do
for test in ${tests}; do
# build the binary
build_target $test $configuration
build_target ${test} ${configuration}

# execute the binary
$BINARY_PATH
# store the path to execute later
binary_paths+=("$BINARY_PATH")
done
done

# run the binaries
for path in "${binary_paths[@]}"; do
echo "$path"
$path
done

# cleanup
rm -rf ${BUILD_DIR}
1 change: 1 addition & 0 deletions Source/Base/config/tests.xcconfig
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
OTHER_LDFLAGS = $(inherited) -lBase
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) GTEST_DONT_DEFINE_FAIL=1 GTEST_DONT_DEFINE_SUCCEED=1
EXECUTABLE_PREFIX=
ONLY_ACTIVE_ARCH=YES
1 change: 1 addition & 0 deletions Source/Config/config/tests.xcconfig
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
OTHER_LDFLAGS = $(inherited) -lConfig -lBase -framework Foundation -framework Cocoa
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) GTEST_DONT_DEFINE_FAIL=1 GTEST_DONT_DEFINE_SUCCEED=1
EXECUTABLE_PREFIX=
ONLY_ACTIVE_ARCH=YES
Original file line number Diff line number Diff line change
Expand Up @@ -1770,6 +1770,7 @@
CFA1716220196CB400F507D5 /* AppDelegate+MainWindowCreation.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "AppDelegate+MainWindowCreation.mm"; path = "NimbleCommander/Bootstrap/AppDelegate+MainWindowCreation.mm"; sourceTree = SOURCE_ROOT; };
CFA1716420196CF600F507D5 /* AppDelegate.Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.Private.h; path = NimbleCommander/Bootstrap/AppDelegate.Private.h; sourceTree = SOURCE_ROOT; };
CFA1716820199E8700F507D5 /* FavoritesImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FavoritesImpl.h; path = NimbleCommander/States/FilePanels/FavoritesImpl.h; sourceTree = SOURCE_ROOT; };
CFA22D2B2B51BD7E00E71479 /* tests.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = tests.xcconfig; path = NimbleCommander/Config/tests.xcconfig; sourceTree = "<group>"; };
CFA2D6011E39D43C009DB708 /* PreferencesWindowThemesTabImportSheet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PreferencesWindowThemesTabImportSheet.h; path = NimbleCommander/Preferences/PreferencesWindowThemesTabImportSheet.h; sourceTree = SOURCE_ROOT; };
CFA2D6021E39D43C009DB708 /* PreferencesWindowThemesTabImportSheet.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = PreferencesWindowThemesTabImportSheet.mm; path = NimbleCommander/Preferences/PreferencesWindowThemesTabImportSheet.mm; sourceTree = SOURCE_ROOT; };
CFA3D7C519625F71006A30A1 /* GTMHotKeyTextField.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GTMHotKeyTextField.h; path = ../../3rd_Party/GTMHotKeyTextField/GTMHotKeyTextField.h; sourceTree = SOURCE_ROOT; };
Expand Down Expand Up @@ -2221,6 +2222,7 @@
CF6572991FA6CD7300ABF85F /* debug.xcconfig */,
CF5FD92D1FA2C02E00752E59 /* default.xcconfig */,
CF65729A1FA6CD8100ABF85F /* release.xcconfig */,
CFA22D2B2B51BD7E00E71479 /* tests.xcconfig */,
);
name = Config;
sourceTree = "<group>";
Expand Down Expand Up @@ -5142,13 +5144,15 @@
};
CF5DE0B7258414CA00604DEE /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = CFA22D2B2B51BD7E00E71479 /* tests.xcconfig */;
buildSettings = {
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
CF5DE0B8258414CA00604DEE /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = CFA22D2B2B51BD7E00E71479 /* tests.xcconfig */;
buildSettings = {
PRODUCT_NAME = "$(TARGET_NAME)";
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ONLY_ACTIVE_ARCH=YES
1 change: 1 addition & 0 deletions Source/Operations/config/tests.xcconfig
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
OTHER_LDFLAGS = $(inherited) -lgmock -lgtest -lOperations -lRoutedIO -lUtility
LD_RUNPATH_SEARCH_PATHS = @executable_path/
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) GTEST_DONT_DEFINE_FAIL=1 GTEST_DONT_DEFINE_SUCCEED=1
ONLY_ACTIVE_ARCH=YES
1 change: 1 addition & 0 deletions Source/Panel/config/tests.xcconfig
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
LD_RUNPATH_SEARCH_PATHS = @executable_path/
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) GTEST_DONT_DEFINE_FAIL=1 GTEST_DONT_DEFINE_SUCCEED=1
OTHER_LDFLAGS = $(inherited) -framework Foundation -framework Cocoa -lUtility -lBase -lPanel -lVFS -lRoutedIO
ONLY_ACTIVE_ARCH=YES
6 changes: 3 additions & 3 deletions Source/Panel/tests/ExternalTools_IT.mm
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2023 Michael Kazakov. Subject to GNU General Public License version 3.
// Copyright (C) 2023-2024 Michael Kazakov. Subject to GNU General Public License version 3.
// TODO: add a PanelIT target

#include "ExternalTools.h"
Expand Down Expand Up @@ -142,7 +142,7 @@ static bool WaitForChildProcess(int _pid, std::chrono::nanoseconds _deadline, st
}
}

TEST_CASE(PREFIX "execute a ui app")
TEST_CASE(PREFIX "execute a ui app", "[!mayfail]")
{
TempTestDir dir;
const auto basedir = dir.directory;
Expand Down Expand Up @@ -235,7 +235,7 @@ static bool WaitForChildProcess(int _pid, std::chrono::nanoseconds _deadline, st
auto run = [&] {
ExternalToolExecution ex{ctx, et};
auto pid = ex.StartDetachedUI();
REQUIRE(pid);
REQUIRE(pid); // <-- Flaky!
REQUIRE(WaitForChildProcess(*pid, 5s, 1ms));
};

Expand Down
1 change: 1 addition & 0 deletions Source/Term/config/tests.xcconfig
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) GTEST_DONT_DEFINE_FAIL=1 GTEST_DONT_DEFINE_SUCCEED=1
OTHER_LDFLAGS = $(inherited) -framework Foundation -framework Cocoa -lTerm -lUtility -lBase
ONLY_ACTIVE_ARCH=YES
53 changes: 38 additions & 15 deletions Source/Term/tests/ChildrenTracker_UT.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2023 Michael Kazakov. Subject to GNU General Public License version 3.
// Copyright (C) 2023-2024 Michael Kazakov. Subject to GNU General Public License version 3.

#include "Tests.h"
#include "AtomicHolder.h"
Expand All @@ -10,44 +10,51 @@ using namespace nc::term;
using namespace std::chrono_literals;
#define PREFIX "nc::term::ChildrenTracker "

TEST_CASE(PREFIX "Generic cases")
TEST_CASE(PREFIX "Generic cases", "[!mayfail]")
{
const int mypid = getpid();
const int p1 = getpid();
QueuedAtomicHolder<int> ncalled{0};
auto cb = [&ncalled, next = 1] mutable { ncalled.store(next++); };

ChildrenTracker tracker{mypid, cb};
ChildrenTracker tracker{p1, cb};

SECTION("Nothing") {}
SECTION("Single fork")
{
int p2;
int p2 = 0;
if( (p2 = fork()) == 0 ) {
std::this_thread::sleep_for(1ms);
exit(0);
}
CHECK(p2 > 0);
CHECK(ncalled.wait_to_become_with_runloop(5s, 1ms, 1)); // p1 fork -> p2
CHECK(ncalled.wait_to_become_with_runloop(5s, 1ms, 2)); // p2 exit
waitpid(p2, nullptr, 0);
CHECK(waitpid(p2, nullptr, 0) == p2);
}
SECTION("Two sequent forks")
{
if( fork() == 0 ) {
int p2 = 0, p3 = 0;
if( (p2 = fork()) == 0 ) {
std::this_thread::sleep_for(1ms);
exit(0);
}
CHECK(p2 > 0);
CHECK(ncalled.wait_to_become_with_runloop(5s, 1ms, 1)); // p1 fork -> p2
CHECK(ncalled.wait_to_become_with_runloop(5s, 1ms, 2)); // p2 exit
if( fork() == 0 ) {
if( (p3 = fork()) == 0 ) {
std::this_thread::sleep_for(1ms);
exit(0);
}
CHECK(p3 > 0);
CHECK(ncalled.wait_to_become_with_runloop(5s, 1ms, 3)); // p1 fork -> p3
CHECK(ncalled.wait_to_become_with_runloop(5s, 1ms, 4)); // p3 exit
CHECK(waitpid(p2, nullptr, 0) == p2);
CHECK(waitpid(p3, nullptr, 0) == p3);
}
SECTION("Two recursive forks")
{
if( fork() == 0 ) {
int p2 = 0;
if( (p2 = fork()) == 0 ) {
std::this_thread::sleep_for(1ms);
if( int p3; (p3 = fork()) == 0 ) {
std::this_thread::sleep_for(1ms);
Expand All @@ -57,14 +64,17 @@ TEST_CASE(PREFIX "Generic cases")
waitpid(p3, nullptr, 0);
exit(0);
}
CHECK(p2 > 0);
CHECK(ncalled.wait_to_become_with_runloop(5s, 1ms, 1)); // p1 fork -> p2
CHECK(ncalled.wait_to_become_with_runloop(5s, 1ms, 2)); // p2 fork -> p3
CHECK(ncalled.wait_to_become_with_runloop(5s, 1ms, 3)); // p3 exit
CHECK(ncalled.wait_to_become_with_runloop(5s, 1ms, 4)); // p2 exit
CHECK(waitpid(p2, nullptr, 0) == p2);
}
SECTION("Three recursive forks")
{
if( fork() == 0 ) {
int p2 = 0;
if( (p2 = fork()) == 0 ) {
std::this_thread::sleep_for(1ms);
if( int p3; (p3 = fork()) == 0 ) {
std::this_thread::sleep_for(1ms);
Expand All @@ -80,16 +90,19 @@ TEST_CASE(PREFIX "Generic cases")
waitpid(p3, nullptr, 0);
exit(0);
}
CHECK(p2 > 0);
CHECK(ncalled.wait_to_become_with_runloop(5s, 1ms, 1)); // p1 fork -> p2
CHECK(ncalled.wait_to_become_with_runloop(5s, 1ms, 2)); // p2 fork -> p3
CHECK(ncalled.wait_to_become_with_runloop(5s, 1ms, 3)); // p3 fork -> p4
CHECK(ncalled.wait_to_become_with_runloop(5s, 1ms, 4)); // p4 exit
CHECK(ncalled.wait_to_become_with_runloop(5s, 1ms, 5)); // p3 exit
CHECK(ncalled.wait_to_become_with_runloop(5s, 1ms, 6)); // p2 exit
CHECK(waitpid(p2, nullptr, 0) == p2);
}
SECTION("2 x two recursive forks")
{
if( fork() == 0 ) {
int p2 = 0, p4 = 0;
if( (p2 = fork()) == 0 ) {
std::this_thread::sleep_for(1ms);
if( int p3; (p3 = fork()) == 0 ) {
std::this_thread::sleep_for(1ms);
Expand All @@ -99,7 +112,7 @@ TEST_CASE(PREFIX "Generic cases")
waitpid(p3, nullptr, 0);
exit(0);
}
if( fork() == 0 ) {
if( (p4 = fork()) == 0 ) {
std::this_thread::sleep_for(1ms);
if( int p3; (p3 = fork()) == 0 ) {
std::this_thread::sleep_for(1ms);
Expand All @@ -109,29 +122,37 @@ TEST_CASE(PREFIX "Generic cases")
waitpid(p3, nullptr, 0);
exit(0);
}
CHECK(p2 > 0);
CHECK(p4 > 0);
CHECK(ncalled.wait_to_become_with_runloop(5s, 1ms, 1)); // p1 fork -> p2
CHECK(ncalled.wait_to_become_with_runloop(5s, 1ms, 2)); // p1 fork -> p4
CHECK(ncalled.wait_to_become_with_runloop(5s, 1ms, 3)); // p2 fork -> p3
CHECK(ncalled.wait_to_become_with_runloop(5s, 1ms, 4)); // p4 fork -> p5
CHECK(ncalled.wait_to_become_with_runloop(5s, 1ms, 5)); // p3 exit
CHECK(ncalled.wait_to_become_with_runloop(5s, 1ms, 6)); // p5 exit
CHECK(ncalled.wait_to_become_with_runloop(5s, 1ms, 7)); // p2 exit
CHECK(ncalled.wait_to_become_with_runloop(5s, 1ms, 8)); // p4 exit
CHECK(ncalled.wait_to_become_with_runloop(5s, 1ms, 8)); // p4 exit <-- this one fails on GHA
CHECK(waitpid(p2, nullptr, 0) == p2);
CHECK(waitpid(p4, nullptr, 0) == p4);
}
SECTION("Fork and exec")
{
if( fork() == 0 ) {
int p2 = 0;
if( (p2 = fork()) == 0 ) {
std::this_thread::sleep_for(1ms);
close(1);
execl("/usr/bin/uptime", "uptime", nullptr);
}
CHECK(p2 > 0);
CHECK(ncalled.wait_to_become_with_runloop(5s, 1ms, 1)); // p1 fork -> p2
CHECK(ncalled.wait_to_become_with_runloop(5s, 1ms, 2)); // p2 exec
CHECK(ncalled.wait_to_become_with_runloop(5s, 1ms, 3)); // p2 exit
CHECK(waitpid(p2, nullptr, 0) == p2);
}
SECTION("Two recursive forks and exec")
{
if( fork() == 0 ) {
int p2 = 0;
if( (p2 = fork()) == 0 ) {
std::this_thread::sleep_for(1ms);
if( int p3; (p3 = fork()) == 0 ) {
std::this_thread::sleep_for(1ms);
Expand All @@ -142,11 +163,13 @@ TEST_CASE(PREFIX "Generic cases")
waitpid(p3, nullptr, 0);
exit(0);
}
CHECK(p2 > 0);
CHECK(ncalled.wait_to_become_with_runloop(5s, 1ms, 1)); // p1 fork -> p2
CHECK(ncalled.wait_to_become_with_runloop(5s, 1ms, 2)); // p2 fork -> p3
CHECK(ncalled.wait_to_become_with_runloop(5s, 1ms, 3)); // p3 exec
CHECK(ncalled.wait_to_become_with_runloop(5s, 1ms, 4)); // p3 exit
CHECK(ncalled.wait_to_become_with_runloop(5s, 1ms, 5)); // p2 exit
CHECK(waitpid(p2, nullptr, 0) == p2);
}
}

Expand Down
1 change: 1 addition & 0 deletions Source/Utility/config/tests.xcconfig
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
OTHER_LDFLAGS = $(inherited) -lUtility -lBase
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) GTEST_DONT_DEFINE_FAIL=1 GTEST_DONT_DEFINE_SUCCEED=1
EXECUTABLE_PREFIX=
ONLY_ACTIVE_ARCH=YES
Loading