xamarin-test-cloud 2.0.3 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9f60e55bbba9205215510389613c5c60672d9983
4
- data.tar.gz: 237e950267809c66173d8d388593a5b598865c63
3
+ metadata.gz: 7179d836cc88e3266185b3d9cd68afa6b8272681
4
+ data.tar.gz: 50bb75d45e930271faaa5a28ae6f718895790a6d
5
5
  SHA512:
6
- metadata.gz: c5696ca55bd8e3c6fde7043b000ffc8dd97d4bab99aa7f5a5e2aa4a7f8bdb52bbd7a64e31a7ea0443aaebb59bb1f8a6938e2a2901fc6c0ce680a647ee4b67390
7
- data.tar.gz: a93037446d255ead1f82a3397fcf6cc879091d28cec34378ef03c3e95776478218e3b5053a6719d331142fd528c843e3ef2245e1e661069c6e6a7b726e136aa2
6
+ metadata.gz: 2adca474985c4505d9dbe98b5ae5c0e0da1365d921578328f2a3a21a7f13d84fdc8ef07e405cb638b8042bb3543602b49b642bac55117bc37fb9c796d5ed9573
7
+ data.tar.gz: 2ed5fcd3105e9622d69c1e88318b4c64bf4fced3bc516877c7cc6804385739f46337fe7acafba81e2f80a0fb2681b8531b09f7d35d6eda65c1f9f739bde642f9
@@ -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
- attr_writer :included_files_map
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
- app_path = File.expand_path(app)
192
- unless File.exist?(app_path)
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
- # TODO: extract this method and the configuration branch below to a module
241
- parse_and_set_config_and_profile
242
- unless self.skip_config_check
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 = #{self.included_files_map}" if self.included_files_map
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
- File.file?(path)
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
- self.included_files_map.each_pair do |src, dest|
832
- FileUtils.cp_r(src, File.join(tmpdir,dest))
833
- collect_files_with_glob(File.join(tmpdir,dest,"**","*")).map do |file|
834
- collected_files << TestFile.new(file, tmpdir)
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? { |x| x[:filename] == filename.sub("libmonodroid.so", "libmonosgen-2.0.so") }
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
- if !config_path
1010
- self.config = false
1011
- return
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
- if config_path
1015
- config_path = File.expand_path(config_path)
1016
- unless File.exist?(config_path)
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
- begin
1021
- config_yml = YAML.load(ERB.new(File.read(config_path)).result)
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
- if Environment.debug?
1028
- puts 'Parsed Cucumber config as:'
1029
- puts config_yml.inspect
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
- profile = options[:profile]
1033
- unless profile
1034
- raise ValidationError, 'Config file provided but no profile selected.'
1226
+ if profile
1227
+ expect_profile_exists_in_cucumber_config(config_path, profile)
1228
+ @profile = profile
1035
1229
  else
1036
- unless config_yml[profile]
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
- self.config = TestFile.cucumber_config(config_path)
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
- parsed_included_folders = {}
1060
- self.include_files_input.each do |folder|
1061
- src = File.join(basedir, folder)
1062
- validate_include_folder!(src, folder)
1063
- parsed_included_folders[src]=folder
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
- parsed_included_folders
1320
+ source_target_map
1066
1321
  end
1067
1322
 
1068
- def validate_include_folder!(src, tgt)
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["workspace"] || File.expand_path(".")
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
- # 1. put the dSYM.zip and .ipa in a directory
5
- # 2. $ unzip <the>.ipa
6
- # 3. $ unzip <the>.dSYM.zip
7
- # 4. $ xcrun dwarfdump --uuid Payload/My.app/My
8
- # 5. $ xcrun dwarfdump --uuid My.app.dSYM
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 ||= lambda do
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.call
85
+ end
86
86
  end
87
87
 
88
88
  private
@@ -1,3 +1,3 @@
1
1
  module XamarinTestCloud
2
- VERSION = "2.0.3"
2
+ VERSION = "2.1.0"
3
3
  end
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.3
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-10-28 00:00:00.000000000 Z
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.6.4
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