Skip to content

Commit

Permalink
Add E2E tests for swift driver integration
Browse files Browse the repository at this point in the history
  • Loading branch information
polac24 committed Jun 4, 2023
1 parent 8dffbd4 commit 811cc00
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 36 deletions.
13 changes: 10 additions & 3 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ DERIVED_DATA_DIR = File.join('.build').freeze
RELEASES_ROOT_DIR = File.join('releases').freeze

EXECUTABLE_NAME = 'XCRemoteCache'
EXECUTABLE_NAMES = ['xclibtool', 'xcpostbuild', 'xcprebuild', 'xcprepare', 'xcswiftc', 'xcld', 'xcldplusplus', 'xclipo']
EXECUTABLE_NAMES = ['xclibtool', 'xcpostbuild', 'xcprebuild', 'xcprepare', 'xcswiftc', 'swiftc', 'xcswift-frontend', 'swift-frontend', 'xcld', 'xcldplusplus', 'xclipo']
PROJECT_NAME = 'XCRemoteCache'

SWIFTLINT_ENABLED = true
Expand Down Expand Up @@ -59,6 +59,10 @@ task :build, [:configuration, :arch, :sdks, :is_archive] do |task, args|

# Path of the executable looks like: `.build/(debug|release)/XCRemoteCache`
build_path_base = File.join(DERIVED_DATA_DIR, args.configuration)
# swift-frontent integration requires that the SWIFT_EXEC is `swiftc` so create
# a symbolic link between swiftc->xcswiftc and swift-frontend->xcswift-frontend
system("cd #{build_path_base} && ln -s xcswiftc swiftc")
system("cd #{build_path_base} && ln -s xcswift-frontend swift-frontend")
sdk_build_paths = EXECUTABLE_NAMES.map {|e| File.join(build_path_base, e)}

build_paths.push(sdk_build_paths)
Expand Down Expand Up @@ -130,7 +134,9 @@ def create_release_zip(build_paths)
# Create and move files into the release directory
mkdir_p release_dir
build_paths.each {|p|
cp_r p, release_dir
# -r for recursive
# -P for copying symbolic link as is
system("cp -rP #{p} #{release_dir}")
}

output_artifact_basename = "#{PROJECT_NAME}.zip"
Expand All @@ -139,7 +145,8 @@ def create_release_zip(build_paths)
# -X: no extras (uid, gid, file times, ...)
# -x: exclude .DS_Store
# -r: recursive
system("zip -X -x '*.DS_Store' -r #{output_artifact_basename} .") or abort "zip failure"
# -y: to store symbolic links (used for swiftc -> xcswiftc)
system("zip -X -x '*.DS_Store' -r -y #{output_artifact_basename} .") or abort "zip failure"
# List contents of zip file
system("unzip -l #{output_artifact_basename}") or abort "unzip failure"
end
Expand Down
15 changes: 9 additions & 6 deletions cocoapods-plugin/lib/cocoapods-xcremotecache/command/hooks.rb
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,8 @@ def self.enable_xcremotecache(
exclude_build_configurations,
final_target,
fake_src_root,
exclude_sdks_configurations
exclude_sdks_configurations,
enable_swift_driver_integration
)
srcroot_relative_xc_location = parent_dir(xc_location, repo_distance)
# location of the entrite CocoaPods project, relative to SRCROOT
Expand All @@ -137,14 +138,15 @@ def self.enable_xcremotecache(
elsif mode == 'producer' || mode == 'producer-fast'
config.build_settings.delete('CC') if config.build_settings.key?('CC')
end
reset_build_setting(config.build_settings, 'SWIFT_EXEC', "$SRCROOT/#{srcroot_relative_xc_location}/xcswiftc", exclude_sdks_configurations)
swiftc_name = enable_swift_driver_integration ? 'swiftc' : 'xcswiftc'
reset_build_setting(config.build_settings, 'SWIFT_EXEC', "$SRCROOT/#{srcroot_relative_xc_location}/#{swiftc_name}", exclude_sdks_configurations)
reset_build_setting(config.build_settings, 'LIBTOOL', "$SRCROOT/#{srcroot_relative_xc_location}/xclibtool", exclude_sdks_configurations)
# Setting LIBTOOL to '' breaks SwiftDriver intengration so resetting it to the original value 'libtool' for all excluded configurations
add_build_setting_for_sdks(config.build_settings, 'LIBTOOL', 'libtool', exclude_sdks_configurations)
reset_build_setting(config.build_settings, 'LD', "$SRCROOT/#{srcroot_relative_xc_location}/xcld", exclude_sdks_configurations)
reset_build_setting(config.build_settings, 'LDPLUSPLUS', "$SRCROOT/#{srcroot_relative_xc_location}/xcldplusplus", exclude_sdks_configurations)
reset_build_setting(config.build_settings, 'LIPO', "$SRCROOT/#{srcroot_relative_xc_location}/xclipo", exclude_sdks_configurations)
reset_build_setting(config.build_settings, 'SWIFT_USE_INTEGRATED_DRIVER', 'NO', exclude_sdks_configurations)
reset_build_setting(config.build_settings, 'SWIFT_USE_INTEGRATED_DRIVER', 'NO', exclude_sdks_configurations) unless enable_swift_driver_integration

reset_build_setting(config.build_settings, 'XCREMOTE_CACHE_FAKE_SRCROOT', fake_src_root, exclude_sdks_configurations)
reset_build_setting(config.build_settings, 'XCRC_PLATFORM_PREFERRED_ARCH', "$(LINK_FILE_LIST_$(CURRENT_VARIANT)_$(PLATFORM_PREFERRED_ARCH):dir:standardizepath:file:default=arm64)", exclude_sdks_configurations)
Expand Down Expand Up @@ -498,6 +500,7 @@ def self.save_lldbinit_rewrite(user_proj_directory,fake_src_root)
check_platform = @@configuration['check_platform']
fake_src_root = @@configuration['fake_src_root']
exclude_sdks_configurations = @@configuration['exclude_sdks_configurations'] || []
enable_swift_driver_integration = @@configuration['enable_swift_driver_integration'] || false

xccc_location_absolute = "#{user_proj_directory}/#{xccc_location}"
xcrc_location_absolute = "#{user_proj_directory}/#{xcrc_location}"
Expand All @@ -521,7 +524,7 @@ def self.save_lldbinit_rewrite(user_proj_directory,fake_src_root)
next if target.name.start_with?("Pods-")
next if target.name.end_with?("Tests")
next if exclude_targets.include?(target.name)
enable_xcremotecache(target, 1, xcrc_location, xccc_location, mode, exclude_build_configurations, final_target,fake_src_root, exclude_sdks_configurations)
enable_xcremotecache(target, 1, xcrc_location, xccc_location, mode, exclude_build_configurations, final_target,fake_src_root, exclude_sdks_configurations, enable_swift_driver_integration)
end

# Create .rcinfo into `Pods` directory as that .xcodeproj reads configuration from .xcodeproj location
Expand All @@ -534,7 +537,7 @@ def self.save_lldbinit_rewrite(user_proj_directory,fake_src_root)
next if target.source_build_phase.files_references.empty?
next if target.name.end_with?("Tests")
next if exclude_targets.include?(target.name)
enable_xcremotecache(target, 1, xcrc_location, xccc_location, mode, exclude_build_configurations, final_target,fake_src_root, exclude_sdks_configurations)
enable_xcremotecache(target, 1, xcrc_location, xccc_location, mode, exclude_build_configurations, final_target,fake_src_root, exclude_sdks_configurations, enable_swift_driver_integration)
end
generated_project.save()
end
Expand Down Expand Up @@ -575,7 +578,7 @@ def self.save_lldbinit_rewrite(user_proj_directory,fake_src_root)
# Attach XCRC to the app targets
user_project.targets.each do |target|
next if exclude_targets.include?(target.name)
enable_xcremotecache(target, 0, xcrc_location, xccc_location, mode, exclude_build_configurations, final_target,fake_src_root, exclude_sdks_configurations)
enable_xcremotecache(target, 0, xcrc_location, xccc_location, mode, exclude_build_configurations, final_target,fake_src_root, exclude_sdks_configurations, enable_swift_driver_integration)
end

# Set Target sourcemap
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@
# limitations under the License.

module CocoapodsXcremotecache
VERSION = "0.0.16"
VERSION = "0.0.17"
end
84 changes: 58 additions & 26 deletions tasks/e2e.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
require 'json'
require "ostruct"
require 'yaml'

desc 'Support for E2E tests: building XCRemoteCache-enabled xcodeproj using xcodebuild'
namespace :e2e do
Expand All @@ -25,12 +26,21 @@
'primary_branch' => GIT_BRANCH,
'mode' => 'consumer',
'final_target' => 'XCRemoteCacheSample',
'artifact_maximum_age' => 0
'artifact_maximum_age' => 0,

}.freeze
# A list of configurations to merge with SHARED_COCOAPODS_CONFIG to run tests with
CONFIGS = {
'no_swift_driver' => {},
'swift_driver' => {
'enable_swift_driver_integration' => true
}
}.freeze
DEFAULT_EXPECTATIONS = {
'misses' => 0,
'hit_rate' => 100
}.freeze
EXCLUDED_ARCHS = 'x86_64'

Stats = Struct.new(:hits, :misses, :hit_rate)

Expand All @@ -43,9 +53,14 @@
start_nginx
configure_git

# Run scenarios for all Podfile scenarios
for podfile_path in Dir.glob('e2eTests/**/*.Podfile')
run_cocoapods_scenario(podfile_path)
for config_name, custom_config in CONFIGS
config = SHARED_COCOAPODS_CONFIG.merge(custom_config)
puts "Running E2E tests for config: #{config_name}"

# Run scenarios for all Podfile scenarios
for podfile_path in Dir.glob('e2eTests/**/*.Podfile')
run_cocoapods_scenario(config, podfile_path)
end
end
# Revert all side effects
clean
Expand All @@ -56,13 +71,27 @@
clean_server
start_nginx
configure_git
CONFIGS.each do |config_name, config|
puts "Running Standalone tests for config: #{config_name}"
run_standalone_scenario(config, config_name)
end
end

def self.run_standalone_scenario(config, config_name)
# Prepare binaries for the standalone mode
prepare_for_standalone(E2E_STANDALONE_SAMPLE_DIR)

puts 'Building standalone producer...'
####### Producer #########
clean_git

Dir.chdir(E2E_STANDALONE_SAMPLE_DIR) do
clean_git
system 'git checkout -f .'
# Include the config in the "shared" configuration that is commited-in to '.rcinfo'
rcinfo_path = '.rcinfo'
rcinfo = YAML.load(File.read(rcinfo_path)).merge(config)
File.open(rcinfo_path, 'w') {|f| f.write rcinfo.to_yaml }

# Run integrate the project
system("pwd")
system("#{XCRC_BINARIES}/xcprepare integrate --input StandaloneApp.xcodeproj --mode producer --final-producer-target StandaloneApp --configurations-exclude #{CONFIGURATIONS_EXCLUDE}")
Expand All @@ -76,22 +105,25 @@

####### Consumer #########
# new dir to emulate different srcroot
consumer_srcroot = "#{E2E_STANDALONE_SAMPLE_DIR}_consumer"
consumer_srcroot = "#{E2E_STANDALONE_SAMPLE_DIR}_consumer_#{config_name}"
system("mv #{E2E_STANDALONE_SAMPLE_DIR} #{consumer_srcroot}")
at_exit { puts("reverting #{E2E_STANDALONE_SAMPLE_DIR}"); system("mv #{consumer_srcroot} #{E2E_STANDALONE_SAMPLE_DIR}") }

prepare_for_standalone(consumer_srcroot)
Dir.chdir(consumer_srcroot) do
system("#{XCRC_BINARIES}/xcprepare integrate --input StandaloneApp.xcodeproj --mode consumer --final-producer-target StandaloneApp --consumer-eligible-configurations #{CONFIGURATION} --configurations-exclude #{CONFIGURATIONS_EXCLUDE}")
build_project(nil, "StandaloneApp.xcodeproj", 'WatchExtension', 'watch', 'watchOS', CONFIGURATION, {'derivedDataPath' => "#{DERIVED_DATA_PATH}_consumer"})
build_project(nil, "StandaloneApp.xcodeproj", 'StandaloneApp', 'iphone', 'iOS', CONFIGURATION, {'derivedDataPath' => "#{DERIVED_DATA_PATH}_consumer"})
valide_hit_rate(OpenStruct.new(DEFAULT_EXPECTATIONS))

puts 'Building standalone consumer with local change...'
# Extra: validate local compilation of the Standalone ObjC code
system("echo '' >> StandaloneApp/StandaloneObjc.m")
build_project(nil, "StandaloneApp.xcodeproj", 'WatchExtension', 'watch', 'watchOS', CONFIGURATION, {'derivedDataPath' => "#{DERIVED_DATA_PATH}_consumer_local"})
build_project(nil, "StandaloneApp.xcodeproj", 'StandaloneApp', 'iphone', 'iOS', CONFIGURATION, {'derivedDataPath' => "#{DERIVED_DATA_PATH}_consumer_local"})
begin
prepare_for_standalone(consumer_srcroot)
Dir.chdir(consumer_srcroot) do
system("#{XCRC_BINARIES}/xcprepare integrate --input StandaloneApp.xcodeproj --mode consumer --final-producer-target StandaloneApp --consumer-eligible-configurations #{CONFIGURATION} --configurations-exclude #{CONFIGURATIONS_EXCLUDE}")
build_project(nil, "StandaloneApp.xcodeproj", 'WatchExtension', 'watch', 'watchOS', CONFIGURATION, {'derivedDataPath' => "#{DERIVED_DATA_PATH}_consumer_#{config_name}"})
build_project(nil, "StandaloneApp.xcodeproj", 'StandaloneApp', 'iphone', 'iOS', CONFIGURATION, {'derivedDataPath' => "#{DERIVED_DATA_PATH}_consumer_#{config_name}"})
valide_hit_rate(OpenStruct.new(DEFAULT_EXPECTATIONS))

puts 'Building standalone consumer with local change...'
# Extra: validate local compilation of the Standalone ObjC code
system("echo '' >> StandaloneApp/StandaloneObjc.m")
build_project(nil, "StandaloneApp.xcodeproj", 'WatchExtension', 'watch', 'watchOS', CONFIGURATION, {'derivedDataPath' => "#{DERIVED_DATA_PATH}_consumer_local_#{config_name}"})
build_project(nil, "StandaloneApp.xcodeproj", 'StandaloneApp', 'iphone', 'iOS', CONFIGURATION, {'derivedDataPath' => "#{DERIVED_DATA_PATH}_consumer_local_#{config_name}"})
end
ensure
puts("reverting #{E2E_STANDALONE_SAMPLE_DIR}")
system("mv #{consumer_srcroot} #{E2E_STANDALONE_SAMPLE_DIR}")
end

# Revert all side effects
Expand Down Expand Up @@ -153,9 +185,9 @@ def self.clean
end

# xcremotecache configuration to add to Podfile
def self.cocoapods_configuration_string(extra_configs = {})
def self.cocoapods_configuration_string(config, extra_configs = {})
configuration_lines = ['xcremotecache({']
all_properties = SHARED_COCOAPODS_CONFIG.merge(extra_configs)
all_properties = config.merge(extra_configs)
config_lines = all_properties.map {|key, value| " \"#{key}\" => #{value.inspect},"}
configuration_lines.push(*config_lines)
configuration_lines << '})'
Expand All @@ -182,7 +214,7 @@ def self.build_project(workspace, project, scheme, sdk = 'iphone', platform = 'i
'derivedDataPath' => DERIVED_DATA_PATH,
}.merge(extra_args).compact
xcodebuild_vars = {
'EXCLUDED_ARCHS' => 'arm64'
'EXCLUDED_ARCHS' => EXCLUDED_ARCHS
}
args = ['set -o pipefail;', 'xcodebuild']
args.push(*xcodebuild_args.map {|k,v| "-#{k} '#{v}'"})
Expand Down Expand Up @@ -227,12 +259,12 @@ def self.valide_hit_rate(expectations)
puts("Hit rate: #{status.hit_rate}% (#{status.hits}/#{all_targets})")
end

def self.run_cocoapods_scenario(template_path)
def self.run_cocoapods_scenario(config, template_path)
# Optional file, which adds extra cocoapods configs to a template
template_config_path = "#{template_path}.config"
extra_config = File.exist?(template_config_path) ? JSON.load(File.read(template_config_path)) : {}
producer_configuration = cocoapods_configuration_string({'mode' => 'producer'}.merge(extra_config))
consumer_configuration = cocoapods_configuration_string(extra_config)
producer_configuration = cocoapods_configuration_string(config, {'mode' => 'producer'}.merge(extra_config))
consumer_configuration = cocoapods_configuration_string(config, extra_config)
expectations = build_expectations(template_path)

puts("****** Scenario: #{template_path}")
Expand Down

0 comments on commit 811cc00

Please sign in to comment.