diff --git a/CHANGELOG.md b/CHANGELOG.md index 502dd22cc..ff6150b3f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ _None_ ### New Features -* Introduce new `ios_send_app_size_metrics` and `android_send_app_size_metrics` actions. [#364] +* Introduce new `ios_send_app_size_metrics` and `android_send_app_size_metrics` actions. [#364] [#365] ### Bug Fixes diff --git a/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_send_app_size_metrics.rb b/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_send_app_size_metrics.rb index 3dbec910f..d5925c67a 100644 --- a/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_send_app_size_metrics.rb +++ b/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_send_app_size_metrics.rb @@ -3,6 +3,13 @@ module Fastlane module Actions class AndroidSendAppSizeMetricsAction < Action + # Keys used by the metrics payload + AAB_FILE_SIZE_KEY = 'AAB File Size'.freeze # value from `File.size` of the `.aab` + UNIVERSAL_APK_FILE_SIZE_KEY = 'Universal APK File Size'.freeze # value from `File.size` of the Universal `.apk` + UNIVERSAL_APK_SPLIT_NAME = 'Universal'.freeze # pseudo-name of the split representing the Universal `.apk` + APK_OPTIMIZED_FILE_SIZE_KEY = 'Optimized APK File Size'.freeze # value from `apkanalyzer apk file-size` + APK_OPTIMIZED_DOWNLOAD_SIZE_KEY = 'Download Size'.freeze # value from `apkanalyzer apk download-size` + def self.run(params) # Check input parameters api_url = URI(params[:api_url]) @@ -10,6 +17,9 @@ def self.run(params) if (api_token.nil? || api_token.empty?) && !api_url.is_a?(URI::File) UI.user_error!('An API token is required when using an `api_url` with a scheme other than `file://`') end + if params[:aab_path].nil? && params[:universal_apk_path].nil? + UI.user_error!('You must provide at least an `aab_path` or an `universal_apk_path`, or both') + end # Build the payload base metrics_helper = Fastlane::Helper::AppSizeMetricsHelper.new( @@ -21,28 +31,22 @@ def self.run(params) 'Build Type': params[:build_type], Source: params[:source] ) - metrics_helper.add_metric(name: 'AAB File Size', value: File.size(params[:aab_path])) + # Add AAB file size + metrics_helper.add_metric(name: AAB_FILE_SIZE_KEY, value: File.size(params[:aab_path])) unless params[:aab_path].nil? + # Add Universal APK file size + metrics_helper.add_metric(name: UNIVERSAL_APK_FILE_SIZE_KEY, value: File.size(params[:universal_apk_path])) unless params[:universal_apk_path].nil? - # Add device-specific 'splits' metrics to the payload if a `:include_split_sizes` is enabled + # Add optimized file and download sizes for each split `.apk` metrics to the payload if a `:include_split_sizes` is enabled if params[:include_split_sizes] - check_bundletool_installed! apkanalyzer_bin = params[:apkanalyzer_binary] || find_apkanalyzer_binary! - UI.message("[App Size Metrics] Generating the various APK splits from #{params[:aab_path]}...") - Dir.mktmpdir('release-toolkit-android-app-size-metrics') do |tmp_dir| - Action.sh('bundletool', 'build-apks', '--bundle', params[:aab_path], '--output-format', 'DIRECTORY', '--output', tmp_dir) - apks = Dir.glob('splits/*.apk', base: tmp_dir).map { |f| File.join(tmp_dir, f) } - UI.message("[App Size Metrics] Generated #{apks.length} APKs.") - - apks.each do |apk| - UI.message("[App Size Metrics] Computing file and download size of #{File.basename(apk)}...") + unless params[:aab_path].nil? + generate_split_apks(aab_path: params[:aab_path]) do |apk| split_name = File.basename(apk, '.apk') - file_size = Action.sh(apkanalyzer_bin, 'apk', 'file-size', apk, print_command: false, print_command_output: false).chomp.to_i - download_size = Action.sh(apkanalyzer_bin, 'apk', 'download-size', apk, print_command: false, print_command_output: false).chomp.to_i - metrics_helper.add_metric(name: 'APK File Size', value: file_size, metadata: { split: split_name }) - metrics_helper.add_metric(name: 'Download Size', value: download_size, metadata: { split: split_name }) + add_apk_size_metrics(helper: metrics_helper, apkanalyzer_bin: apkanalyzer_bin, apk: apk, split_name: split_name) end - - UI.message('[App Size Metrics] Done computing splits sizes.') + end + unless params[:universal_apk_path].nil? + add_apk_size_metrics(helper: metrics_helper, apkanalyzer_bin: apkanalyzer_bin, apk: params[:universal_apk_path], split_name: UNIVERSAL_APK_SPLIT_NAME) end end @@ -54,26 +58,79 @@ def self.run(params) ) end - def self.check_bundletool_installed! - Action.sh('command', '-v', 'bundletool', print_command: false, print_command_output: false) - rescue StandardError - UI.user_error!('bundletool is required to build the split APKs. Install it with `brew install bundletool`') - raise - end + ##################################################### + # @!group Small helper methods + ##################################################### + class << self + # @raise if `bundletool` can not be found in `$PATH` + def check_bundletool_installed! + Action.sh('command', '-v', 'bundletool', print_command: false, print_command_output: false) + rescue StandardError + UI.user_error!('`bundletool` is required to build the split APKs. Install it with `brew install bundletool`') + raise + end - def self.find_apkanalyzer_binary - sdk_root = ENV['ANDROID_SDK_ROOT'] || ENV['ANDROID_HOME'] - if sdk_root - pattern = File.join(sdk_root, 'cmdline-tools', '{latest,tools}', 'bin', 'apkanalyzer') - apkanalyzer_bin = Dir.glob(pattern).find { |path| File.executable?(path) } + # The path where the `apkanalyzer` binary was found, after searching it: + # - in priority in `$ANDROID_SDK_ROOT` (or `$ANDROID_HOME` for legacy setups), under `cmdline-tools/latest/bin/` or `cmdline-tools/tools/bin` + # - and falling back by trying to find it in `$PATH` + # + # @return [String,Nil] The path to `apkanalyzer`, or `nil` if it wasn't found in any of the above tested paths. + # + def find_apkanalyzer_binary + sdk_root = ENV['ANDROID_SDK_ROOT'] || ENV['ANDROID_HOME'] + if sdk_root + pattern = File.join(sdk_root, 'cmdline-tools', '{latest,tools}', 'bin', 'apkanalyzer') + apkanalyzer_bin = Dir.glob(pattern).find { |path| File.executable?(path) } + end + apkanalyzer_bin || Action.sh('command', '-v', 'apkanalyzer', print_command_output: false) { |_| nil } end - apkanalyzer_bin || Action.sh('command', '-v', 'apkanalyzer', print_command_output: false) { |_| nil } - end - def self.find_apkanalyzer_binary! - apkanalyzer_bin = find_apkanalyzer_binary - UI.user_error!('Unable to find `apkanalyzer` executable in `$PATH` nor `$ANDROID_SDK_ROOT`. Make sure you installed the Android SDK Command-line Tools') if apkanalyzer_bin.nil? - apkanalyzer_bin + # The path where the `apkanalyzer` binary was found, after searching it: + # - in priority in `$ANDROID_SDK_ROOT` (or `$ANDROID_HOME` for legacy setups), under `cmdline-tools/latest/bin/` or `cmdline-tools/tools/bin` + # - and falling back by trying to find it in `$PATH` + # + # @return [String] The path to `apkanalyzer` + # @raise [FastlaneCore::Interface::FastlaneError] if it wasn't found in any of the above tested paths. + # + def find_apkanalyzer_binary! + apkanalyzer_bin = find_apkanalyzer_binary + UI.user_error!('Unable to find `apkanalyzer` executable in either `$PATH` or `$ANDROID_SDK_ROOT`. Make sure you installed the Android SDK Command-line Tools') if apkanalyzer_bin.nil? + apkanalyzer_bin + end + + # Add the `file-size` and `download-size` values of an APK to the helper, as reported by the corresponding `apkanalyzer apk …` commands + # + # @param [Fastlane::Helper::AppSizeMetricsHelper] helper The helper to add the metrics to + # @param [String] apkanalyzer_bin The path to the `apkanalyzer` binary to use to extract those file and download sizes from the `.apk` + # @param [String] apk The path to the `.apk` file to extract the sizes from + # @param [String] split_name The name to use for the value of the `split` metadata key in the metrics being added + # + def add_apk_size_metrics(helper:, apkanalyzer_bin:, apk:, split_name:) + UI.message("[App Size Metrics] Computing file and download size of #{File.basename(apk)}...") + file_size = Action.sh(apkanalyzer_bin, 'apk', 'file-size', apk, print_command: false, print_command_output: false).chomp.to_i + download_size = Action.sh(apkanalyzer_bin, 'apk', 'download-size', apk, print_command: false, print_command_output: false).chomp.to_i + helper.add_metric(name: APK_OPTIMIZED_FILE_SIZE_KEY, value: file_size, metadata: { split: split_name }) + helper.add_metric(name: APK_OPTIMIZED_DOWNLOAD_SIZE_KEY, value: download_size, metadata: { split: split_name }) + end + + # Generates all the split `.apk` files (typically one per device architecture) from a given `.aab` file, then yield for each apk produced. + # + # @note The split `.apk` files are generated in a temporary directory and are thus all deleted after each of them has been `yield`ed to the provided block. + # @param [String] aab_path The path to the `.aab` file to generate split `.apk` files for + # @yield [apk] Calls the provided block once for each split `.apk` that was generated from the `.aab` + # @yieldparam apk [String] The path to one of the split `.apk` temporary file generated from the `.aab` + # + def generate_split_apks(aab_path:, &block) + check_bundletool_installed! + UI.message("[App Size Metrics] Generating the various APK splits from #{aab_path}...") + Dir.mktmpdir('release-toolkit-android-app-size-metrics') do |tmp_dir| + Action.sh('bundletool', 'build-apks', '--bundle', aab_path, '--output-format', 'DIRECTORY', '--output', tmp_dir) + apks = Dir.glob('splits/*.apk', base: tmp_dir).map { |f| File.join(tmp_dir, f) } + UI.message("[App Size Metrics] Generated #{apks.length} APKs.") + apks.each(&block) + UI.message('[App Size Metrics] Done computing splits sizes.') + end + end end ##################################################### @@ -95,6 +152,7 @@ def self.details DETAILS end + # rubocop:disable Metrics/MethodLength def self.available_options [ FastlaneCore::ConfigItem.new( @@ -165,7 +223,7 @@ def self.available_options env_name: 'FL_ANDROID_SEND_APP_SIZE_METRICS_AAB_PATH', description: 'The path to the .aab to extract size information from', type: String, - optional: false, + optional: true, # We can have `aab_path` only, or `universal_apk_path` only, or both (but not none) verify_block: proc do |value| UI.user_error!('You must provide an path to an existing `.aab` file') unless File.exist?(value) end @@ -178,6 +236,16 @@ def self.available_options type: FastlaneCore::Boolean, default_value: true ), + FastlaneCore::ConfigItem.new( + key: :universal_apk_path, + env_name: 'FL_ANDROID_SEND_APP_SIZE_METRICS_UNIVERSAL_APK_PATH', + description: 'The path to the Universal `.apk` to extract size information from', + type: String, + optional: true, # We can have `aab_path` only, or `universal_apk_path` only, or both (but not none) + verify_block: proc do |value| + UI.user_error!('You must provide a path to an existing `.apk` file') unless File.exist?(value) + end + ), FastlaneCore::ConfigItem.new( key: :apkanalyzer_binary, env_name: 'FL_ANDROID_SEND_APP_SIZE_METRICS_APKANALYZER_BINARY', @@ -191,6 +259,7 @@ def self.available_options ), ] end + # rubocop:enable Metrics/MethodLength def self.return_type :integer diff --git a/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_send_app_size_metrics.rb b/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_send_app_size_metrics.rb index b01f54908..fa3ca3bc6 100644 --- a/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_send_app_size_metrics.rb +++ b/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_send_app_size_metrics.rb @@ -4,6 +4,11 @@ module Fastlane module Actions class IosSendAppSizeMetricsAction < Action + # Keys used by the metrics payload + IPA_FILE_SIZE_KEY = 'File Size'.freeze # value from `File.size` of the Universal `.ipa` + IPA_DOWNLOAD_SIZE_KEY = 'Download Size'.freeze # value from `app-thinning.plist` + IPA_INSTALL_SIZE_KEY = 'Install Size'.freeze # value from `app-thinning.plist` + def self.run(params) # Check input parameters api_url = URI(params[:api_url]) @@ -20,7 +25,7 @@ def self.run(params) 'Build Type': params[:build_type], Source: params[:source] ) - metrics_helper.add_metric(name: 'File Size', value: File.size(params[:ipa_path])) + metrics_helper.add_metric(name: IPA_FILE_SIZE_KEY, value: File.size(params[:ipa_path])) # Add app-thinning metrics to the payload if a `.plist` is provided app_thinning_plist_path = params[:app_thinning_plist_path] || File.join(File.dirname(params[:ipa_path]), 'app-thinning.plist') @@ -30,8 +35,8 @@ def self.run(params) variant_descriptors = variant['variantDescriptors'] || [{ 'device' => 'Universal' }] variant_descriptors.each do |desc| variant_metadata = { device: desc['device'], 'OS Version': desc['os-version'] } - metrics_helper.add_metric(name: 'Download Size', value: variant['sizeCompressedApp'], metadata: variant_metadata) - metrics_helper.add_metric(name: 'Install Size', value: variant['sizeUncompressedApp'], metadata: variant_metadata) + metrics_helper.add_metric(name: IPA_DOWNLOAD_SIZE_KEY, value: variant['sizeCompressedApp'], metadata: variant_metadata) + metrics_helper.add_metric(name: IPA_INSTALL_SIZE_KEY, value: variant['sizeUncompressedApp'], metadata: variant_metadata) end end end diff --git a/spec/android_send_app_size_metrics_spec.rb b/spec/android_send_app_size_metrics_spec.rb index 9a51b8a58..49cd34c13 100644 --- a/spec/android_send_app_size_metrics_spec.rb +++ b/spec/android_send_app_size_metrics_spec.rb @@ -3,13 +3,22 @@ describe Fastlane::Actions::AndroidSendAppSizeMetricsAction do let(:test_data_dir) { File.join(File.dirname(__FILE__), 'test-data', 'app_size_metrics') } - def test_app_size_action(fake_aab_size:, fake_apks:, expected_payload:, **other_action_args) + def test_app_size_action(fake_aab_size:, fake_apks:, fake_universal_apk_sizes:, expected_payload:, **other_action_args) in_tmp_dir do |tmp_dir| # Arrange output_file = File.join(tmp_dir, 'output-payload') - aab_path = File.join(tmp_dir, 'fake.aab') - File.write(aab_path, '-fake-aab-file-') - allow(File).to receive(:size).with(aab_path).and_return(fake_aab_size) + aab_path = nil + unless fake_aab_size.nil? + aab_path = File.join(tmp_dir, 'fake.aab') + File.write(aab_path, '-fake-aab-file-') + allow(File).to receive(:size).with(aab_path).and_return(fake_aab_size) + end + universal_apk_path = nil + unless fake_universal_apk_sizes.empty? + universal_apk_path = File.join(tmp_dir, 'fake.apk') + File.write(universal_apk_path, '-fake-universal-apk-file-') + allow(File).to receive(:size).with(universal_apk_path).and_return(fake_universal_apk_sizes[0]) + end if other_action_args[:include_split_sizes] != false # Arrange: fake that apkanalyzer exists @@ -17,26 +26,34 @@ def test_app_size_action(fake_aab_size:, fake_apks:, expected_payload:, **other_ allow(described_class).to receive(:find_apkanalyzer_binary).and_return(apkanalyzer_bin) allow(File).to receive(:executable?).with(apkanalyzer_bin).and_return(true) - # Arrange: fake that bundletool exists and mock its call to create fake apks with corresponding apkanalyzer calls mocks - allow(Fastlane::Action).to receive(:sh).with('command', '-v', 'bundletool', anything) - allow(Fastlane::Action).to receive(:sh).with('bundletool', 'build-apks', '--bundle', aab_path, '--output-format', 'DIRECTORY', '--output', anything) do |*args| - bundletool_tmpdir = args.last - FileUtils.mkdir(File.join(bundletool_tmpdir, 'splits')) - fake_apks.each do |apk_name, sizes| - apk_path = File.join(bundletool_tmpdir, 'splits', apk_name.to_s) - File.write(apk_path, "Fake APK file (#{sizes})") - allow(Fastlane::Action).to receive(:sh).with(apkanalyzer_bin, 'apk', 'file-size', apk_path, anything).and_return(sizes[0].to_s) - allow(Fastlane::Action).to receive(:sh).with(apkanalyzer_bin, 'apk', 'download-size', apk_path, anything).and_return(sizes[1].to_s) + unless fake_apks.empty? + # Arrange: fake that `bundletool` exists and mock its call to create fake apks with corresponding apkanalyzer calls mocks + allow(Fastlane::Action).to receive(:sh).with('command', '-v', 'bundletool', anything) + allow(Fastlane::Action).to receive(:sh).with('bundletool', 'build-apks', '--bundle', aab_path, '--output-format', 'DIRECTORY', '--output', anything) do |*args| + bundletool_tmpdir = args.last + FileUtils.mkdir(File.join(bundletool_tmpdir, 'splits')) + fake_apks.each do |apk_name, sizes| + apk_path = File.join(bundletool_tmpdir, 'splits', apk_name.to_s) + File.write(apk_path, "Fake APK file (#{sizes})") + allow(Fastlane::Action).to receive(:sh).with(apkanalyzer_bin, 'apk', 'file-size', apk_path, anything).and_return(sizes[0].to_s) + allow(Fastlane::Action).to receive(:sh).with(apkanalyzer_bin, 'apk', 'download-size', apk_path, anything).and_return(sizes[1].to_s) + end end end + unless fake_universal_apk_sizes.empty? + allow(Fastlane::Action).to receive(:sh).with(apkanalyzer_bin, 'apk', 'file-size', universal_apk_path, anything).and_return(fake_universal_apk_sizes[1].to_s) + allow(Fastlane::Action).to receive(:sh).with(apkanalyzer_bin, 'apk', 'download-size', universal_apk_path, anything).and_return(fake_universal_apk_sizes[2].to_s) + end end # Act - code = run_described_fastlane_action( + action_params = { api_url: File.join('file://localhost/', output_file), aab_path: aab_path, + universal_apk_path: universal_apk_path, **other_action_args - ) + }.compact + code = run_described_fastlane_action(action_params) # Asserts expect(code).to eq(201) @@ -51,87 +68,192 @@ def test_app_size_action(fake_aab_size:, fake_apks:, expected_payload:, **other_ end context 'when `include_split_sizes` is turned off' do - it 'generates the expected payload compressed by default' do - expected = { - meta: [ - { name: 'Platform', value: 'Android' }, - { name: 'App Name', value: 'my-app' }, - { name: 'App Version', value: '10.2-rc-3' }, - { name: 'Product Flavor', value: 'Vanilla' }, - { name: 'Build Type', value: 'Release' }, - { name: 'Source', value: 'unit-test' }, - ], - metrics: [ - { name: 'AAB File Size', value: 123_456 }, - ] - } - - test_app_size_action( - fake_aab_size: 123_456, - fake_apks: {}, - expected_payload: expected, + let(:common_action_args) do + { app_name: 'my-app', app_version_name: '10.2-rc-3', product_flavor: 'Vanilla', build_type: 'Release', source: 'unit-test', include_split_sizes: false - ) + } end - it 'generates the expected payload uncompressed when disabling gzip' do - expected = { - meta: [ - { name: 'Platform', value: 'Android' }, - { name: 'App Name', value: 'my-app' }, - { name: 'App Version', value: '10.2-rc-3' }, - { name: 'Product Flavor', value: 'Vanilla' }, - { name: 'Build Type', value: 'Release' }, - { name: 'Source', value: 'unit-test' }, - ], - metrics: [ - { name: 'AAB File Size', value: 123_456 }, - ] - } + context 'when only providing an `aab_path`' do + it 'generates the expected payload compressed by default' do + expected = { + meta: [ + { name: 'Platform', value: 'Android' }, + { name: 'App Name', value: 'my-app' }, + { name: 'App Version', value: '10.2-rc-3' }, + { name: 'Product Flavor', value: 'Vanilla' }, + { name: 'Build Type', value: 'Release' }, + { name: 'Source', value: 'unit-test' }, + ], + metrics: [ + { name: 'AAB File Size', value: 123_456 }, + ] + } - test_app_size_action( - fake_aab_size: 123_456, - fake_apks: {}, - expected_payload: expected, - app_name: 'my-app', - app_version_name: '10.2-rc-3', - product_flavor: 'Vanilla', - build_type: 'Release', - source: 'unit-test', - include_split_sizes: false, - use_gzip_content_encoding: false - ) + test_app_size_action( + fake_aab_size: 123_456, + fake_apks: {}, + fake_universal_apk_sizes: [], + expected_payload: expected, + **common_action_args + ) + end + + it 'generates the expected payload uncompressed when disabling gzip' do + expected = { + meta: [ + { name: 'Platform', value: 'Android' }, + { name: 'App Name', value: 'my-app' }, + { name: 'App Version', value: '10.2-rc-3' }, + { name: 'Product Flavor', value: 'Vanilla' }, + { name: 'Build Type', value: 'Release' }, + { name: 'Source', value: 'unit-test' }, + ], + metrics: [ + { name: 'AAB File Size', value: 123_456 }, + { name: 'Universal APK File Size', value: 56_789 }, + ] + } + + test_app_size_action( + fake_aab_size: 123_456, + fake_apks: {}, + fake_universal_apk_sizes: [56_789], + expected_payload: expected, + **common_action_args, + use_gzip_content_encoding: false + ) + end + end + + context 'when only providing an `universal_apk_path`' do + it 'generates the expected payload containing the apk file size' do + expected = { + meta: [ + { name: 'Platform', value: 'Android' }, + { name: 'App Name', value: 'my-app' }, + { name: 'App Version', value: '10.2-rc-3' }, + { name: 'Product Flavor', value: 'Vanilla' }, + { name: 'Build Type', value: 'Release' }, + { name: 'Source', value: 'unit-test' }, + ], + metrics: [ + { name: 'Universal APK File Size', value: 567_654_321 }, + ] + } + + test_app_size_action( + fake_aab_size: nil, + fake_apks: {}, + fake_universal_apk_sizes: [567_654_321], + expected_payload: expected, + **common_action_args + ) + end + end + + context 'when providing both an `aab_path` and an `universal_apk_path`' do + it 'generates the expected payload containing the aab and universal apk file size' do + expected = { + meta: [ + { name: 'Platform', value: 'Android' }, + { name: 'App Name', value: 'my-app' }, + { name: 'App Version', value: '10.2-rc-3' }, + { name: 'Product Flavor', value: 'Vanilla' }, + { name: 'Build Type', value: 'Release' }, + { name: 'Source', value: 'unit-test' }, + ], + metrics: [ + { name: 'AAB File Size', value: 123_456 }, + { name: 'Universal APK File Size', value: 567_654_321 }, + ] + } + + test_app_size_action( + fake_aab_size: 123_456, + fake_apks: {}, + fake_universal_apk_sizes: [567_654_321], + expected_payload: expected, + **common_action_args + ) + end end end context 'when keeping the default value of `include_split_sizes` turned on' do - it 'generates the expected payload containing the aab file size and optimized split sizes' do - expected_fixture = File.join(test_data_dir, 'android-metrics-payload.json') - expected = JSON.parse(File.read(expected_fixture)) - - test_app_size_action( - fake_aab_size: 987_654_321, - fake_apks: { - 'base-arm64_v8a.apk': [164_080, 64_080], - 'base-arm64_v8a_2.apk': [164_082, 64_082], - 'base-armeabi.apk': [150_000, 50_000], - 'base-armeabi_2.apk': [150_002, 50_002], - 'base-armeabi_v7a.apk': [150_070, 50_070], - 'base-armeabi_v7a_2.apk': [150_072, 50_072] - }, - expected_payload: expected, + let(:common_action_args) do + { app_name: 'wordpress', app_version_name: '19.8-rc-3', app_version_code: 1214, product_flavor: 'Vanilla', build_type: 'Release', source: 'unit-test' - ) + } + end + + context 'when only providing an `aab_path`' do + it 'generates the expected payload containing the aab file size and optimized split sizes' do + expected_fixture = File.join(test_data_dir, 'android-metrics-payload-aab.json') + expected = JSON.parse(File.read(expected_fixture)) + + test_app_size_action( + fake_aab_size: 987_654_321, + fake_apks: { + 'base-arm64_v8a.apk': [164_080, 64_080], + 'base-arm64_v8a_2.apk': [164_082, 64_082], + 'base-armeabi.apk': [150_000, 50_000], + 'base-armeabi_2.apk': [150_002, 50_002], + 'base-armeabi_v7a.apk': [150_070, 50_070], + 'base-armeabi_v7a_2.apk': [150_072, 50_072] + }, + fake_universal_apk_sizes: [], + expected_payload: expected, + **common_action_args + ) + end + end + + context 'when only providing an `universal_apk_path`' do + it 'generates the expected payload containing the apk file size and optimized file and download sizes' do + expected_fixture = File.join(test_data_dir, 'android-metrics-payload-apk.json') + expected = JSON.parse(File.read(expected_fixture)) + + test_app_size_action( + fake_aab_size: nil, + fake_apks: {}, + fake_universal_apk_sizes: [567_654_321, 555_000, 533_000], + expected_payload: expected, + **common_action_args + ) + end + end + + context 'when providing both an `aab_path` and an `universal_apk_path`' do + it 'generates the expected payload containing the aab and universal apk file size and optimized file and download sizes for all splits' do + expected_fixture = File.join(test_data_dir, 'android-metrics-payload-aab+apk.json') + expected = JSON.parse(File.read(expected_fixture)) + + test_app_size_action( + fake_aab_size: 987_654_321, + fake_apks: { + 'base-arm64_v8a.apk': [164_080, 64_080], + 'base-arm64_v8a_2.apk': [164_082, 64_082], + 'base-armeabi.apk': [150_000, 50_000], + 'base-armeabi_2.apk': [150_002, 50_002], + 'base-armeabi_v7a.apk': [150_070, 50_070], + 'base-armeabi_v7a_2.apk': [150_072, 50_072] + }, + fake_universal_apk_sizes: [567_654_321, 555_000, 533_000], + expected_payload: expected, + **common_action_args + ) + end end end end diff --git a/spec/test-data/app_size_metrics/android-metrics-payload-aab+apk.json b/spec/test-data/app_size_metrics/android-metrics-payload-aab+apk.json new file mode 100644 index 000000000..4fba63922 --- /dev/null +++ b/spec/test-data/app_size_metrics/android-metrics-payload-aab+apk.json @@ -0,0 +1,29 @@ +{ + "meta": [ + { "name": "Platform", "value": "Android" }, + { "name": "App Name", "value": "wordpress" }, + { "name": "App Version", "value": "19.8-rc-3" }, + { "name": "Version Code", "value": 1214 }, + { "name": "Product Flavor", "value": "Vanilla" }, + { "name": "Build Type", "value": "Release" }, + { "name": "Source", "value": "unit-test" } + ], + "metrics": [ + { "name": "AAB File Size", "value": 987654321 }, + { "name": "Universal APK File Size", "value": 567654321 }, + { "name": "Optimized APK File Size", "value": 164082, "meta": [{ "name": "split", "value": "base-arm64_v8a_2" }] }, + { "name": "Download Size", "value": 64082, "meta": [{ "name": "split", "value": "base-arm64_v8a_2" }] }, + { "name": "Optimized APK File Size", "value": 150072, "meta": [{ "name": "split", "value": "base-armeabi_v7a_2" }] }, + { "name": "Download Size", "value": 50072, "meta": [{ "name": "split", "value": "base-armeabi_v7a_2" }] }, + { "name": "Optimized APK File Size", "value": 150002, "meta": [{ "name": "split", "value": "base-armeabi_2" }] }, + { "name": "Download Size", "value": 50002, "meta": [{ "name": "split", "value": "base-armeabi_2" }] }, + { "name": "Optimized APK File Size", "value": 164080, "meta": [{ "name": "split", "value": "base-arm64_v8a" }] }, + { "name": "Download Size", "value": 64080, "meta": [{ "name": "split", "value": "base-arm64_v8a" }] }, + { "name": "Optimized APK File Size", "value": 150000, "meta": [{ "name": "split", "value": "base-armeabi" }] }, + { "name": "Download Size", "value": 50000, "meta": [{ "name": "split", "value": "base-armeabi" }] }, + { "name": "Optimized APK File Size", "value": 150070, "meta": [{ "name": "split", "value": "base-armeabi_v7a" }] }, + { "name": "Download Size", "value": 50070, "meta": [{ "name": "split", "value": "base-armeabi_v7a" }] }, + { "name": "Optimized APK File Size", "value": 555000, "meta": [{ "name": "split", "value": "Universal" }] }, + { "name": "Download Size", "value": 533000, "meta": [{ "name": "split", "value": "Universal" }] } + ] +} diff --git a/spec/test-data/app_size_metrics/android-metrics-payload.json b/spec/test-data/app_size_metrics/android-metrics-payload-aab.json similarity index 60% rename from spec/test-data/app_size_metrics/android-metrics-payload.json rename to spec/test-data/app_size_metrics/android-metrics-payload-aab.json index f77014b5f..e7f366942 100644 --- a/spec/test-data/app_size_metrics/android-metrics-payload.json +++ b/spec/test-data/app_size_metrics/android-metrics-payload-aab.json @@ -10,17 +10,17 @@ ], "metrics": [ { "name": "AAB File Size", "value": 987654321 }, - { "name": "APK File Size", "value": 164082, "meta": [{ "name": "split", "value": "base-arm64_v8a_2" }] }, + { "name": "Optimized APK File Size", "value": 164082, "meta": [{ "name": "split", "value": "base-arm64_v8a_2" }] }, { "name": "Download Size", "value": 64082, "meta": [{ "name": "split", "value": "base-arm64_v8a_2" }] }, - { "name": "APK File Size", "value": 150072, "meta": [{ "name": "split", "value": "base-armeabi_v7a_2" }] }, + { "name": "Optimized APK File Size", "value": 150072, "meta": [{ "name": "split", "value": "base-armeabi_v7a_2" }] }, { "name": "Download Size", "value": 50072, "meta": [{ "name": "split", "value": "base-armeabi_v7a_2" }] }, - { "name": "APK File Size", "value": 150002, "meta": [{ "name": "split", "value": "base-armeabi_2" }] }, + { "name": "Optimized APK File Size", "value": 150002, "meta": [{ "name": "split", "value": "base-armeabi_2" }] }, { "name": "Download Size", "value": 50002, "meta": [{ "name": "split", "value": "base-armeabi_2" }] }, - { "name": "APK File Size", "value": 164080, "meta": [{ "name": "split", "value": "base-arm64_v8a" }] }, + { "name": "Optimized APK File Size", "value": 164080, "meta": [{ "name": "split", "value": "base-arm64_v8a" }] }, { "name": "Download Size", "value": 64080, "meta": [{ "name": "split", "value": "base-arm64_v8a" }] }, - { "name": "APK File Size", "value": 150000, "meta": [{ "name": "split", "value": "base-armeabi" }] }, + { "name": "Optimized APK File Size", "value": 150000, "meta": [{ "name": "split", "value": "base-armeabi" }] }, { "name": "Download Size", "value": 50000, "meta": [{ "name": "split", "value": "base-armeabi" }] }, - { "name": "APK File Size", "value": 150070, "meta": [{ "name": "split", "value": "base-armeabi_v7a" }] }, + { "name": "Optimized APK File Size", "value": 150070, "meta": [{ "name": "split", "value": "base-armeabi_v7a" }] }, { "name": "Download Size", "value": 50070, "meta": [{ "name": "split", "value": "base-armeabi_v7a" }] } ] } diff --git a/spec/test-data/app_size_metrics/android-metrics-payload-apk.json b/spec/test-data/app_size_metrics/android-metrics-payload-apk.json new file mode 100644 index 000000000..1543ccb4a --- /dev/null +++ b/spec/test-data/app_size_metrics/android-metrics-payload-apk.json @@ -0,0 +1,16 @@ +{ + "meta": [ + { "name": "Platform", "value": "Android" }, + { "name": "App Name", "value": "wordpress" }, + { "name": "App Version", "value": "19.8-rc-3" }, + { "name": "Version Code", "value": 1214 }, + { "name": "Product Flavor", "value": "Vanilla" }, + { "name": "Build Type", "value": "Release" }, + { "name": "Source", "value": "unit-test" } + ], + "metrics": [ + { "name": "Universal APK File Size", "value": 567654321 }, + { "name": "Optimized APK File Size", "value": 555000, "meta": [{ "name": "split", "value": "Universal" }] }, + { "name": "Download Size", "value": 533000, "meta": [{ "name": "split", "value": "Universal" }] } + ] +}