xamarin-test-cloud 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: eb0063f60324d962136569cbb02a120355c4a9b3
4
+ data.tar.gz: c3f3a4eafe87aa4ee3a2e750cdd92144ac50b86c
5
+ SHA512:
6
+ metadata.gz: 94064d3747ab099c6f042106f6bb93ad69d4bb1bba70cdef9223be64539d48334bf1a66aac47d4457963823d09990e36e6b04df7d38c1ea4c0a4d1652d20fa7f
7
+ data.tar.gz: f0c598d9d96dc52303cb2b3e52425aa3bc1cf3b794fd7f3abbf5ee9a89c022ee143da830abef93711eb3b85b27bacad34976419a9221de3bda3fa17310712e7a
data/CHANGES.txt ADDED
@@ -0,0 +1,2 @@
1
+ 0.9.0 Initial beta
2
+ Supports upload of Calabash test suites to https://testcloud.xamarin.com.
data/README.md ADDED
File without changes
data/bin/test-cloud ADDED
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+ require 'xamarin-test-cloud/cli'
3
+
4
+ begin
5
+ ENV['THOR_DEBUG'] = '1'
6
+ XamarinTestCloud::CLI.start
7
+ exit(true)
8
+ rescue XamarinTestCloud::ValidationError, Thor::RequiredArgumentMissingError, Thor::UndefinedCommandError => e
9
+ puts e.message
10
+ exit 64
11
+ rescue Thor::Error => e
12
+ puts e.message
13
+ if ENV['DEBUG'] == '1'
14
+ puts e.backtrace.join("\n") if e.backtrace
15
+ p e.class
16
+ end
17
+ exit 70
18
+ end
@@ -0,0 +1,3 @@
1
+ source 'http://rubygems.org'
2
+ gem 'calabash-android'
3
+
data/lib/GemfileIOS ADDED
@@ -0,0 +1,3 @@
1
+ source 'http://rubygems.org'
2
+ gem 'calabash-cucumber'
3
+
@@ -0,0 +1,618 @@
1
+ require 'thor'
2
+ require 'yaml'
3
+ require 'rubygems'
4
+ require 'zip/zip'
5
+ require 'digest'
6
+ require 'rest_client'
7
+ require 'json'
8
+ require 'rbconfig'
9
+ require 'tmpdir'
10
+ require 'fileutils'
11
+
12
+ module XamarinTestCloud
13
+
14
+ class ValidationError < Thor::InvocationError
15
+ end
16
+
17
+ class CLI < Thor
18
+ include Thor::Actions
19
+
20
+ attr_accessor :host, :app, :api_key, :appname, :workspace, :config, :profile, :features_zip, :skip_check, :dry_run, :device_selection
21
+ attr_accessor :pretty
22
+ attr_accessor :endpoint_path
23
+
24
+ FILE_UPLOAD_ENDPOINT = 'upload2'
25
+ FORM_URL_ENCODED_ENDPOINT = 'upload'
26
+
27
+
28
+ def self.source_root
29
+ File.join(File.dirname(__FILE__), '..')
30
+ end
31
+
32
+ desc 'version', 'Prints version of the xamarin-test-cloud gem'
33
+
34
+ def version
35
+ puts XamarinTestCloud::VERSION
36
+ end
37
+
38
+ desc 'submit <APP> <API_KEY>', 'Submits your app and test suite to Xamarin Test Cloud'
39
+
40
+ method_option :host,
41
+ :desc => 'Test Cloud host to submit to',
42
+ :aliases => '-h', :type => :string,
43
+ :default => (ENV['XTC_ENDPOINT'] || 'https://testcloud.xamarin.com/ci')
44
+
45
+ method_option 'app-name',
46
+ :desc => 'App name to create or add test to',
47
+ :aliases => '-a',
48
+ :required => true,
49
+ :type => :string
50
+
51
+ method_option 'device-selection',
52
+ :desc => 'Device selection',
53
+ :aliases => '-d',
54
+ :required => true,
55
+ :type => :string
56
+
57
+ method_option :workspace,
58
+ :desc => 'Workspace containing Gemfile and features',
59
+ :aliases => '-w',
60
+ :type => :string,
61
+ :default => File.expand_path('.')
62
+
63
+ method_option :features,
64
+ :desc => 'Zip file with features, step definitions, etc...',
65
+ :aliases => '-f',
66
+ :type => :string
67
+
68
+ method_option :config,
69
+ :desc => 'Cucumber configuration file (cucumber.yml)',
70
+ :aliases => '-c',
71
+ :type => :string
72
+
73
+ method_option :profile,
74
+ :desc => 'Profile to run (profile from cucumber.yml)',
75
+ :aliases => '-p',
76
+ :type => :string
77
+
78
+ method_option :pretty,
79
+ :desc => 'Pretty print output.',
80
+ :type => :boolean,
81
+ :default => false
82
+
83
+ method_option 'skip-check',
84
+ :desc => 'Skip checking for ipa linked with Calabash (iOS only)',
85
+ :type => :boolean
86
+
87
+ method_option 'dry-run',
88
+ :desc => "Sanity check only, don't upload",
89
+ :aliases => '-d',
90
+ :type => :boolean,
91
+ :default => false #do upload by default
92
+
93
+ def submit(app, api_key)
94
+
95
+ self.host = options[:host]
96
+
97
+ self.pretty = options[:pretty]
98
+
99
+ app_path = File.expand_path(app)
100
+ unless File.exist?(app_path)
101
+ raise ValidationError, "App is not a file: #{app_path}"
102
+ end
103
+
104
+ self.app = app_path
105
+
106
+ self.dry_run = options['dry-run']
107
+
108
+ self.api_key = api_key
109
+
110
+ self.appname = options['app-name']
111
+
112
+ self.device_selection = options['device-selection']
113
+ unless File.exist?(device_selection)
114
+ raise ValidationError, "Device selection is not a file: #{device_selection}"
115
+ end
116
+
117
+ self.skip_check = options['skip-check']
118
+
119
+ parse_and_set_config_and_profile
120
+
121
+ workspace_path = options[:workspace] || File.expand_path('.')
122
+
123
+ unless File.directory?(workspace_path)
124
+ raise ValidationError, "Provided workspace: #{workspace_path} is not a directory."
125
+ end
126
+ self.workspace = File.join(File.expand_path(workspace_path), File::Separator)
127
+
128
+ features_path = options[:features]
129
+
130
+ unless features_path.nil?
131
+ if File.exist?(features_path)
132
+ self.features_zip = File.expand_path(features_path)
133
+ else
134
+ raise ValidationError, "Provided features file does not exist #{features_path}"
135
+ end
136
+ end
137
+
138
+ if ENV['DEBUG']
139
+ puts "Host = #{self.host}"
140
+ puts "App = #{self.app}"
141
+ puts "App Name = #{self.app}"
142
+ puts "API Key = #{self.api_key}"
143
+ puts "Device Selection = #{self.device_selection}"
144
+ puts "Workspace = #{self.workspace}"
145
+ puts "Features Zip = #{self.features_zip}"
146
+ puts "Config = #{self.config}"
147
+ puts "Profile = #{self.profile}"
148
+ puts "Skip Check = #{self.skip_check}"
149
+ end
150
+
151
+ #Argument parsing done
152
+ json = submit_test_job[:body]
153
+ log_header("Test enqueued")
154
+ puts "User: #{json["user_email"]}"
155
+
156
+ puts "Devices:"
157
+ json["devices"].each do |device|
158
+ puts device
159
+ end
160
+ puts ""
161
+
162
+
163
+ result = wait_for_job(json["id"])
164
+
165
+ end
166
+
167
+ default_task :submit
168
+
169
+ no_tasks do
170
+
171
+ def exit_on_failure?
172
+ true
173
+ end
174
+
175
+ def wait_for_job(id)
176
+ while(true)
177
+ status_json = JSON.parse(http_post("status", {'id' => id, 'api_key' => api_key}))
178
+
179
+ log "Status: #{status_json["status_description"]}"
180
+ if status_json["status"] == "finished"
181
+ puts "Done!"
182
+ exit 0
183
+ end
184
+
185
+ if ["cancelled", "invalid"].include?(status_json["status"])
186
+ puts "Failed!"
187
+ exit 1
188
+ end
189
+ sleep 10
190
+ end
191
+ end
192
+
193
+
194
+ def submit_test_job
195
+ start_at = Time.now
196
+
197
+ server = verify_app_and_extract_test_server
198
+
199
+
200
+ log_header('Checking for Gemfile')
201
+ gemfile_path = File.join(self.workspace, 'Gemfile')
202
+ unless File.exist?(gemfile_path)
203
+ copy_default_gemfile(gemfile_path, server)
204
+ end
205
+
206
+ log_header('Packaging')
207
+ FileUtils.cd(self.workspace) do
208
+ unless system('bundle package --all')
209
+ log_and_abort 'Bundler failed. Please check command: bundle package'
210
+ end
211
+ end
212
+
213
+
214
+ log_header('Verifying dependencies')
215
+ verify_dependencies
216
+
217
+
218
+ if dry_run
219
+ log_header('Dry run only')
220
+ log('Dry run completed OK')
221
+ return
222
+ end
223
+
224
+ app_file, files, paths = gather_files_and_paths_to_upload(all_files)
225
+
226
+
227
+ log_header('Uploading negotiated files')
228
+
229
+ upload_data = {'files' => files,
230
+ 'paths' => paths,
231
+ 'app_file' => app_file,
232
+ 'device_selection' => IO.read(device_selection),
233
+ 'app' => self.appname,
234
+ 'api_key' => api_key,
235
+ 'app_filename' => File.basename(app)}
236
+
237
+ if profile #only if config and profile
238
+ upload_data['profile'] = profile
239
+ end
240
+
241
+ if ENV['DEBUG']
242
+ puts JSON.pretty_generate(upload_data)
243
+ end
244
+
245
+
246
+ contains_file = files.find { |f| f.is_a?(File) }
247
+
248
+ contains_file = contains_file || app_file.is_a?(File)
249
+
250
+ if contains_file
251
+ self.endpoint_path = FILE_UPLOAD_ENDPOINT #nginx receives upload
252
+ else
253
+ self.endpoint_path = FORM_URL_ENCODED_ENDPOINT #ruby receives upload
254
+ end
255
+
256
+ if ENV['DEBUG']
257
+ puts "Will upload to file path: #{self.endpoint_path}"
258
+ end
259
+
260
+
261
+ response = http_post(endpoint_path, upload_data) do |response, request, result, &block|
262
+ if ENV['DEBUG']
263
+ puts "Request url: #{request.url}"
264
+ puts "Response code: #{response.code}"
265
+ end
266
+ case response.code
267
+ when 200..202
268
+ response
269
+ when 403
270
+ abort do
271
+ puts 'Invalid API key'
272
+ end
273
+ when 413
274
+ abort do
275
+ puts 'Files too large'
276
+ end
277
+ else
278
+ abort do
279
+ log 'Unexpected Error. Please contact support at testcloud@xamarin.com.'
280
+ end
281
+ end
282
+ end
283
+
284
+ return :status => response.code, :body => JSON.parse(response)
285
+ end
286
+
287
+
288
+ def copy_default_gemfile(gemfile_path, server)
289
+ log('')
290
+ log('Gemfile missing.')
291
+ log('You must provide a Gemfile in your workspace.')
292
+ log('A Gemfile must describe your dependencies and their versions')
293
+ log('See: http://gembundler.com/v1.3/gemfile.html')
294
+ log('')
295
+ log('Warning proceeding with default Gemfile.')
296
+ log('It is strongly recommended that you create a custom Gemfile.')
297
+ tgt = nil
298
+ if is_android?
299
+ log('Creating Gemfile for Android')
300
+ tgt = File.join(CLI.source_root, 'GemfileAndroid')
301
+ elsif is_ios?
302
+ log('Creating Gemfile for iOS')
303
+ gemfile = 'GemfileIOS'
304
+ if server == :frank
305
+ raise ValidationError, 'Frank not supported just yet'
306
+ end
307
+ tgt = File.join(CLI.source_root, gemfile)
308
+ else
309
+ raise ValidationError, 'Your app must be an ipa or apk file.'
310
+ end
311
+ log("Proceeding with Gemfile: #{gemfile_path}")
312
+
313
+
314
+ FileUtils.cp(File.expand_path(tgt), gemfile_path)
315
+
316
+ puts(File.read(gemfile_path))
317
+
318
+ log('')
319
+
320
+ end
321
+
322
+ def gather_files_and_paths_to_upload(collected_files)
323
+
324
+ log_header('Calculating digests')
325
+
326
+ file_paths = collected_files[:files]
327
+ feature_prefix = collected_files[:feature_prefix]
328
+ workspace_prefix = collected_files[:workspace_prefix]
329
+
330
+ hashes = file_paths.collect { |f| digest(f) }
331
+ hashes << digest(app)
332
+
333
+
334
+ log_header('Negotiating upload')
335
+
336
+ response = http_post('check_hash', {'hashes' => hashes})
337
+
338
+ cache_status = JSON.parse(response)
339
+
340
+ log_header('Gathering files based on negotiation')
341
+
342
+ files = []
343
+ paths = []
344
+ file_paths.each do |file|
345
+ if cache_status[digest(file)]
346
+ #Server already knows about this file. No need to upload it.
347
+ files << digest(file)
348
+ else
349
+ #Upload file
350
+ files << File.new(file)
351
+ end
352
+
353
+ if file.start_with?(feature_prefix)
354
+ prefix = feature_prefix
355
+ else
356
+ prefix = workspace_prefix
357
+ end
358
+ paths << file.sub(prefix, '')
359
+ end
360
+
361
+ if config
362
+ files << File.new(config)
363
+ paths << 'config/cucumber.yml'
364
+ end
365
+
366
+ app_file = cache_status[digest(app)] ? digest(app) : File.new(app)
367
+
368
+ return app_file, files, paths
369
+ end
370
+
371
+ def digest(file)
372
+ Digest::SHA256.file(file).hexdigest
373
+ end
374
+
375
+ def unzip_file (file, destination)
376
+ Zip::ZipFile.open(file) { |zip_file|
377
+ zip_file.each { |f|
378
+ f_path=File.join(destination, f.name)
379
+ FileUtils.mkdir_p(File.dirname(f_path))
380
+ zip_file.extract(f, f_path) unless File.exist?(f_path)
381
+ }
382
+ }
383
+ end
384
+
385
+
386
+ def is_android?
387
+ app.end_with? '.apk'
388
+ end
389
+
390
+ def calabash_android_version
391
+ `bundle exec calabash-android version`.strip
392
+ end
393
+
394
+ def is_ios?
395
+ app.end_with? '.ipa'
396
+ end
397
+
398
+ def test_server_path
399
+ require 'digest/md5'
400
+ digest = Digest::MD5.file(app).hexdigest
401
+ File.join('test_servers', "#{digest}_#{calabash_android_version}.apk")
402
+ end
403
+
404
+ def all_files
405
+ dir = workspace
406
+ if features_zip
407
+ dir = Dir.mktmpdir
408
+ unzip_file(features_zip, dir)
409
+ dir = File.join(dir, File::Separator)
410
+ end
411
+
412
+
413
+ files = Dir.glob(File.join("#{dir}features", '**', '*'))
414
+
415
+ if File.directory?("#{dir}playback")
416
+ files += Dir.glob(File.join("#{dir}playback", '*'))
417
+ end
418
+
419
+ if config
420
+ files << config
421
+ end
422
+
423
+
424
+ files += Dir.glob(File.join("#{workspace}vendor", 'cache', '*'))
425
+
426
+ if workspace and workspace.strip != ''
427
+ files += Dir.glob("#{workspace}Gemfile")
428
+ files += Dir.glob("#{workspace}Gemfile.lock")
429
+ end
430
+
431
+ if is_android?
432
+ files << test_server_path
433
+ end
434
+
435
+ {:feature_prefix => dir, :workspace_prefix => workspace, :files => files.find_all { |file_or_dir| File.file? file_or_dir }}
436
+ end
437
+
438
+ def http_post(address, args, &block)
439
+ exec_options = {}
440
+ if ENV['XTC_USERNAME'] && ENV['XTC_PASSWORD']
441
+ exec_options[:user] = ENV['XTC_USERNAME']
442
+ exec_options[:password] = ENV['XTC_PASSWORD']
443
+ end
444
+
445
+ if block_given?
446
+ exec_options = exec_options.merge ({:method => :post, :url => "#{host}/#{address}", :payload => args,
447
+ :timeout => 90000000,
448
+ :open_timeout => 90000000,
449
+ :headers => {:content_type => 'multipart/form-data'}})
450
+ response = RestClient::Request.execute(exec_options) do |response, request, result, &other_block|
451
+ block.call(response, request, result, &other_block)
452
+ end
453
+ else
454
+ exec_options = exec_options.merge(:method => :post, :url => "#{host}/#{address}", :payload => args)
455
+ response = RestClient::Request.execute(exec_options)
456
+ end
457
+
458
+ response.body
459
+ end
460
+
461
+ def is_windows?
462
+ (RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/)
463
+ end
464
+
465
+ def is_macosx?
466
+ (RbConfig::CONFIG['host_os'] =~ /darwin/)
467
+ end
468
+
469
+ def validate_ipa(ipa)
470
+ result = false
471
+ dir = Dir.mktmpdir #do |dir|
472
+
473
+ unzip_file(ipa, dir)
474
+ unless File.directory?("#{dir}/Payload") #macos only
475
+ raise ValidationError, "Unzipping #{ipa} to #{dir} failed: Did not find a Payload directory (invalid .ipa)."
476
+ end
477
+ app_dir = Dir.foreach("#{dir}/Payload").find { |d| /\.app$/.match(d) }
478
+ app = app_dir.split('.')[0]
479
+ res = `otool "#{File.expand_path(dir)}/Payload/#{app_dir}/#{app}" -o 2> /dev/null | grep CalabashServer`
480
+
481
+ if /CalabashServer/.match(res)
482
+ puts "ipa: #{ipa} *contains* calabash.framework"
483
+ result = :calabash
484
+ end
485
+
486
+ unless result
487
+ res = `otool "#{File.expand_path(dir)}/Payload/#{app_dir}/#{app}" -o 2> /dev/null | grep FrankServer`
488
+ if /FrankServer/.match(res)
489
+ puts "ipa: #{ipa} *contains* FrankServer"
490
+ result = :frank
491
+ else
492
+ puts "ipa: #{ipa} *does not contain* calabash.framework"
493
+ result = false
494
+ end
495
+ end
496
+
497
+ result
498
+ end
499
+
500
+
501
+ def log(message)
502
+ puts "#{Time.now } #{message}"
503
+ $stdout.flush
504
+ end
505
+
506
+ def log_header(message)
507
+ if is_windows?
508
+ puts "\n### #{message} ###"
509
+ else
510
+ puts "\n\e[#{35}m### #{message} ###\e[0m"
511
+ end
512
+ end
513
+
514
+
515
+ def verify_app_and_extract_test_server
516
+ server = nil
517
+
518
+ unless File.exist?(app)
519
+ raise ValidationError, "No such file: #{app}"
520
+ end
521
+ unless (/\.(apk|ipa)$/ =~ app)
522
+ raise ValidationError, '<APP> should be either an ipa or apk file.'
523
+ end
524
+ if is_ios? and is_macosx? and not skip_check
525
+ log_header('Checking ipa for linking with Calabash')
526
+ server = validate_ipa(app)
527
+ abort_unless(server) do
528
+ puts 'The .ipa file does not seem to be linked with Calabash.'
529
+ puts 'Verify that your app is linked correctly.'
530
+ puts 'To disable this check run with --skip-check or set Environment Variable CHECK_IPA=0'
531
+ end
532
+ end
533
+ server
534
+ end
535
+
536
+ def abort(&block)
537
+ yield block
538
+ exit 1
539
+ end
540
+
541
+ def abort_unless(condition, &block)
542
+ unless condition
543
+ yield block
544
+ exit 1
545
+ end
546
+ end
547
+
548
+ def log_and_abort(message)
549
+ abort do
550
+ puts message
551
+ end
552
+ end
553
+
554
+
555
+ def verify_dependencies
556
+ if is_android?
557
+ abort_unless(File.exist?(test_server_path)) do
558
+ puts 'No test server found. Please run:'
559
+ puts " calabash-android build #{app}"
560
+ end
561
+
562
+ calabash_gem = Dir.glob('vendor/cache/calabash-android-*').first
563
+ abort_unless(calabash_gem) do
564
+ puts 'calabash-android was not packaged correct.'
565
+ puts 'Please tell testcloud@xamarin.com about this bug.'
566
+ end
567
+ end
568
+ end
569
+
570
+
571
+ def parse_and_set_config_and_profile
572
+ config_path = options[:config]
573
+ if config_path
574
+ config_path = File.expand_path(config_path)
575
+ unless File.exist?(config_path)
576
+ raise ValidationError, "Config file does not exist #{config_path}"
577
+ end
578
+
579
+
580
+ begin
581
+ config_yml = YAML.load_file(config_path)
582
+ rescue Exception => e
583
+ puts "Unable to parse #{config_path} as yml. Is this your Cucumber.yml file?"
584
+ raise ValidationError, e
585
+ end
586
+
587
+ if ENV['DEBUG']
588
+ puts 'Parsed Cucumber config as:'
589
+ puts config_yml.inspect
590
+ end
591
+
592
+ profile = options[:profile]
593
+ unless profile
594
+ raise ValidationError, 'Config file provided but no profile selected.'
595
+ else
596
+ unless config_yml[profile]
597
+ raise ValidationError, "Config file provided did not contain profile #{profile}."
598
+ else
599
+ puts "Using profile #{profile}..."
600
+ self.profile = profile
601
+ end
602
+ end
603
+ else
604
+ if options[:profile]
605
+ raise ValidationError, 'Profile selected but no config file provided.'
606
+ end
607
+ end
608
+
609
+ self.config = config_path
610
+ end
611
+
612
+ end
613
+
614
+
615
+ end
616
+ end
617
+
618
+
@@ -0,0 +1,3 @@
1
+ module XamarinTestCloud
2
+ VERSION = '0.9.1'
3
+ end
@@ -0,0 +1 @@
1
+ require 'calabash-cucumber/calabash_steps'
@@ -0,0 +1,4 @@
1
+ Given /^I am on the Welcome Screen$/ do
2
+ element_exists("view")
3
+ sleep(STEP_PAUSE)
4
+ end
@@ -0,0 +1 @@
1
+ require 'calabash-cucumber/cucumber'
File without changes
@@ -0,0 +1,77 @@
1
+ ########################################
2
+ # #
3
+ # Important Note #
4
+ # #
5
+ # When running calabash-ios tests at #
6
+ # www.xamarin-test-cloud.com #
7
+ # this file will be overwritten by #
8
+ # a file which automates #
9
+ # app launch on devices. #
10
+ # #
11
+ # Don't rely on this file being #
12
+ # present when running at #
13
+ # www.xamarin-test-cloud.com. #
14
+ # #
15
+ # Only put stuff here to automate #
16
+ # iOS Simulator. #
17
+ # #
18
+ # You can put your app bundle path #
19
+ # for automating simulator app start: #
20
+ # Uncomment APP_BUNDLE_PATH =.. #
21
+ # #
22
+ ########################################
23
+
24
+ require 'calabash-cucumber/launch/simulator_helper'
25
+ require 'sim_launcher'
26
+
27
+ # Uncomment and replace ?? appropriately
28
+ # This should point to your Simulator build
29
+ # which includes calabash framework
30
+ # this is usually the Calabash build configuration
31
+ # of your production target.
32
+ #APP_BUNDLE_PATH = "~/Library/Developer/Xcode/DerivedData/??/Build/Products/Calabash-iphonesimulator/??.app"
33
+ #
34
+
35
+ def reset_app_jail(sdk, app_path)
36
+ app = File.basename(app_path)
37
+ bundle = `find "#{ENV['HOME']}/Library/Application Support/iPhone Simulator/#{sdk}/Applications/" -type d -depth 2 -name #{app} | head -n 1`
38
+ return if bundle.empty? # Assuming we're already clean
39
+
40
+ sandbox = File.dirname(bundle)
41
+ ['Library', 'Documents', 'tmp'].each do |dir|
42
+ FileUtils.rm_rf(File.join(sandbox, dir))
43
+ end
44
+ end
45
+
46
+ def relaunch
47
+ if ENV['NO_LAUNCH']!="1"
48
+ sdk = ENV['SDK_VERSION'] || SimLauncher::SdkDetector.new().latest_sdk_version
49
+ path = Calabash::Cucumber::SimulatorHelper.app_bundle_or_raise(app_path)
50
+ if ENV['RESET_BETWEEN_SCENARIOS']=="1"
51
+ reset_app_jail(sdk, path)
52
+ end
53
+
54
+ Calabash::Cucumber::SimulatorHelper.relaunch(path,sdk,ENV['DEVICE'] || 'iphone')
55
+ end
56
+ end
57
+
58
+ def app_path
59
+ ENV['APP_BUNDLE_PATH'] || (defined?(APP_BUNDLE_PATH) && APP_BUNDLE_PATH)
60
+ end
61
+
62
+ def calabash_notify
63
+ if self.respond_to?(:on_launch)
64
+ self.on_launch
65
+ end
66
+ end
67
+
68
+ Before do |scenario|
69
+ relaunch
70
+ calabash_notify
71
+ end
72
+
73
+ at_exit do
74
+ if ENV['NO_LAUNCH']!="1" and ENV['NO_STOP']!="1"
75
+ Calabash::Cucumber::SimulatorHelper.stop
76
+ end
77
+ end
@@ -0,0 +1,62 @@
1
+ require 'test/unit'
2
+ require 'thor'
3
+ require 'fileutils'
4
+ require 'xamarin-test-cloud/cli'
5
+
6
+ class TestParser < Test::Unit::TestCase
7
+ def test_should_raise_if_no_app_or_api_key_is_given
8
+ script = XamarinTestCloud::CLI.new([])
9
+ assert_raise Thor::InvocationError do
10
+ script.invoke(:submit)
11
+ end
12
+
13
+ script = XamarinTestCloud::CLI.new(["test/ipa/2012 Olympics_cal.ipa"])
14
+ assert_raise Thor::InvocationError do
15
+ script.invoke(:submit)
16
+ end
17
+
18
+
19
+ end
20
+
21
+ def test_should_raise_if_app_is_not_file_ipa_or_apk
22
+ script = XamarinTestCloud::CLI.new(["test/ipa/NONE_EXIST_2012 Olympics_cal.ipa","JIFZCTPZJJXJLEKMMYRY","."])
23
+ assert_raise RuntimeError do
24
+ script.invoke(:submit)
25
+ end
26
+
27
+ script = XamarinTestCloud::CLI.new(["test/ipa/features.zip","JIFZCTPZJJXJLEKMMYRY","."])
28
+
29
+ assert_raise RuntimeError do
30
+ script.invoke(:submit)
31
+ end
32
+ end
33
+
34
+ def test_should_parse_all_configuration_options
35
+ FileUtils.rm_f(File.join("test","vendor"))
36
+ FileUtils.rm_f(File.join("test","Gemfile"))
37
+ FileUtils.rm_f(File.join("test","Gemfile.lock"))
38
+
39
+ config_options = {
40
+ :host => "http://localhost:8080",
41
+ :workspace => "test",
42
+ :features => "test/ipa/features.zip",
43
+ :config => "test/config/cucumber.yml",
44
+ :profile => "a",
45
+ "skip-check" => false,
46
+ "reset-between-scenarios" => false,
47
+ "dry-run" => true
48
+ }
49
+ script = XamarinTestCloud::CLI.new(["test/ipa/2012 Olympics_cal.ipa","JIFZCTPZJJXJLEKMMYRY"],config_options)
50
+
51
+
52
+ script.invoke(:submit)
53
+
54
+ FileUtils.rm_f(File.join("test","vendor","cache"))
55
+ FileUtils.rm_f(File.join("test","Gemfile"))
56
+ FileUtils.rm_f(File.join("test","Gemfile.lock"))
57
+
58
+
59
+
60
+ end
61
+
62
+ end
metadata ADDED
@@ -0,0 +1,142 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: xamarin-test-cloud
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.1
5
+ platform: ruby
6
+ authors:
7
+ - Karl Krukow
8
+ - Jonas Maturana Larsen
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-07-26 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: thor
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - '>='
19
+ - !ruby/object:Gem::Version
20
+ version: 0.18.1
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - '>='
26
+ - !ruby/object:Gem::Version
27
+ version: 0.18.1
28
+ - !ruby/object:Gem::Dependency
29
+ name: bundler
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - '>='
33
+ - !ruby/object:Gem::Version
34
+ version: 1.3.0
35
+ - - <
36
+ - !ruby/object:Gem::Version
37
+ version: '2.0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - '>='
43
+ - !ruby/object:Gem::Version
44
+ version: 1.3.0
45
+ - - <
46
+ - !ruby/object:Gem::Version
47
+ version: '2.0'
48
+ - !ruby/object:Gem::Dependency
49
+ name: json
50
+ requirement: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: rubyzip
64
+ requirement: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: 0.9.9
69
+ type: :runtime
70
+ prerelease: false
71
+ version_requirements: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: 0.9.9
76
+ - !ruby/object:Gem::Dependency
77
+ name: rest-client
78
+ requirement: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
81
+ - !ruby/object:Gem::Version
82
+ version: 1.6.7
83
+ type: :runtime
84
+ prerelease: false
85
+ version_requirements: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ~>
88
+ - !ruby/object:Gem::Version
89
+ version: 1.6.7
90
+ description: Xamarin Test Cloud let's you automatically test your app on hundreds
91
+ of mobile devices
92
+ email:
93
+ - karl@xamarin.com
94
+ - jonas@xamarin.com
95
+ executables:
96
+ - test-cloud
97
+ extensions: []
98
+ extra_rdoc_files: []
99
+ files:
100
+ - bin/test-cloud
101
+ - lib/GemfileAndroid
102
+ - lib/GemfileIOS
103
+ - lib/xamarin-test-cloud/cli.rb
104
+ - lib/xamarin-test-cloud/version.rb
105
+ - README.md
106
+ - CHANGES.txt
107
+ - test/ipa/features/step_definitions/calabash_steps.rb
108
+ - test/ipa/features/step_definitions/my_first_steps.rb
109
+ - test/ipa/features/support/env.rb
110
+ - test/ipa/features/support/hooks.rb
111
+ - test/ipa/features/support/launch.rb
112
+ - test/test_parser.rb
113
+ homepage: http://xamarin.com/test-cloud
114
+ licenses: []
115
+ metadata: {}
116
+ post_install_message:
117
+ rdoc_options: []
118
+ require_paths:
119
+ - lib
120
+ required_ruby_version: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - '>='
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ required_rubygems_version: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - '>='
128
+ - !ruby/object:Gem::Version
129
+ version: '0'
130
+ requirements: []
131
+ rubyforge_project:
132
+ rubygems_version: 2.0.5
133
+ signing_key:
134
+ specification_version: 4
135
+ summary: Command-line interface to Xamarin Test Cloud
136
+ test_files:
137
+ - test/ipa/features/step_definitions/calabash_steps.rb
138
+ - test/ipa/features/step_definitions/my_first_steps.rb
139
+ - test/ipa/features/support/env.rb
140
+ - test/ipa/features/support/hooks.rb
141
+ - test/ipa/features/support/launch.rb
142
+ - test/test_parser.rb