Skip to content

Commit 848c0ae

Browse files
committed
Move log file parsing out of the FirebaseHelper
The Firebase Helper had a lot of pieces doing a variety of things – splitting this into model objects makes it easier to change and test.
1 parent f866e66 commit 848c0ae

File tree

7 files changed

+233
-241
lines changed

7 files changed

+233
-241
lines changed

lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_firebase_test.rb

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,20 @@
22

33
module Fastlane
44
module Actions
5-
require_relative '../../helper/android/android_firebase_helper'
6-
75
module SharedValues
8-
FIREBASE_PROJECT_ID = :FIREBASE_PROJECT_ID
9-
FIREBASE_CREDENTIALS = :FIREBASE_CREDENTIALS
106
FIREBASE_TEST_RESULT = :FIREBASE_TEST_ACTION_RESULT
7+
FIREBASE_TEST_LOG_FILE = :FIREBASE_TEST_LOG_FILE
118
FIREBASE_TEST_LOG_FILE_PATH = :FIREBASE_TEST_LOG_FILE_PATH
129
FIREBASE_TEST_RESULTS_FILE_PATH = :FIREBASE_TEST_LOG_FILE_PATH
1310
end
1411

1512
class AndroidFirebaseTestAction < Action
1613
def self.run(params)
1714
# Preflight – ensure the system is set up correctly
18-
Fastlane::Helper::Android::FirebaseHelper.verify_has_gcloud_binary
15+
Fastlane::FirebaseTestRunner.verify_has_gcloud_binary
1916

2017
# Log in to Firebase (and validate credentials)
21-
Fastlane::Helper::Android::FirebaseHelper.setup(key_file: params[:key_file])
18+
test_runner = Fastlane::FirebaseTestRunner.new(key_file: params[:key_file])
2219

2320
# Set up the log file and output directory
2421
Fastlane::Actions.lane_context[:FIREBASE_TEST_RESULTS_FILE_PATH] = params[:results_output_dir]
@@ -31,23 +28,23 @@ def self.run(params)
3128
orientation: params[:orientation]
3229
)
3330

34-
result = Fastlane::Helper::Android::FirebaseHelper.run_tests(
31+
result = test_runner.run_tests(
3532
apk_path: params[:apk_path],
3633
test_apk_path: params[:test_apk_path],
3734
device: device,
3835
type: params[:type]
3936
)
4037

4138
# Download all of the outputs from the job to the local machine
42-
Fastlane::Helper::Android::FirebaseHelper.download_raw_results
39+
test_runner.download_raw_results(params[:results_output_dir])
4340

4441
if result == true
4542
UI.success 'Firebase Tests Complete'
4643
return
4744
end
4845

49-
more_details_url = Fastlane::Helper::Android::FirebaseHelper.more_details_url
50-
FastlaneCore::UI.test_failure! "Firebase Tests failed – more information can be found at #{more_details_url}"
46+
log_file = Fastlane::Actions.lane_context[:FIREBASE_TEST_LOG_FILE]
47+
FastlaneCore::UI.test_failure! "Firebase Tests failed – more information can be found at #{log_file.more_details_url}"
5148
end
5249

5350
#####################################################
@@ -155,7 +152,7 @@ def self.available_options
155152
type: String,
156153
default_value: 'instrumentation',
157154
verify_block: proc do |value|
158-
types = Fastlane::Helper::Android::FirebaseHelper.valid_test_types
155+
types = Fastlane::FirebaseTestRunner::VALID_TEST_TYPES
159156
next if types.include? value
160157

161158
UI.user_error!("Invalid Test Type: #{value}. Valid Types: #{types}")

lib/fastlane/plugin/wpmreleasetoolkit/helper/android/android_firebase_helper.rb

Lines changed: 0 additions & 127 deletions
This file was deleted.
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
module Fastlane
2+
class FirebaseTestLabLogFile
3+
def initialize(path:)
4+
raise "No log file found at path #{path}" unless File.file? path
5+
6+
@path = path
7+
end
8+
9+
# Scan the log file to for indications that the Test Run failed
10+
def indicates_failure
11+
File.readlines(@path).any? { |line| !line.include? 'Failed' }
12+
end
13+
14+
# Parse the log for the "More details are available..." URL
15+
def more_details_url
16+
File.readlines(@path)
17+
.map { |line| URI.extract(line) }
18+
.flatten
19+
.compact
20+
.filter { |string| string.include? 'matrices' }
21+
.first
22+
end
23+
24+
# Parse the log for the Google Cloud Storage Bucket URL
25+
def raw_results_paths
26+
uri = File.readlines(@path)
27+
.map { |line| URI.extract(line) }
28+
.flatten
29+
.compact
30+
.map { |string| URI(string) }
31+
.filter { |u| u.scheme == 'gs' }
32+
.first
33+
34+
return nil if uri.nil?
35+
36+
return {
37+
bucket: uri.host,
38+
prefix: uri.path.delete_prefix('/').chomp('/')
39+
}
40+
end
41+
end
42+
end
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
require 'json'
2+
require 'uri'
3+
4+
module Fastlane
5+
class FirebaseTestRunner
6+
VALID_TEST_TYPES = %w[instrumentation robo].freeze
7+
8+
def initialize(key_file:, automatic_login: true)
9+
raise "Unable to find key file: #{key_file}" unless File.file? key_file
10+
11+
@key_file = key_file
12+
authenticate if automatic_login
13+
end
14+
15+
def authenticate
16+
Action.sh(
17+
'gcloud', 'auth', 'activate-service-account',
18+
'--key-file', @key_file
19+
)
20+
end
21+
22+
def run_tests(apk_path:, test_apk_path:, device:, type: 'instrumentation')
23+
raise "Unable to find apk: #{apk_path}" unless File.file? apk_path
24+
raise "Unable to find apk: #{test_apk_path}" unless File.file? test_apk_path
25+
raise "Invalid Type: #{type}" unless VALID_TEST_TYPES.include? type
26+
27+
command = [
28+
'gcloud', 'firebase', 'test', 'android', 'run',
29+
'--type', Shellwords.escape(type),
30+
'--app', Shellwords.escape(apk_path),
31+
'--test', Shellwords.escape(test_apk_path),
32+
'--device', Shellwords.escape(device.to_s),
33+
'--verbosity', 'info',
34+
].join(' ')
35+
36+
log_file_path = Fastlane::Actions.lane_context[:FIREBASE_TEST_LOG_FILE_PATH]
37+
UI.message "Streaming log output to #{log_file_path}"
38+
Action.sh("#{command} 2>&1 | tee #{log_file_path}")
39+
40+
# Make the file object available to other tasks
41+
file = FirebaseTestLabLogFile.new(path: log_file_path)
42+
Fastlane::Actions.lane_context[:FIREBASE_TEST_LOG_FILE] = file
43+
44+
# Return `true` if it looks like the test passed
45+
!file.indicates_failure
46+
end
47+
48+
def download_raw_results(log_file:, destination:)
49+
raise unless log_file.is_a? Fastlane::FirebaseTestLabLogFile
50+
51+
paths = log_file.raw_results_paths
52+
raise "Log File doesn't contain a raw results URL" if paths.nil?
53+
54+
FileUtils.mkdir_p(destination) unless File.directory? destination
55+
56+
require 'google/cloud/storage'
57+
storage = Google::Cloud::Storage.new(
58+
project_id: Fastlane::Actions.lane_context[:FIREBASE_PROJECT_ID],
59+
credentials: Fastlane::Actions.lane_context[:FIREBASE_CREDENTIALS]
60+
)
61+
62+
# Set up the download
63+
bucket = storage.bucket(paths[:bucket])
64+
files_to_download = bucket.files(prefix: paths[:prefix])
65+
66+
# Download the files
67+
UI.header "Downloading Results Files to #{destination}"
68+
files_to_download.each { |file| download_file(file: file, destination: destination) }
69+
end
70+
71+
def download_file(file:, destination:)
72+
destination = File.join(destination, file.name)
73+
FileUtils.mkdir_p(File.dirname(destination))
74+
75+
# Print our progress
76+
UI.message(file.name)
77+
78+
file.download(destination)
79+
end
80+
81+
def self.verify_has_gcloud_binary
82+
Action.sh('command -v gcloud > /dev/null')
83+
rescue StandardError
84+
UI.user_error!("The `gcloud` binary isn't available on this machine. Unable to continue.")
85+
end
86+
end
87+
end

0 commit comments

Comments
 (0)