xamarin-test-cloud 2.0.3 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -0
- data/lib/xamarin-test-cloud/cli.rb +416 -154
- data/lib/xamarin-test-cloud/dsym.rb +5 -7
- data/lib/xamarin-test-cloud/test_file.rb +2 -2
- data/lib/xamarin-test-cloud/version.rb +1 -1
- metadata +3 -4
- data/lib/xamarin-test-cloud/cli.rb alias +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7179d836cc88e3266185b3d9cd68afa6b8272681
|
4
|
+
data.tar.gz: 50bb75d45e930271faaa5a28ae6f718895790a6d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2adca474985c4505d9dbe98b5ae5c0e0da1365d921578328f2a3a21a7f13d84fdc8ef07e405cb638b8042bb3543602b49b642bac55117bc37fb9c796d5ed9573
|
7
|
+
data.tar.gz: 2ed5fcd3105e9622d69c1e88318b4c64bf4fced3bc516877c7cc6804385739f46337fe7acafba81e2f80a0fb2681b8531b09f7d35d6eda65c1f9f739bde642f9
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,16 @@
|
|
1
|
+
### 2.1.0
|
2
|
+
|
3
|
+
* CLI#collect\_files\_with\_glob should collect . files and skip anything
|
4
|
+
under \_\_MACOSX #89
|
5
|
+
* Fix the collection of included files #87
|
6
|
+
* Add `prepare` command to generate an upload manifest #85
|
7
|
+
|
8
|
+
### 2.0.3
|
9
|
+
|
10
|
+
* CLI: add calabash=true to upload data #82
|
11
|
+
* Support --include flag #81
|
12
|
+
* Gem: force HTTPClient 2.7.1 or higher #80
|
13
|
+
|
1
14
|
### 2.0.2
|
2
15
|
|
3
16
|
* Use otool-classic to check for the CalabashServer whenever it is
|
@@ -41,7 +41,7 @@ module XamarinTestCloud
|
|
41
41
|
:include_files_input,
|
42
42
|
:locale, :series, :session_id
|
43
43
|
|
44
|
-
|
44
|
+
attr_reader :included_files_map
|
45
45
|
|
46
46
|
def self.exit_on_failure?
|
47
47
|
true
|
@@ -63,6 +63,7 @@ module XamarinTestCloud
|
|
63
63
|
FILE_UPLOAD_ENDPOINT = 'upload2'
|
64
64
|
FORM_URL_ENCODED_ENDPOINT = 'upload'
|
65
65
|
|
66
|
+
MANIFEST_SCHEMA_VERSION = "1.0.0"
|
66
67
|
|
67
68
|
def self.source_root
|
68
69
|
File.join(File.dirname(__FILE__), '..')
|
@@ -134,8 +135,7 @@ module XamarinTestCloud
|
|
134
135
|
:type => :boolean,
|
135
136
|
:default => false
|
136
137
|
|
137
|
-
|
138
|
-
#A list of items which are files or folders in the workspace.
|
138
|
+
# A list of items which are files or folders in the workspace.
|
139
139
|
# Must be a path relative to workspace and
|
140
140
|
# must not refer outside (e.g. no usage of ../ etc). Example --include .xtc foo
|
141
141
|
method_option 'include',
|
@@ -188,27 +188,8 @@ module XamarinTestCloud
|
|
188
188
|
$stdout = @async_log
|
189
189
|
end
|
190
190
|
|
191
|
-
|
192
|
-
|
193
|
-
raise ValidationError, "App is not a file: #{app_path}"
|
194
|
-
end
|
195
|
-
|
196
|
-
if shared_runtime?(app_path)
|
197
|
-
puts "Xamarin Test Cloud doesn't yet support shared runtime apps."
|
198
|
-
puts "To test your app it needs to be compiled for release."
|
199
|
-
puts "You can learn how to compile you app for release here:"
|
200
|
-
puts "http://docs.xamarin.com/guides/android/deployment%2C_testing%2C_and_metrics/publishing_an_application/part_1_-_preparing_an_application_for_release"
|
201
|
-
# TODO Change this to: "Shared runtime apps are not supported."
|
202
|
-
raise ValidationError, "Aborting"
|
203
|
-
end
|
204
|
-
|
205
|
-
app_extension = File.extname(app_path)
|
206
|
-
unless /ipa/i.match(app_extension) || /apk/i.match(app_extension)
|
207
|
-
raise ValidationError, "App #{app_path} must be an .ipa or .apk file"
|
208
|
-
end
|
209
|
-
|
210
|
-
|
211
|
-
self.app = app_path
|
191
|
+
# Verifies the app argument and sets the @app instance variable
|
192
|
+
verify_app!(app)
|
212
193
|
|
213
194
|
self.user = options['user']
|
214
195
|
|
@@ -230,36 +211,15 @@ module XamarinTestCloud
|
|
230
211
|
|
231
212
|
self.priority = options['priority']
|
232
213
|
|
233
|
-
self.skip_config_check = options['skip-config-check']
|
234
214
|
|
235
215
|
self.include_files_input = options['include']
|
236
216
|
|
237
217
|
# Resolves the workspace and sets the @derived_workspace variable.
|
238
218
|
expect_features_directory_in_workspace
|
239
219
|
|
240
|
-
#
|
241
|
-
|
242
|
-
|
243
|
-
default_config = File.join(derived_workspace, 'config', 'cucumber.yml')
|
244
|
-
# TODO .config/cucumber.yml is valid
|
245
|
-
# TODO cucumber.yaml is valid
|
246
|
-
if File.exist?(default_config) && self.config.nil?
|
247
|
-
# TODO Reword the header.
|
248
|
-
log_header 'Warning: Detected cucumber.yml config file, but no --config specified'
|
249
|
-
|
250
|
-
# TODO Reword the message and pass to raise as message.
|
251
|
-
puts "Please specify --config #{default_config}"
|
252
|
-
puts 'and specify a profile via --profile'
|
253
|
-
puts 'If you know what you are doing you can skip this check with'
|
254
|
-
puts '--skip-config-check'
|
255
|
-
|
256
|
-
# TODO This error message is wrong.
|
257
|
-
# It should be cucumber.yml detected by no --config option set.
|
258
|
-
raise ValidationError, "#{default_config} detected but no profile selected."
|
259
|
-
end
|
260
|
-
end
|
261
|
-
|
262
|
-
self.included_files_map = parse_included_folders
|
220
|
+
# Verifies the --config and --profile options and sets these variables:
|
221
|
+
# @config, @profile, @skip_config_check
|
222
|
+
verify_cucumber_config_and_profile!
|
263
223
|
|
264
224
|
if Environment.debug?
|
265
225
|
puts "Host = #{self.host}"
|
@@ -273,11 +233,10 @@ module XamarinTestCloud
|
|
273
233
|
puts "Config = #{self.config}"
|
274
234
|
puts "Profile = #{self.profile}"
|
275
235
|
puts "dSym = #{self.dsym}"
|
276
|
-
puts "Include = #{
|
236
|
+
puts "Include = #{included_files_map}"
|
277
237
|
end
|
278
238
|
|
279
|
-
|
280
|
-
#Argument parsing done
|
239
|
+
# Argument parsing done
|
281
240
|
|
282
241
|
test_jon_data = submit_test_job(device_selection_data)
|
283
242
|
if self.dry_run
|
@@ -332,11 +291,197 @@ module XamarinTestCloud
|
|
332
291
|
end
|
333
292
|
end
|
334
293
|
|
294
|
+
desc "prepare <APP>",
|
295
|
+
"Prepares workspace and creates a manifest of files to be uploaded for test"
|
296
|
+
method_option :config,
|
297
|
+
:desc => "Cucumber configuration file (cucumber.yml)",
|
298
|
+
:aliases => "-c",
|
299
|
+
:type => :string
|
300
|
+
|
301
|
+
method_option :profile,
|
302
|
+
:desc => "Profile to run (profile from Cucumber configuration file)",
|
303
|
+
:aliases => "-p",
|
304
|
+
:type => :string
|
305
|
+
|
306
|
+
method_option :workspace,
|
307
|
+
:desc => "Alternate path to your Calabash workspace",
|
308
|
+
:aliases => "-w",
|
309
|
+
:type => :string
|
310
|
+
|
311
|
+
method_option "test-parameters",
|
312
|
+
:desc => "Example: -params username:nat@xamarin.com password:xamarin)",
|
313
|
+
:aliases => "-params",
|
314
|
+
:type => :hash,
|
315
|
+
:default => {}
|
316
|
+
|
317
|
+
method_option "skip-config-check",
|
318
|
+
:desc => "Force running without Cucumber profile (cucumber.yml)",
|
319
|
+
:type => :boolean,
|
320
|
+
:default => false
|
321
|
+
|
322
|
+
method_option "artifacts-dir",
|
323
|
+
:desc => "Directory where the test files should be staged.",
|
324
|
+
:type => :string,
|
325
|
+
:required => true
|
326
|
+
|
327
|
+
# A list of items which are files or folders in the workspace.
|
328
|
+
# Must be a path relative to workspace and
|
329
|
+
# must not refer outside (e.g. no usage of ../ etc). Example --include .xtc foo
|
330
|
+
method_option 'include',
|
331
|
+
:desc => 'Include folders or files in workspace in addition the features folder.',
|
332
|
+
:aliases => '-i',
|
333
|
+
:type => :array,
|
334
|
+
:default => []
|
335
|
+
|
336
|
+
def prepare(app)
|
337
|
+
@include_files_input = options[:include]
|
338
|
+
artifact_directory = File.expand_path(options["artifacts-dir"])
|
339
|
+
|
340
|
+
# Validates and sets the @app instance variable.
|
341
|
+
verify_app!(app)
|
342
|
+
|
343
|
+
# Resolves the workspace and sets the @derived_workspace variable.
|
344
|
+
expect_features_directory_in_workspace
|
345
|
+
|
346
|
+
# Verifies the --config and --profile options and sets these variables:
|
347
|
+
# @config, @profile, @skip_config_check
|
348
|
+
verify_cucumber_config_and_profile!
|
349
|
+
|
350
|
+
require "xamarin-test-cloud/tmpdir"
|
351
|
+
bundle_package_dir = XamarinTestCloud::TmpDir.mktmpdir
|
352
|
+
|
353
|
+
stage_gemfile_and_bundle_package(bundle_package_dir)
|
354
|
+
|
355
|
+
files = collect_test_suite_files(bundle_package_dir)
|
356
|
+
|
357
|
+
FileUtils.mkdir_p(artifact_directory)
|
358
|
+
manifest_file = File.join(artifact_directory, "manifest.json")
|
359
|
+
|
360
|
+
log_header("Preparing the Artifact Directory")
|
361
|
+
test_parameters = options["test-parameters"]
|
362
|
+
|
363
|
+
manifest = generate_manifest_hash(files, test_parameters)
|
364
|
+
write_manifest_to_file(manifest, manifest_file)
|
365
|
+
|
366
|
+
puts ""
|
367
|
+
puts "Copied files to:"
|
368
|
+
puts " #{File.expand_path(artifact_directory)}"
|
369
|
+
puts ""
|
370
|
+
puts "Wrote upload manifest to:"
|
371
|
+
puts " #{File.expand_path(manifest_file)}"
|
372
|
+
|
373
|
+
files.each do |test_file|
|
374
|
+
target = File.join(artifact_directory, test_file.remote_path)
|
375
|
+
basedir = File.dirname(target)
|
376
|
+
FileUtils.mkdir_p(basedir)
|
377
|
+
FileUtils.cp(test_file.path, target)
|
378
|
+
end
|
379
|
+
|
380
|
+
end
|
335
381
|
|
336
382
|
default_task :submit
|
337
383
|
|
338
384
|
no_tasks do
|
339
385
|
|
386
|
+
# TODO: move to separate module/class
|
387
|
+
def expect_app_exists_at_path(app_path)
|
388
|
+
unless File.exist?(File.expand_path(app_path))
|
389
|
+
raise ValidationError, %Q[
|
390
|
+
App does not exist at path:
|
391
|
+
|
392
|
+
#{File.expand_path(app_path)}
|
393
|
+
|
394
|
+
]
|
395
|
+
end
|
396
|
+
end
|
397
|
+
|
398
|
+
# TODO: move to separate module/class
|
399
|
+
def expect_apk_or_ipa(app_path)
|
400
|
+
app_extension = File.extname(app_path)
|
401
|
+
unless /ipa/i.match(app_extension) || /apk/i.match(app_extension)
|
402
|
+
raise ValidationError, %Q[
|
403
|
+
App must be an .ipa or .apk file. Found:
|
404
|
+
|
405
|
+
#{app_path}
|
406
|
+
|
407
|
+
with extension: #{app_extension}
|
408
|
+
|
409
|
+
]
|
410
|
+
end
|
411
|
+
end
|
412
|
+
|
413
|
+
# TODO: move to separate module/class
|
414
|
+
def expect_no_shared_runtime(app_path)
|
415
|
+
if shared_runtime?(app_path)
|
416
|
+
raise ValidationError, %Q[
|
417
|
+
Xamarin Test Cloud does not support shared runtime apps.
|
418
|
+
|
419
|
+
To test your app it needs to be compiled for release.
|
420
|
+
|
421
|
+
You can learn how to compile your app for release here:
|
422
|
+
|
423
|
+
http://docs.xamarin.com/guides/android/deployment%2C_testing%2C_and_metrics/publishing_an_application/part_1_-_preparing_an_application_for_release
|
424
|
+
|
425
|
+
]
|
426
|
+
end
|
427
|
+
end
|
428
|
+
|
429
|
+
# TODO: move to separate module/class
|
430
|
+
def expect_ipa_linked_with_calabash(app_path)
|
431
|
+
# Cannot use #is_ios because that method depends on the @app instance
|
432
|
+
# variable which is not set when this method is called.
|
433
|
+
return nil if !app_path.end_with?(".ipa")
|
434
|
+
|
435
|
+
# otool does not exist in non-macOS environments. We have to rely on
|
436
|
+
# the Test Cloud to perform the validation.
|
437
|
+
return nil if !Environment.macos_env?
|
438
|
+
|
439
|
+
require "xamarin-test-cloud/tmpdir"
|
440
|
+
tmpdir = TmpDir.mktmpdir
|
441
|
+
|
442
|
+
unzip_file(app_path, tmpdir)
|
443
|
+
|
444
|
+
payload_dir = File.join(tmpdir, "Payload")
|
445
|
+
|
446
|
+
if !File.directory?(payload_dir)
|
447
|
+
raise ValidationError, %Q[
|
448
|
+
Did not find a Payload directory after expanding the ipa.
|
449
|
+
|
450
|
+
#{app_path}
|
451
|
+
|
452
|
+
The .ipa was not packaged correctly.
|
453
|
+
|
454
|
+
]
|
455
|
+
end
|
456
|
+
|
457
|
+
app_dir = Dir.foreach("#{tmpdir}/Payload").find { |d| /\.app$/.match(d) }
|
458
|
+
res = check_for_calabash_server(tmpdir, app_dir)
|
459
|
+
|
460
|
+
if /CalabashServer/.match(res)
|
461
|
+
puts "ipa: #{app_path} *contains* calabash.framework"
|
462
|
+
else
|
463
|
+
raise ValidationError, %Q[
|
464
|
+
|
465
|
+
The ipa does not contain an app that is linked with Calabash.
|
466
|
+
|
467
|
+
Your app must link the calabash.framework at compile time or it must dynamically
|
468
|
+
load (in code) a Calabash dylib.
|
469
|
+
|
470
|
+
https://github.com/calabash/calabash-ios/wiki/Tutorial%3A-How-to-add-Calabash-to-Xcode
|
471
|
+
]
|
472
|
+
end
|
473
|
+
end
|
474
|
+
|
475
|
+
def verify_app!(app_path)
|
476
|
+
expanded = File.expand_path(app_path)
|
477
|
+
expect_app_exists_at_path(expanded)
|
478
|
+
expect_apk_or_ipa(expanded)
|
479
|
+
expect_no_shared_runtime(expanded)
|
480
|
+
expect_ipa_linked_with_calabash(expanded)
|
481
|
+
|
482
|
+
@app = expanded
|
483
|
+
end
|
484
|
+
|
340
485
|
def dsym
|
341
486
|
if @dsym.nil?
|
342
487
|
value = options["dsym-file"]
|
@@ -437,10 +582,6 @@ module XamarinTestCloud
|
|
437
582
|
log "Packaging gems in: #{tmpdir}"
|
438
583
|
end
|
439
584
|
|
440
|
-
# TODO Move to common expect_valid_application
|
441
|
-
# TODO Rename: does not need to extract a test server
|
442
|
-
verify_app_and_extract_test_server
|
443
|
-
|
444
585
|
stage_gemfile_and_bundle_package(tmpdir)
|
445
586
|
|
446
587
|
log_header('Verifying dependencies')
|
@@ -469,6 +610,7 @@ module XamarinTestCloud
|
|
469
610
|
log_header('Uploading negotiated files')
|
470
611
|
|
471
612
|
upload_data = {
|
613
|
+
'calabash' => true,
|
472
614
|
'files' => digests_and_files,
|
473
615
|
'paths' => remote_paths,
|
474
616
|
'user' => self.user,
|
@@ -794,10 +936,16 @@ and try submitting again.
|
|
794
936
|
files
|
795
937
|
end
|
796
938
|
|
939
|
+
def collect_file?(path)
|
940
|
+
File.file?(path) &&
|
941
|
+
File.basename(path) != ".DS_Store" &&
|
942
|
+
!path[/__MACOSX/]
|
943
|
+
end
|
944
|
+
|
797
945
|
# Returns file paths as Strings
|
798
946
|
def collect_files_with_glob(glob)
|
799
|
-
Dir.glob(glob).select do |path|
|
800
|
-
|
947
|
+
Dir.glob(glob, File::FNM_DOTMATCH).select do |path|
|
948
|
+
collect_file?(path)
|
801
949
|
end
|
802
950
|
end
|
803
951
|
|
@@ -828,10 +976,26 @@ and try submitting again.
|
|
828
976
|
|
829
977
|
def collect_included_files(tmpdir)
|
830
978
|
collected_files = []
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
979
|
+
included_files_map.each_pair do |source, target|
|
980
|
+
# Use File.expand_path to handle the Windows case where the user passes:
|
981
|
+
# --workspace "test-cloud\ios"
|
982
|
+
# --include ".xtc\sample.txt"
|
983
|
+
#
|
984
|
+
# A File.join will result in:
|
985
|
+
# /path/to/test-cloud/ios/.xtc\sample.txt
|
986
|
+
# because the join naively appends the ".xtc\sample.txt"
|
987
|
+
# Use File.expand_path to handle the /
|
988
|
+
tmp_target = File.expand_path(File.join(tmpdir, target))
|
989
|
+
|
990
|
+
FileUtils.mkdir_p(File.dirname(tmp_target))
|
991
|
+
FileUtils.cp_r(source, tmp_target)
|
992
|
+
|
993
|
+
if File.directory?(tmp_target)
|
994
|
+
collect_files_with_glob(File.join(tmpdir,target,"**","*")).map do |file|
|
995
|
+
collected_files << TestFile.new(file, tmpdir)
|
996
|
+
end
|
997
|
+
else
|
998
|
+
collected_files << TestFile.new(tmp_target, tmpdir)
|
835
999
|
end
|
836
1000
|
end
|
837
1001
|
collected_files
|
@@ -893,30 +1057,6 @@ and try submitting again.
|
|
893
1057
|
end
|
894
1058
|
end
|
895
1059
|
|
896
|
-
# TODO: move to separate module/class
|
897
|
-
# TODO: fix the detection algorithm - it is broken
|
898
|
-
# TODO: remove the Frank check
|
899
|
-
# https://github.com/xamarinhq/test-cloud-command-line/issues/19
|
900
|
-
def validate_ipa(ipa)
|
901
|
-
result = false
|
902
|
-
dir = Dir.mktmpdir #do |dir|
|
903
|
-
|
904
|
-
unzip_file(ipa, dir)
|
905
|
-
unless File.directory?("#{dir}/Payload") #macos only
|
906
|
-
raise ValidationError, "Unzipping #{ipa} to #{dir} failed: Did not find a Payload directory (invalid .ipa)."
|
907
|
-
end
|
908
|
-
app_dir = Dir.foreach("#{dir}/Payload").find { |d| /\.app$/.match(d) }
|
909
|
-
res = check_for_calabash_server(dir, app_dir)
|
910
|
-
|
911
|
-
if /CalabashServer/.match(res)
|
912
|
-
puts "ipa: #{ipa} *contains* calabash.framework"
|
913
|
-
result = :calabash
|
914
|
-
end
|
915
|
-
|
916
|
-
result
|
917
|
-
end
|
918
|
-
|
919
|
-
|
920
1060
|
def log(message)
|
921
1061
|
if message.is_a? Array
|
922
1062
|
message.each { |m| log(m) }
|
@@ -934,27 +1074,6 @@ and try submitting again.
|
|
934
1074
|
end
|
935
1075
|
end
|
936
1076
|
|
937
|
-
# TODO: rename: does not need to extract or return the server
|
938
|
-
def verify_app_and_extract_test_server
|
939
|
-
server = nil
|
940
|
-
|
941
|
-
unless File.exist?(app)
|
942
|
-
raise ValidationError, "No such file: #{app}"
|
943
|
-
end
|
944
|
-
unless (/\.(apk|ipa)$/ =~ app)
|
945
|
-
raise ValidationError, '<APP> should be either an ipa or apk file.'
|
946
|
-
end
|
947
|
-
if is_ios? && Environment.macos_env?
|
948
|
-
log_header('Checking ipa for linking with Calabash')
|
949
|
-
server = validate_ipa(app)
|
950
|
-
abort_unless(server) do
|
951
|
-
puts 'The .ipa file does not seem to be linked with Calabash.'
|
952
|
-
puts 'Verify that your app is linked correctly.'
|
953
|
-
end
|
954
|
-
end
|
955
|
-
server
|
956
|
-
end
|
957
|
-
|
958
1077
|
# TODO Rename or remove; abort is not the right verb - use exit
|
959
1078
|
# +1 for remove
|
960
1079
|
def abort(&block)
|
@@ -982,12 +1101,15 @@ and try submitting again.
|
|
982
1101
|
end
|
983
1102
|
|
984
1103
|
# TODO Move to a module
|
1104
|
+
# TODO Needs tests
|
985
1105
|
def shared_runtime?(app_path)
|
986
1106
|
f = files(app_path)
|
987
1107
|
f.any? do |file|
|
988
1108
|
filename = file[:filename]
|
989
1109
|
if filename.end_with?("libmonodroid.so")
|
990
|
-
file[:size] < 120 * 1024 && f.none?
|
1110
|
+
file[:size] < 120 * 1024 && f.none? do |x|
|
1111
|
+
x[:filename] == filename.sub("libmonodroid.so", "libmonosgen-2.0.so")
|
1112
|
+
end
|
991
1113
|
end
|
992
1114
|
end
|
993
1115
|
end
|
@@ -1001,52 +1123,186 @@ and try submitting again.
|
|
1001
1123
|
end
|
1002
1124
|
end
|
1003
1125
|
|
1004
|
-
# TODO: extract to a module
|
1005
|
-
# TODO: replace unless/else branches
|
1006
|
-
def parse_and_set_config_and_profile
|
1007
|
-
config_path = options[:config]
|
1008
1126
|
|
1009
|
-
|
1010
|
-
|
1011
|
-
|
1127
|
+
# TODO: extract this method to a module
|
1128
|
+
# TODO: .config/cucumber.yml is a valid cucumber profile
|
1129
|
+
# TODO: cucumber.yaml and cucumber.yml are both valid names
|
1130
|
+
#
|
1131
|
+
# Raises if there is config/cucumber.yml file but no --config option is
|
1132
|
+
# passed.
|
1133
|
+
#
|
1134
|
+
# Check can be skipped by passing --skip-config-check.
|
1135
|
+
#
|
1136
|
+
# This method, sadly, has a preconditions: @config and @skip_config_check
|
1137
|
+
# must be set correctly before calling this method.
|
1138
|
+
def expect_config_option_if_cucumber_config_file_exists
|
1139
|
+
return nil if config
|
1140
|
+
return nil if skip_config_check
|
1141
|
+
|
1142
|
+
default_config = File.join(derived_workspace, "config", "cucumber.yml")
|
1143
|
+
if File.exist?(default_config)
|
1144
|
+
raise ValidationError, %Q[
|
1145
|
+
Detected a cucumber configuration file, but no --config option was specified.
|
1146
|
+
|
1147
|
+
#{default_config}
|
1148
|
+
|
1149
|
+
You can pass the cucumber configuration file to the uploader using:
|
1150
|
+
|
1151
|
+
--config #{default_config}
|
1152
|
+
|
1153
|
+
and specify a profile using the --profile option.
|
1154
|
+
|
1155
|
+
You can skip this check using the --skip-config-check option, but this is not
|
1156
|
+
recommended.
|
1157
|
+
|
1158
|
+
]
|
1012
1159
|
end
|
1160
|
+
end
|
1013
1161
|
|
1014
|
-
|
1015
|
-
|
1016
|
-
|
1017
|
-
raise ValidationError, "Config file does not exist #{config_path}"
|
1018
|
-
end
|
1162
|
+
# TODO: extract this method to a module
|
1163
|
+
def resolve_cucumber_config_path(path)
|
1164
|
+
return nil if !path
|
1019
1165
|
|
1020
|
-
|
1021
|
-
|
1022
|
-
rescue Exception => e
|
1023
|
-
puts "Unable to parse #{config_path} as yml. Is this your Cucumber.yml file?"
|
1024
|
-
raise ValidationError, e
|
1025
|
-
end
|
1166
|
+
expanded = File.expand_path(path)
|
1167
|
+
return expanded if expanded == path
|
1026
1168
|
|
1027
|
-
|
1028
|
-
|
1029
|
-
|
1169
|
+
workspace = derived_workspace
|
1170
|
+
# Use File.expand_path to handle the Windows case where the user passes:
|
1171
|
+
# --workspace "test-cloud\ios"
|
1172
|
+
# --config "config\cucumber.yml"
|
1173
|
+
#
|
1174
|
+
# A File.join will result in:
|
1175
|
+
# /path/to/test-cloud/ios/config\cucumber.yml
|
1176
|
+
# because the join naively appends the "config\cucumber.yml".
|
1177
|
+
File.expand_path(File.join(workspace, path))
|
1178
|
+
end
|
1179
|
+
|
1180
|
+
# TODO: extract this method to a module
|
1181
|
+
#
|
1182
|
+
# Sets @config to a TestFile instance
|
1183
|
+
# Sets @skip_config_check to option passed on the CLI
|
1184
|
+
# Sets @profile to option passed on the CLI
|
1185
|
+
#
|
1186
|
+
# Raises if there is problem parsing or interpreting the profile with
|
1187
|
+
# respect to the configuration file.
|
1188
|
+
#
|
1189
|
+
# Raises if there is config/cucumber.yml file but no --config option is
|
1190
|
+
# passed.
|
1191
|
+
def verify_cucumber_config_and_profile!
|
1192
|
+
config = options[:config]
|
1193
|
+
profile = options[:profile]
|
1194
|
+
|
1195
|
+
if profile && !config
|
1196
|
+
raise ValidationError, %Q[
|
1197
|
+
|
1198
|
+
The --profile option was set without a --config option.
|
1199
|
+
|
1200
|
+
If a --profile is specified, then a cucumber configuration file is required.
|
1201
|
+
|
1202
|
+
https://github.com/cucumber/cucumber/wiki/cucumber.yml
|
1203
|
+
|
1204
|
+
]
|
1205
|
+
end
|
1206
|
+
|
1207
|
+
# Defaults to false
|
1208
|
+
@skip_config_check = options["skip-config-check"]
|
1209
|
+
|
1210
|
+
if !config
|
1211
|
+
@config = nil
|
1212
|
+
@profile = nil
|
1213
|
+
expect_config_option_if_cucumber_config_file_exists
|
1214
|
+
else
|
1215
|
+
config_path = resolve_cucumber_config_path(config)
|
1216
|
+
|
1217
|
+
if !File.exist?(config_path)
|
1218
|
+
raise ValidationError, %Q[
|
1219
|
+
File specified by --config option does not exist:
|
1220
|
+
|
1221
|
+
#{config_path}
|
1222
|
+
|
1223
|
+
]
|
1030
1224
|
end
|
1031
1225
|
|
1032
|
-
|
1033
|
-
|
1034
|
-
|
1226
|
+
if profile
|
1227
|
+
expect_profile_exists_in_cucumber_config(config_path, profile)
|
1228
|
+
@profile = profile
|
1035
1229
|
else
|
1036
|
-
|
1037
|
-
raise ValidationError, "Config file provided did not contain profile #{profile}."
|
1038
|
-
else
|
1039
|
-
puts "Using profile #{profile}..."
|
1040
|
-
self.profile = profile
|
1041
|
-
end
|
1042
|
-
end
|
1043
|
-
else
|
1044
|
-
if options[:profile]
|
1045
|
-
raise ValidationError, 'Profile selected but no config file provided.'
|
1230
|
+
@profile = nil
|
1046
1231
|
end
|
1232
|
+
@config = TestFile.cucumber_config(config_path)
|
1233
|
+
end
|
1234
|
+
end
|
1235
|
+
|
1236
|
+
# TODO: extract this method to a module
|
1237
|
+
def expect_profile_exists_in_cucumber_config(config_path, profile)
|
1238
|
+
config_yml = {}
|
1239
|
+
|
1240
|
+
begin
|
1241
|
+
config_yml = YAML.load(ERB.new(File.read(config_path)).result)
|
1242
|
+
rescue => e
|
1243
|
+
raise ValidationError, %Q[
|
1244
|
+
|
1245
|
+
#{e.message}
|
1246
|
+
|
1247
|
+
Unable to parse --config option as yml:
|
1248
|
+
|
1249
|
+
#{config_path}
|
1250
|
+
|
1251
|
+
]
|
1252
|
+
end
|
1253
|
+
|
1254
|
+
if Environment.debug?
|
1255
|
+
puts "Parsed Cucumber config as:"
|
1256
|
+
puts config_yml.inspect
|
1257
|
+
end
|
1258
|
+
|
1259
|
+
if !config_yml[profile]
|
1260
|
+
raise ValidationError, %Q[
|
1261
|
+
The cucumber configuration file specified by the --config option does not
|
1262
|
+
contain the profile specified by --profile option:
|
1263
|
+
|
1264
|
+
:config => #{config_path}
|
1265
|
+
:profile => #{profile}
|
1266
|
+
|
1267
|
+
]
|
1047
1268
|
end
|
1269
|
+
end
|
1270
|
+
|
1271
|
+
# TODO Make --include'd paths generic when possible (see code comment below)
|
1272
|
+
def generate_manifest_hash(test_files, test_parameters)
|
1273
|
+
files = test_files.map do |test_file|
|
1274
|
+
path = test_file.remote_path
|
1275
|
+
if path[/features/]
|
1276
|
+
"features"
|
1277
|
+
elsif path[/vendor/]
|
1278
|
+
"vendor"
|
1279
|
+
else
|
1280
|
+
# --include'd files. It is not possible to filter these to a single
|
1281
|
+
# directory without inspecting the --include option itself.
|
1282
|
+
# Consider the example where --include is `config/xtc-users.json`.
|
1283
|
+
# We cannot filter to `config` because the config directory might
|
1284
|
+
# include a cucumber.yml or some other file that the user does _not_
|
1285
|
+
# want to upload.
|
1286
|
+
path
|
1287
|
+
end
|
1288
|
+
end.uniq!
|
1289
|
+
|
1290
|
+
{
|
1291
|
+
schemaVersion: MANIFEST_SCHEMA_VERSION,
|
1292
|
+
files: files,
|
1293
|
+
testFramework: {
|
1294
|
+
name: "calabash",
|
1295
|
+
data: test_parameters
|
1296
|
+
}
|
1297
|
+
}
|
1298
|
+
end
|
1299
|
+
|
1300
|
+
def write_manifest_to_file(manifest, manifest_file)
|
1301
|
+
json = JSON.pretty_generate(manifest)
|
1048
1302
|
|
1049
|
-
|
1303
|
+
File.open(manifest_file, "w:UTF-8") do |file|
|
1304
|
+
file.write(json)
|
1305
|
+
end
|
1050
1306
|
end
|
1051
1307
|
|
1052
1308
|
def included_files_map
|
@@ -1054,24 +1310,24 @@ and try submitting again.
|
|
1054
1310
|
end
|
1055
1311
|
|
1056
1312
|
def parse_included_folders
|
1057
|
-
return {} unless self.include_files_input
|
1058
1313
|
basedir = derived_workspace
|
1059
|
-
|
1060
|
-
|
1061
|
-
|
1062
|
-
|
1063
|
-
|
1314
|
+
source_target_map = {}
|
1315
|
+
include_files_input.each do |file|
|
1316
|
+
source = File.join(basedir, file)
|
1317
|
+
expect_valid_included_file(source, file)
|
1318
|
+
source_target_map[source] = file
|
1064
1319
|
end
|
1065
|
-
|
1320
|
+
source_target_map
|
1066
1321
|
end
|
1067
1322
|
|
1068
|
-
def
|
1323
|
+
def expect_valid_included_file(src, tgt)
|
1069
1324
|
unless File.exists?(src)
|
1070
1325
|
raise ValidationError, %Q[
|
1071
1326
|
Requested include folder: #{src} does not exist.
|
1072
1327
|
|
1073
1328
|
Specified include options: #{self.include_files_input}
|
1074
|
-
|
1329
|
+
|
1330
|
+
]
|
1075
1331
|
end
|
1076
1332
|
tgt_pathname = Pathname.new(tgt)
|
1077
1333
|
if tgt_pathname.absolute?
|
@@ -1081,7 +1337,8 @@ Only relative target path names are allowed.
|
|
1081
1337
|
Requested target folder: #{tgt} is an absolute file name.
|
1082
1338
|
|
1083
1339
|
Specified include options: #{self.include_files_input}"
|
1084
|
-
|
1340
|
+
|
1341
|
+
]
|
1085
1342
|
end
|
1086
1343
|
if /\.\./.match(tgt)
|
1087
1344
|
raise ValidationError, %Q[
|
@@ -1090,10 +1347,12 @@ Only expanded relative target paths are allowed.
|
|
1090
1347
|
Requested target folder includes '..'.
|
1091
1348
|
|
1092
1349
|
Specified include options: #{self.include_files_input}"
|
1093
|
-
|
1350
|
+
|
1351
|
+
]
|
1094
1352
|
end
|
1095
1353
|
end
|
1096
1354
|
|
1355
|
+
# TODO rename to verify_workspace!
|
1097
1356
|
def expect_features_directory_in_workspace
|
1098
1357
|
path = derived_workspace
|
1099
1358
|
features = File.join(path, "features")
|
@@ -1114,6 +1373,7 @@ You have two options:
|
|
1114
1373
|
See also
|
1115
1374
|
|
1116
1375
|
$ test-cloud help submit
|
1376
|
+
|
1117
1377
|
])
|
1118
1378
|
end
|
1119
1379
|
|
@@ -1134,7 +1394,7 @@ $ test-cloud help submit
|
|
1134
1394
|
end
|
1135
1395
|
|
1136
1396
|
def detect_workspace(options)
|
1137
|
-
options[
|
1397
|
+
options[:workspace] || File.expand_path(".")
|
1138
1398
|
end
|
1139
1399
|
|
1140
1400
|
def expect_workspace_directory(path)
|
@@ -1233,6 +1493,8 @@ Check your local Gemfile and and the remote Gemfile at:
|
|
1233
1493
|
$?.exitstatus == 0
|
1234
1494
|
end
|
1235
1495
|
|
1496
|
+
# TODO: Server detection algorithm might be broken.
|
1497
|
+
# https://github.com/xamarinhq/test-cloud-command-line/issues/19
|
1236
1498
|
def check_for_calabash_server(dir, app_dir)
|
1237
1499
|
if system("xcrun --find otool-classic &> /dev/null")
|
1238
1500
|
otool_cmd = "xcrun otool-classic"
|
@@ -1,13 +1,11 @@
|
|
1
1
|
module XamarinTestCloud
|
2
2
|
|
3
3
|
# TODO validate dsym against the current application.
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
|
9
|
-
# 6. confirm that they match.
|
10
|
-
class Dsym
|
4
|
+
# https://github.com/xamarinhq/test-cloud-command-line/issues/91
|
5
|
+
#
|
6
|
+
# TODO dsym-file can be relative to workspace
|
7
|
+
# https://github.com/xamarinhq/test-cloud-command-line/issues/92
|
8
|
+
class Dsym
|
11
9
|
|
12
10
|
attr_reader :path
|
13
11
|
|
@@ -76,13 +76,13 @@ expanded: #{expanded}
|
|
76
76
|
end
|
77
77
|
|
78
78
|
def remote_path
|
79
|
-
@remote_path ||=
|
79
|
+
@remote_path ||= begin
|
80
80
|
require "pathname"
|
81
81
|
absolute = Pathname.new(path)
|
82
82
|
root = Pathname.new(basedir)
|
83
83
|
relative = absolute.relative_path_from(root)
|
84
84
|
relative.to_s
|
85
|
-
end
|
85
|
+
end
|
86
86
|
end
|
87
87
|
|
88
88
|
private
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: xamarin-test-cloud
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0
|
4
|
+
version: 2.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Karl Krukow
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2016-
|
12
|
+
date: 2016-11-11 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: thor
|
@@ -322,7 +322,6 @@ files:
|
|
322
322
|
- bin/test-cloud
|
323
323
|
- lib/xamarin-test-cloud/calabash_version_detector.rb
|
324
324
|
- lib/xamarin-test-cloud/cli.rb
|
325
|
-
- lib/xamarin-test-cloud/cli.rb alias
|
326
325
|
- lib/xamarin-test-cloud/dsym.rb
|
327
326
|
- lib/xamarin-test-cloud/environment.rb
|
328
327
|
- lib/xamarin-test-cloud/http/payload.rb
|
@@ -351,7 +350,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
351
350
|
version: '0'
|
352
351
|
requirements: []
|
353
352
|
rubyforge_project:
|
354
|
-
rubygems_version: 2.
|
353
|
+
rubygems_version: 2.5.1
|
355
354
|
signing_key:
|
356
355
|
specification_version: 4
|
357
356
|
summary: Command-line interface to Xamarin Test Cloud
|
Binary file
|