build(homebrew): reduce test duplication and add test coverage for macOS (#5331)

This commit is contained in:
Dave Lane
2026-06-25 10:18:40 -04:00
committed by GitHub
parent 0731729347
commit 4f7bde2430
4 changed files with 193 additions and 41 deletions

View File

@@ -5,8 +5,13 @@ class Sunshine < Formula
CUDA_VERSION = "13.1".freeze
CUDA_FORMULA = "cuda@#{CUDA_VERSION}".freeze
COVERAGE_LCOV = "coverage.lcov".freeze
COVERAGE_PROFDATA = "coverage.profdata".freeze
COVERAGE_XML = "coverage.xml".freeze
GCC_VERSION = "14".freeze
GCC_FORMULA = "gcc@#{GCC_VERSION}".freeze
LLVM_PROFILE_FILE_ENV = "LLVM_PROFILE_FILE".freeze
TEST_BINARY = "test_sunshine".freeze
IS_UPSTREAM_REPO = ENV.fetch("GITHUB_REPOSITORY", "") == "LizardByte/Sunshine"
desc "@PROJECT_DESCRIPTION@"
@@ -48,7 +53,6 @@ class Sunshine < Formula
depends_on "graphviz" => :build if build.with? "docs"
depends_on "node" => :build
depends_on "pkgconf" => :build
depends_on "gcovr" => :test
depends_on "boost"
depends_on "curl"
depends_on "icu4c@78"
@@ -56,12 +60,9 @@ class Sunshine < Formula
depends_on "openssl@3"
depends_on "opus"
on_macos do
depends_on "llvm" => [:build, :test]
end
on_linux do
depends_on GCC_FORMULA => [:build, :test]
depends_on "gcovr" => [:build, :test]
depends_on "lizardbyte/homebrew/#{CUDA_FORMULA}" => :build if build.with? "cuda"
depends_on "python3" => :build
depends_on "at-spi2-core"
@@ -183,6 +184,7 @@ class Sunshine < Formula
def add_test_args(args)
if IS_UPSTREAM_REPO
args << "-DBUILD_TESTS=ON"
args << "-DSUNSHINE_LLVM_COVERAGE=ON" if OS.mac?
ohai "Building tests: enabled"
else
args << "-DBUILD_TESTS=OFF"
@@ -248,6 +250,164 @@ class Sunshine < Formula
ohai "CUDA enabled with nvcc at: #{nvcc_path}"
end
def release_homebrew_testpath
testpath_value = ENV.fetch("HOMEBREW_TEST_ARTIFACTS_DIR", "")
return Pathname.new(testpath_value) unless testpath_value.empty?
return if ENV.fetch("HOMEBREW_TEST_BOT", "") != "1"
temp_path = ENV.fetch("HOMEBREW_TEMP", "")
return if temp_path.empty?
Pathname.new(temp_path)/name/"test"
end
def ensure_artifact_exists(path)
odie "#{path} was not created" unless path.exist?
end
def run_test_suite(artifact_dir)
mkdir_p artifact_dir/"tests"
test_results = artifact_dir/"tests/test_results.xml"
if OS.mac?
with_llvm_profile_file(artifact_dir) do
system bin/TEST_BINARY, "--gtest_color=yes", "--gtest_output=xml:#{test_results}"
end
else
system bin/TEST_BINARY, "--gtest_color=yes", "--gtest_output=xml:#{test_results}"
end
ensure_artifact_exists test_results
end
def with_llvm_profile_file(artifact_dir)
original_profile_file = ENV.fetch(LLVM_PROFILE_FILE_ENV, nil)
ENV[LLVM_PROFILE_FILE_ENV] = "#{artifact_dir}/sunshine-%p.profraw"
yield
ensure
if original_profile_file
ENV[LLVM_PROFILE_FILE_ENV] = original_profile_file
else
ENV.delete(LLVM_PROFILE_FILE_ENV)
end
end
def coverage_gcov_options
gcc_path = Formula[GCC_FORMULA]
["--gcov-executable", "#{gcc_path.opt_bin}/gcov-#{GCC_VERSION}"]
end
def llvm_profdata_executable
Utils.safe_popen_read("xcrun", "--find", "llvm-profdata").strip
end
def llvm_cov_executable
Utils.safe_popen_read("xcrun", "--find", "llvm-cov").strip
end
def coverage_report_path(artifact_dir)
artifact_dir/(OS.mac? ? COVERAGE_LCOV : COVERAGE_XML)
end
def coverage_common_options(coverage_report)
[
"--exclude-noncode-lines",
"--exclude-throw-branches",
"--exclude-unreachable-branches",
"--xml-pretty",
"-o=#{coverage_report}",
]
end
def generate_coverage_report(artifact_dir, coverage_buildpath)
return if coverage_buildpath.to_s.empty?
coverage_report = coverage_report_path(artifact_dir)
if OS.mac?
generate_llvm_coverage_report artifact_dir, coverage_buildpath, coverage_report
else
generate_gcov_coverage_report coverage_report, coverage_buildpath
end
ensure_artifact_exists coverage_report
end
def generate_llvm_coverage_report(artifact_dir, coverage_buildpath, coverage_report)
profile_files = Dir["#{artifact_dir}/*.profraw"]
odie "No LLVM profile data was created" if profile_files.empty?
profile_data = artifact_dir/COVERAGE_PROFDATA
system llvm_profdata_executable, "merge", "--sparse", "-o", profile_data.to_s, *profile_files
lcov = Utils.safe_popen_read(
llvm_cov_executable,
"export",
"-format=lcov",
"-instr-profile=#{profile_data}",
(bin/TEST_BINARY).to_s,
)
coverage_report.write lcov_for_source_files(lcov, coverage_buildpath)
ensure_lcov_report_has_lines coverage_report
end
def generate_gcov_coverage_report(coverage_report, coverage_buildpath)
cd "#{coverage_buildpath}/build" do
system "gcovr", ".",
"-r", "../src",
*coverage_gcov_options,
*coverage_common_options(coverage_report)
end
ensure_cobertura_report_has_lines coverage_report
end
def coverage_source_prefixes(coverage_buildpath)
paths = [
coverage_buildpath.to_s,
Pathname.new(coverage_buildpath.to_s).realpath.to_s,
]
paths.uniq.map { |path| "#{path}/src/" }
end
def relative_lcov_record(record, source_prefixes)
lines = record.lines
source_index = lines.index { |line| line.start_with?("SF:") }
return unless source_index
source_path = lines[source_index].delete_prefix("SF:").strip
source_prefix = source_prefixes.find { |prefix| source_path.start_with?(prefix) }
return unless source_prefix
lines[source_index] = "SF:src/#{source_path.delete_prefix(source_prefix)}\n"
"#{lines.join}end_of_record\n"
end
def lcov_for_source_files(lcov, coverage_buildpath)
source_prefixes = coverage_source_prefixes(coverage_buildpath)
records = lcov.split("end_of_record\n")
records.filter_map { |record| relative_lcov_record(record, source_prefixes) }.join
end
def ensure_cobertura_report_has_lines(path)
lines_valid = path.read[/lines-valid="(\d+)"/, 1].to_i
odie "#{path} does not contain any source lines" if lines_valid.zero?
end
def ensure_lcov_report_has_lines(path)
has_lines = path.read.lines.any? { |line| line.start_with?("DA:") }
odie "#{path} does not contain any source lines" unless has_lines
end
def collect_test_artifacts
artifact_dir = release_homebrew_testpath
return unless IS_UPSTREAM_REPO
return unless artifact_dir
run_test_suite artifact_dir
generate_coverage_report artifact_dir, buildpath
end
def build_cmake_args
args = base_cmake_args
add_test_args(args)
@@ -267,7 +427,7 @@ class Sunshine < Formula
end
def install_platform_specific_files
bin.install "build/tests/test_sunshine" if IS_UPSTREAM_REPO
bin.install "build/tests/#{TEST_BINARY}" if IS_UPSTREAM_REPO
# codesign the binary on intel macs
system "codesign", "-s", "-", "--force", "--deep", bin/"sunshine" if OS.mac? && Hardware::CPU.intel?
@@ -279,6 +439,7 @@ class Sunshine < Formula
setup_build_environment
build_and_install_project
install_platform_specific_files
collect_test_artifacts
end
service do
@@ -314,33 +475,14 @@ class Sunshine < Formula
# test that the binary runs at all
system bin/"sunshine", "--version"
if IS_UPSTREAM_REPO && ENV.fetch("HOMEBREW_BOTTLE_BUILD", "false") != "true"
# run the test suite
system bin/"test_sunshine", "--gtest_color=yes", "--gtest_output=xml:tests/test_results.xml"
assert_path_exists File.join(testpath, "tests", "test_results.xml")
# create gcovr report
buildpath = ENV.fetch("HOMEBREW_BUILDPATH", "")
unless buildpath.empty?
# Change to the source directory for gcovr to work properly
cd "#{buildpath}/build" do
# Use GCC version to match what was used during compilation
if OS.linux?
gcc_path = Formula[GCC_FORMULA]
gcov_executable = "#{gcc_path.opt_bin}/gcov-#{GCC_VERSION}"
system "gcovr", ".",
"-r", "../src",
"--gcov-executable", gcov_executable,
"--exclude-noncode-lines",
"--exclude-throw-branches",
"--exclude-unreachable-branches",
"--xml-pretty",
"-o=#{testpath}/coverage.xml"
assert_path_exists File.join(testpath, "coverage.xml")
end
end
if IS_UPSTREAM_REPO
artifact_dir = release_homebrew_testpath
if artifact_dir
assert_path_exists artifact_dir/"tests/test_results.xml"
assert_path_exists coverage_report_path(artifact_dir)
elsif ENV.fetch("HOMEBREW_BOTTLE_BUILD", "false") != "true"
run_test_suite testpath
generate_coverage_report testpath, ENV.fetch("HOMEBREW_BUILDPATH", "")
end
end
end