flick 0.1.0 → 0.2.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: e81859ea546a01df31102f29fc4657df27c46fe0
4
- data.tar.gz: 9f1a43b57c5337fe732b01bdabec233a4cf0040c
3
+ metadata.gz: bb64107c4d21b8fc509321fdc0d98763e3e376e6
4
+ data.tar.gz: 154aa4c74efa4fead2f6cdd5880c7b71602607ed
5
5
  SHA512:
6
- metadata.gz: 5da1d5231a633095d3fe7cda6452d007d7c13e78f17ddee6939bc6427f1135909a544649a83285850f71cd06de4a43b7ade43c10bd463e493a9dad4cf7e14f8b
7
- data.tar.gz: 83780db9a51eb5ab0b50a0e812138e95082389ed5ddf197cc22bf000d62ed9575dc869bec50ebfbc11160640395cd68f72f0f25e04b1beca4ac87f5a6470072d
6
+ metadata.gz: 07dba93a03a97b5a725bc98afca7e3cda8c66138aab7ae1f15efaa6f3caf4a43a01599344117130f04f54391ef6c40629178b66e714c5693e0d1dacd7f026c43
7
+ data.tar.gz: 7e400657a20828488546d0229b251fe3bbe7a125c25aee55eb23aba1cc41fdc275b0d2aa6bd4c76c1c04295194e535fdd2bddab91a18486db83214081abd991e
data/README.md CHANGED
@@ -14,6 +14,7 @@ Features
14
14
  * Falls back to screenshot recording if video record is not available.
15
15
  * Video record android emulators and **real** iOS devices.
16
16
  * Takes a screenshot every 0.5 seconds (default), then combines the screenshots into a single mp4 or gif.
17
+ * Android pulls only unique (default) screenshots from devices/emulators. e.g. A 1 minute test run might convert to only 30 seconds of video based on unique images. You can change this by passing `-q false` to pull all images instead.
17
18
  * iOS example [here](https://www.dropbox.com/s/4pjhhmnsx9gj5pi/ios-flick-example.mp4?dl=0)
18
19
  * Android Emulator example [here](https://www.dropbox.com/s/gwunrvgzxkny13z/android-flick-example.mp4?dl=0)
19
20
  * Flick auto selects device when only one device is connected, per platform.
@@ -28,17 +29,24 @@ So I created Flick to work for my needs, and included a couple other tools I use
28
29
 
29
30
  If you're looking for high-quality video, then this wouldn't be the tool for you. Take a look at this great tool [androidtool-mac](https://github.com/mortenjust/androidtool-mac) instead.
30
31
 
31
- Prerequisites
32
+ Prerequisites
32
33
  -------------
33
34
  #### System Tools
34
35
  * Install ffmpeg. [OSX](https://trac.ffmpeg.org/wiki/CompilationGuide/MacOSX)
35
36
  * ```$ brew install ffmpeg```
37
+ * Install ffmpeg. [Windows](https://ffmpeg.zeranoe.com/builds/)
38
+ * Download either the 32-bit or 64-bit static versions.
39
+ * Extract the zip file and move the ffmpeg folder somewhere else.
40
+ * Add the ffmpeg bin location to your PATH user variables. e.g. C:\ffmpeg\bin
36
41
  * Install mp4box. [OSX](http://hunterford.me/compiling-mp4box-on-mac-os-x/)
37
42
  * ```$ brew install mp4box```
43
+ * Install mp4box. [Windows](http://www.videohelp.com/software/MP4Box)
44
+ * Run the gpac-0.6.2-DEV-rev634-gd2332cb-master-x(64 or 32 bit).exe
45
+ * Select to install only the MP4Box component.
38
46
 
39
47
  #### Android
40
48
  * Install [SDK Tools](http://developer.android.com/sdk/installing/index.html?pkg=tools).
41
- * SDK tools are added to your $PATH. [OSX](http://stackoverflow.com/questions/5526470/trying-to-add-adb-to-path-variable-osx)
49
+ * SDK tools are added to your $PATH. [OSX](http://stackoverflow.com/questions/5526470/trying-to-add-adb-to-path-variable-osx)
42
50
  * Enable [USB Debugging](https://www.kingoapp.com/root-tutorials/how-to-enable-usb-debugging-mode-on-android.htm) on your device(s).
43
51
  * Emulator or Devices have approximately 1GB of [sdcard space](http://developer.android.com/tools/help/mksdcard.html).
44
52
 
@@ -60,54 +68,55 @@ Usage:
60
68
  $ flick --help
61
69
 
62
70
  DESCRIPTION:
63
-
71
+
64
72
  A CLI to capture screenshots, video, logs, and device info for Android (Devices & Emulators) and iOS (Devices).
65
-
73
+
66
74
  COMMANDS:
67
-
68
- help Display global or [command] help documentation
69
- info Get device information
70
- log Get device log output
71
- screenshot Take a screenshot
72
- video Record video
73
-
75
+
76
+ help Display global or [command] help documentation
77
+ info Get device information
78
+ log Get device log output
79
+ screenshot Take a screenshot
80
+ video Record video
81
+
74
82
  GLOBAL OPTIONS:
75
-
76
- -h, --help
83
+
84
+ -h, --help
77
85
  Display help documentation
78
-
79
- -v, --version
86
+
87
+ -v, --version
80
88
  Display version information
81
-
82
- -t, --trace
89
+
90
+ -t, --trace
83
91
  Display backtrace when an error occurs
84
92
 
85
- * See usage examples in:
86
-
87
93
  `$ flick info --help`
88
-
94
+
89
95
  $ flick info -p (ios or android)
90
96
  $ flick info -p (ios or android) -s true -o $HOME
91
-
92
-
97
+
98
+
93
99
  `$ flick log --help`
94
-
100
+
95
101
  $ flick log -a start -p (ios or android) -o $HOME -n iosLog
96
102
  $ flick log -a stop -p (ios or android)
97
103
 
98
104
  `$ flick screenshot --help`
99
-
100
- $ flick screenshot -p (ios or android)
101
-
105
+
106
+ $ flick screenshot -p (ios or android) -o $HOME -n myImage
107
+
102
108
  `$ flick video --help`
103
-
109
+
104
110
  $ flick video -a start -p (ios or android)
105
111
  $ flick video -a stop -p (ios or android) -o /output -n myVideo -f gif
106
112
  $ flick video -a start -p android -u emulator-5554 -c 1000
107
113
  $ flick video -a stop -p android -u emulator-5554
108
114
 
109
- Demo
110
- ----
115
+ `$ flick manager --help`
116
+ $ flick manager -a install -p (ios or android) -f ~/myApp/my-awesome-app.apk or .app
117
+ $ flick maanger -a uninstall -p (ios or android) -n com.package.name
118
+
119
+ ##Demo
111
120
  <img src="https://www.dropbox.com/s/9be37gc1c2dlxa6/flick-demo.gif?raw=1" width="600">
112
121
 
113
122
  ## Contributing
@@ -119,7 +128,7 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/isonic
119
128
  * Setup Flick android for cross platform os's (windows & linux)
120
129
  * Add screenshot capture for iOS Simulators.
121
130
  * Multithread the screenshot and pull process.
122
- * Look into capturing video from iOS similar to [this](https://github.com/mortenjust/androidtool-mac/blob/9347cd9aeca9e7370e323d12f862bc5d8beacc25/AndroidTool/IOSDeviceHelper.swift#L56)
131
+ * Look into capturing video for iOS similar to [this](https://github.com/mortenjust/androidtool-mac/blob/9347cd9aeca9e7370e323d12f862bc5d8beacc25/AndroidTool/IOSDeviceHelper.swift#L56)
123
132
 
124
133
  ## License
125
134
 
data/bin/flick CHANGED
@@ -28,7 +28,7 @@ command :video do |c|
28
28
  :unique => true, #defaults to pulling only unique screenshots. Speeds up pulling process from device.
29
29
  :format => "mp4",
30
30
  :outdir => Dir.pwd #defaults to save in current directory.
31
-
31
+
32
32
  Video.new(options.default).run
33
33
  end
34
34
  end
@@ -45,11 +45,11 @@ command :screenshot do |c|
45
45
  c.action do |args, options|
46
46
  options.default \
47
47
  :outdir => Dir.pwd #defaults to save in current directory.
48
-
48
+
49
49
  Screenshot.new(options.default).screenshot
50
50
  end
51
51
  end
52
-
52
+
53
53
  command :log do |c|
54
54
  c.syntax = 'flick log [options]'
55
55
  c.summary = 'Get device log output'
@@ -63,7 +63,7 @@ command :log do |c|
63
63
  c.action do |args, options|
64
64
  options.default \
65
65
  :outdir => Dir.pwd #defaults to save in current directory.
66
-
66
+
67
67
  Log.new(options.default).run
68
68
  end
69
69
  end
@@ -81,7 +81,23 @@ command :info do |c|
81
81
  options.default \
82
82
  :save => false,
83
83
  :outdir => Dir.pwd #defaults to save in current directory.
84
-
84
+
85
85
  Info.new(options.default).info
86
86
  end
87
+ end
88
+
89
+ command :manager do |c|
90
+ c.syntax = 'flick manager [options]'
91
+ c.summary = 'Manage apps on devices'
92
+ c.description = c.summary
93
+ c.example 'description', "flick manager -a install -p android -f ~/my-awesome-app.apk\n flick manager -a uninstall -p ios -n com.viber"
94
+ c.option '-a', '--action ACTION', String, 'Set action: install or uninstall'
95
+ c.option '-p', '--platform PLATFORM', String, 'Set platform: android or ios'
96
+ c.option '-u', '--udid UDID', String, 'Set device UDID.'
97
+ c.option '-f', '--file FILE', String, 'Set the apk or app file location path.'
98
+ c.option '-n', '--name NAME', String, 'Set the package name.'
99
+ c.action do |args, options|
100
+
101
+ Manager.new(options.default).run
102
+ end
87
103
  end
data/flick.gemspec CHANGED
@@ -24,7 +24,7 @@ Gem::Specification.new do |spec|
24
24
  spec.bindir = "bin"
25
25
  spec.executables = ["flick"]
26
26
  spec.require_paths = ["lib"]
27
-
27
+
28
28
  spec.add_development_dependency "bundler", "~> 1.10"
29
29
  spec.add_development_dependency "rake", "~> 10.0"
30
30
  spec.add_development_dependency "rspec", '~> 3.4', '>= 3.4.0'
@@ -34,4 +34,7 @@ Gem::Specification.new do |spec|
34
34
  spec.add_dependency "json", '~> 1.8', '>= 1.8.3'
35
35
  spec.add_dependency "wannabe_bool", "~> 0.5.0"
36
36
  spec.add_dependency "awesome_print", '~> 1.6', '>= 1.6.1'
37
+ spec.add_dependency "os", "~> 0.9.6"
38
+ spec.add_dependency "sys-proctable", '~> 1.1', '>= 1.1.1'
39
+ spec.add_dependency "apktools", '~> 0.7.1', '>= 0.7.1'
37
40
  end
data/lib/flick.rb CHANGED
@@ -3,6 +3,10 @@ require 'json'
3
3
  require 'parallel'
4
4
  require 'wannabe_bool'
5
5
  require 'awesome_print'
6
+ require 'os'
7
+ require 'tempfile'
8
+ require 'sys/proctable'
9
+ require 'apktools/apkxml'
6
10
 
7
11
  require "flick/version"
8
12
  require_relative "./flick/android"
@@ -13,4 +17,5 @@ require_relative "./flick/log"
13
17
  require_relative "./flick/screenshot"
14
18
  require_relative "./flick/simple_daemon"
15
19
  require_relative "./flick/system"
16
- require_relative "./flick/video"
20
+ require_relative "./flick/video"
21
+ require_relative "./flick/manager"
data/lib/flick/android.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  module Flick
2
2
  class Android
3
3
  attr_accessor :flick_dir, :dir_name, :udid, :name, :outdir, :unique, :limit, :specs
4
-
4
+
5
5
  def initialize options
6
6
  Flick::Checker.system_dependency "adb"
7
7
  self.flick_dir = "#{Dir.home}/.flick"
@@ -14,43 +14,43 @@ module Flick
14
14
  self.specs = options.fetch(:specs, false)
15
15
  create_flick_dirs
16
16
  end
17
-
17
+
18
18
  def create_flick_dirs
19
19
  Flick::System.setup_system_dir flick_dir
20
- %x(adb -s #{udid} shell 'mkdir #{dir_name}' >> /dev/null 2>&1)
20
+ %x(adb -s #{udid} shell 'mkdir #{dir_name}')
21
21
  end
22
-
22
+
23
23
  def clear_files
24
- %x(adb -s #{udid} shell rm '#{dir_name}/*' >> /dev/null 2>&1)
25
24
  Flick::System.clean_system_dir flick_dir, udid
25
+ %x(adb -s #{udid} shell rm '#{dir_name}/*')
26
26
  end
27
-
27
+
28
28
  def devices
29
29
  (`adb devices`).scan(/\n(.*)\t/).flatten
30
30
  end
31
-
31
+
32
32
  def devices_connected?
33
- !devices.empty?
33
+ devices.any?
34
34
  end
35
-
35
+
36
36
  def check_for_devices
37
37
  unless devices_connected?
38
- puts "\nNo Devices Connected or Authorized!!!\nMake sure at least one device (emulator/simulator) is started!\n".red
38
+ puts "\nNo Devices Connected or Authorized!!!\nMake sure at least one device (emulator/simulator) is connected!\n".red
39
39
  abort
40
40
  end
41
41
  end
42
-
42
+
43
43
  def get_device_udid opts_hash
44
44
  devices_connected?
45
45
  return unless opts_hash[:udid].nil?
46
46
  if devices.size == 1
47
- devices[0]
47
+ devices.first
48
48
  else
49
49
  puts "\nMultiple android devices '#{devices}' found.\nSpecify a single UDID. e.g. -u #{devices.sample}\n".red
50
50
  abort unless specs
51
51
  end
52
52
  end
53
-
53
+
54
54
  def info
55
55
  specs = { os: "ro.build.version.release", manufacturer: "ro.product.manufacturer", model: "ro.product.model", sdk: "ro.build.version.sdk" }
56
56
  hash = { udid: udid }
@@ -61,54 +61,85 @@ module Flick
61
61
  hash
62
62
  end
63
63
 
64
+ def install app_path
65
+ %x(adb -s #{udid} install -r #{app_path})
66
+ end
67
+
68
+ def uninstall package
69
+ if app_installed? package
70
+ %x(adb -s #{udid} shell pm uninstall #{package})
71
+ else
72
+ puts packages
73
+ puts "\n#{package} was not found on device #{udid}! Please choose one from above. e.g. #{packages.sample}\n".red
74
+ end
75
+ end
76
+
77
+ def app_version app_path
78
+ manifest(app_path).find {|x| x.name == "versionName" }["value"]
79
+ end
80
+
81
+ def package_name app_path
82
+ manifest(app_path).find {|x| x.name == "package" }["value"]
83
+ end
84
+
85
+ def manifest app_path
86
+ data = ApkXml.new app_path
87
+ data.parse_xml("AndroidManifest.xml", false, true)
88
+ data.xml_elements[0].attributes
89
+ end
90
+
91
+ def app_installed? package
92
+ packages.include? "package:#{package}"
93
+ end
94
+
95
+ def packages
96
+ %x(adb -s #{udid} shell pm list packages).split
97
+ end
98
+
64
99
  def os_version
65
- `adb -s #{udid} shell getprop "ro.build.version.release"`.strip.to_f
100
+ %x(adb -s #{udid} shell getprop "ro.build.version.release").strip.to_f
66
101
  end
67
-
102
+
68
103
  def screenshot name
69
104
  %x(adb -s #{udid} shell screencap #{dir_name}/#{name}.png)
70
105
  end
71
-
106
+
72
107
  def log name
73
108
  %x(adb -s #{udid} logcat -v long > #{outdir}/#{name}.log)
74
109
  end
75
-
110
+
76
111
  def recordable?
77
- (`adb -s #{udid} shell 'ls /system/bin/screenrecord'`).strip == "/system/bin/screenrecord"
112
+ %x(adb -s #{udid} shell "ls /system/bin/screenrecord").strip == "/system/bin/screenrecord"
78
113
  end
79
-
114
+
80
115
  def screenrecord name
81
116
  %x(adb -s #{udid} shell screenrecord --time-limit #{limit} --size 720x1280 #{dir_name}/#{name}.mp4)
82
117
  end
83
-
118
+
84
119
  def pull_file file, dir
85
- %x(adb -s #{udid} pull #{file} #{dir} >> /dev/null 2>&1)
120
+ %x(adb -s #{udid} pull #{file} #{dir})
86
121
  end
87
-
88
- def unique_files
122
+
123
+ def unique_files type
89
124
  if os_version < 6.0
90
125
  command = "md5"
91
126
  else
92
127
  command = "md5sum"
93
128
  end
94
- files = `adb -s #{udid} shell "#{command} #{dir_name}/#{udid}*"`
129
+ files = %x(adb -s #{udid} shell "#{command} #{dir_name}/#{type}-#{udid}*")
95
130
  hash = files.split("\r\n").map { |file| { md5: file.match(/(.*) /)[1].strip, file: file.match(/ (.*)/)[1].strip } }
96
131
  hash.uniq! { |e| e[:md5] }
97
132
  hash.map { |file| file[:file] }
98
133
  end
99
-
100
- def pull_files
134
+
135
+ def pull_files type
101
136
  if unique
102
- files = unique_files
137
+ files = unique_files type
103
138
  else
104
- files = (`adb -s #{udid} shell "ls #{dir_name}/#{udid}*"`).split("\r\n")
139
+ files = %x(adb -s #{udid} shell "ls #{dir_name}/#{type}-#{udid}*").split("\r\n")
105
140
  end
106
141
  return if files.empty?
107
142
  Parallel.map(files, in_threads: 10) { |file| pull_file file, flick_dir }
108
143
  end
109
-
110
- def screenshots_exist?
111
- (`adb -s #{udid} shell "ls #{dir_name}/#{udid}-*.png | wc -l"`).to_i > 0
112
- end
113
144
  end
114
145
  end
data/lib/flick/checker.rb CHANGED
@@ -1,13 +1,24 @@
1
1
  module Flick
2
2
  module Checker
3
+ def self.which(cmd)
4
+ exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
5
+ ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
6
+ exts.each { |ext|
7
+ exe = File.join(path, "#{cmd}#{ext}")
8
+ return exe if File.executable?(exe) && !File.directory?(exe)
9
+ }
10
+ end
11
+ return nil
12
+ end
13
+
3
14
  def self.system_dependency dep
4
- program = `which #{dep}`
15
+ program = self.which dep
5
16
  if program.empty?
6
17
  puts "\n#{dep} was not found. Please ensure you have installed #{dep} and it's in your $PATH\n".red
7
18
  abort
8
19
  end
9
20
  end
10
-
21
+
11
22
  def self.platform platform
12
23
  platforms = ["android","ios"]
13
24
  unless platforms.include? platform
@@ -15,7 +26,7 @@ module Flick
15
26
  abort
16
27
  end
17
28
  end
18
-
29
+
19
30
  def self.action action
20
31
  actions = ["start","stop"]
21
32
  unless actions.include? action
@@ -23,7 +34,22 @@ module Flick
23
34
  abort
24
35
  end
25
36
  end
26
-
37
+
38
+ def self.manager option
39
+ options = ["install","uninstall"]
40
+ unless options.include? option
41
+ puts "\nPlease specify a valid option #{options}. e.g. flick <job> -a #{options.sample} -p ios\n".red
42
+ abort
43
+ end
44
+ end
45
+
46
+ def self.file_exists? file
47
+ unless File.exists? file
48
+ puts "\n#{file} does not exist! Please specify a valid file path.".red
49
+ abort
50
+ end
51
+ end
52
+
27
53
  def self.format format
28
54
  formats = ["mp4","gif"]
29
55
  unless formats.include? format
data/lib/flick/ios.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  module Flick
2
2
  class Ios
3
3
  attr_accessor :flick_dir, :udid, :name, :outdir, :todir, :specs
4
-
4
+
5
5
  def initialize options
6
6
  Flick::Checker.system_dependency "idevice_id"
7
7
  self.flick_dir = "#{Dir.home}/.flick"
@@ -12,26 +12,26 @@ module Flick
12
12
  self.specs = options.fetch(:specs, false)
13
13
  create_flick_dirs
14
14
  end
15
-
15
+
16
16
  def create_flick_dirs
17
17
  Flick::System.setup_system_dir flick_dir
18
18
  end
19
-
19
+
20
20
  def devices
21
21
  (`idevice_id -l`).split.uniq.map { |d| d }
22
22
  end
23
-
23
+
24
24
  def devices_connected?
25
- !devices.empty?
25
+ devices.any?
26
26
  end
27
-
27
+
28
28
  def check_for_devices
29
29
  unless devices_connected?
30
30
  puts "\nNo iPhone or iPad Devices Connected!!!\nMake sure at least one REAL device is connected!\n".red
31
31
  abort
32
- end
32
+ end
33
33
  end
34
-
34
+
35
35
  def get_device_udid opts_hash
36
36
  check_for_devices
37
37
  return unless opts_hash[:udid].nil?
@@ -42,7 +42,7 @@ module Flick
42
42
  abort unless specs
43
43
  end
44
44
  end
45
-
45
+
46
46
  def info
47
47
  specs = { os: "ProductVersion", name: "DeviceName", arc: "CPUArchitecture", type: "DeviceClass", sdk: "ProductType" }
48
48
  hash = { udid: udid }
@@ -52,23 +52,46 @@ module Flick
52
52
  end
53
53
  hash
54
54
  end
55
-
55
+
56
56
  def recordable?
57
57
  false
58
58
  end
59
-
59
+
60
60
  def clear_files
61
61
  Flick::System.clean_system_dir flick_dir, udid
62
62
  end
63
-
63
+
64
64
  def screenshot name
65
65
  Flick::Checker.system_dependency "idevicescreenshot"
66
66
  %x(idevicescreenshot -u #{udid} #{todir}/#{name}.png)
67
67
  end
68
-
68
+
69
69
  def log name
70
70
  Flick::Checker.system_dependency "idevicesyslog"
71
71
  %x(idevicesyslog -u #{udid} > #{outdir}/#{name}.log)
72
72
  end
73
+
74
+ def install app_path
75
+ Flick::Checker.system_dependency "ideviceinstaller"
76
+ %x(ideviceinstaller -u #{udid} -i #{app_path})
77
+ end
78
+
79
+ def uninstall package
80
+ Flick::Checker.system_dependency "ideviceinstaller"
81
+ if app_installed? package
82
+ %x(ideviceinstaller -u #{udid} -U #{package})
83
+ else
84
+ puts packages
85
+ puts "\n#{package} was not found on device #{udid}! Please choose one from above. e.g. #{packages.sample}\n".red
86
+ end
87
+ end
88
+
89
+ def app_installed? package
90
+ packages.include? "#{package}"
91
+ end
92
+
93
+ def packages
94
+ %x(ideviceinstaller -u #{udid} -l -o list_user).split("\n")[1..100000].map { |p| p.match(/(.*) -/)[1] }
95
+ end
73
96
  end
74
97
  end
data/lib/flick/log.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  class Log
2
-
2
+
3
3
  attr_accessor :action, :platform, :driver, :udid
4
-
4
+
5
5
  def initialize options
6
6
  Flick::Checker.action options[:action]
7
7
  Flick::Checker.platform options[:platform]
@@ -16,24 +16,34 @@ class Log
16
16
  end
17
17
  self.udid = self.driver.udid
18
18
  end
19
-
19
+
20
+ def android
21
+ platform == "android"
22
+ end
23
+
24
+ def ios
25
+ platform == "ios"
26
+ end
27
+
20
28
  def run
21
29
  self.send(action)
22
30
  end
23
-
31
+
24
32
  def start
25
33
  puts "Saving to #{driver.outdir}/#{driver.name}.log"
26
34
  log
27
35
  end
28
-
36
+
29
37
  def stop
30
38
  Flick::System.kill_process "log", udid
39
+ Flick::System.kill "idevicesyslog -u #{udid}" if ios
40
+ Flick::System.kill "adb -s #{udid} logcat" if android
31
41
  end
32
-
42
+
33
43
  def log
34
44
  stop
35
45
  $0 = "flick-log-#{udid}"
36
- SimpleDaemon.daemonize! "/tmp/#{udid}-pidfile"
46
+ SimpleDaemon.daemonize!
37
47
  command = -> do
38
48
  driver.log driver.name
39
49
  end
@@ -0,0 +1,41 @@
1
+ class Manager
2
+
3
+ attr_accessor :action, :platform, :driver, :udid, :file, :name
4
+
5
+ def initialize options
6
+ Flick::Checker.manager options[:action]
7
+ Flick::Checker.platform options[:platform]
8
+ self.action = options[:action]
9
+ self.platform = options[:platform]
10
+ case platform
11
+ when "ios"
12
+ self.driver = Flick::Ios.new options
13
+ when "android"
14
+ self.driver = Flick::Android.new options
15
+ end
16
+ self.udid = self.driver.udid
17
+ self.file = options[:file]
18
+ self.name = options[:name]
19
+ end
20
+
21
+ def run
22
+ self.send(action)
23
+ end
24
+
25
+ def install
26
+ if file.nil?
27
+ puts "Specify a file path. e.g. -f #{Dir.home}/myApp/amazing-app.apk or .app".red; abort
28
+ else
29
+ Flick::Checker.file_exists? file
30
+ driver.install file
31
+ end
32
+ end
33
+
34
+ def uninstall
35
+ if name.nil?
36
+ puts "Specify a Package Name or Bundle ID. e.g. -n ".red; abort
37
+ else
38
+ driver.uninstall name
39
+ end
40
+ end
41
+ end
@@ -1,7 +1,7 @@
1
1
  class Screenshot
2
-
2
+
3
3
  attr_accessor :platform, :driver
4
-
4
+
5
5
  def initialize options
6
6
  Flick::Checker.platform options[:platform]
7
7
  self.platform = options[:platform]
@@ -14,15 +14,19 @@ class Screenshot
14
14
  end
15
15
  setup
16
16
  end
17
-
17
+
18
+ def android
19
+ platform == "android"
20
+ end
21
+
18
22
  def screenshot
19
23
  puts "Saving to #{driver.outdir}/#{driver.name}.png"
20
24
  driver.screenshot driver.name
21
- driver.pull_file "#{driver.dir_name}/#{driver.name}.png", driver.outdir if platform == "android"
25
+ driver.pull_file "#{driver.dir_name}/#{driver.name}.png", driver.outdir if android
22
26
  end
23
-
27
+
24
28
  private
25
-
29
+
26
30
  def setup
27
31
  driver.clear_files
28
32
  end
@@ -18,7 +18,7 @@
18
18
  # * The process becomes a session leader of a new session
19
19
  # * The process becomes the process group leader of a new process group
20
20
  # * The process has no controlling terminal
21
- #
21
+ #
22
22
  # Optionally fork again and have the parent exit. This guarantes that
23
23
  # the daemon is not a session leader nor can it acquire a controlling
24
24
  # terminal (under SVR4)
@@ -28,13 +28,13 @@
28
28
  # \_ simple daemon - writes out its pid to file
29
29
  #
30
30
  # Change the current working directory to / to avoid interfering with
31
- # mounting and unmounting. By default don't bother to chdir("/") here
31
+ # mounting and unmounting. By default don't bother to chdir("/") here
32
32
  # because we might to run inside APP_ROOT.
33
33
  #
34
34
  # Set file mode creation mask to 000 to allow creation of files with any
35
- # required permission later. By default umask is whatever was set by the
36
- # parent process at startup and can be set in config.ru and config_file,
37
- # so making it 0000 and potentially exposing sensitive log data can be
35
+ # required permission later. By default umask is whatever was set by the
36
+ # parent process at startup and can be set in config.ru and config_file,
37
+ # so making it 0000 and potentially exposing sensitive log data can be
38
38
  # bad policy.
39
39
  #
40
40
  # Close unneeded file descriptors inherited from the parent (there is no
@@ -46,8 +46,9 @@
46
46
  # value of getpid() after step 3.
47
47
  #
48
48
  class SimpleDaemon
49
+
49
50
  # In the directory where you want your daemon add a git submodule to
50
- # your project and create a daemon launcher script:
51
+ # your project and create a daemon launcher script:
51
52
  #
52
53
  # #!/usr/bin/env ruby
53
54
  #
@@ -74,14 +75,16 @@ class SimpleDaemon
74
75
  # $ ps aux | grep "my daemon"
75
76
  #
76
77
  #
77
- def self.daemonize! pidfile, out = '/dev/null', err = '/dev/null', safe = true
78
+ def self.daemonize! out = '/dev/null', err = '/dev/null', safe = true
78
79
  raise 'First fork failed' if (pid = fork) == -1
79
80
  exit unless pid.nil?
80
81
  Process.setsid
81
82
  raise 'Second fork failed' if (pid = fork) == -1
82
83
  exit unless pid.nil?
83
- kill pidfile
84
- write Process.pid, pidfile
84
+ kill_pid
85
+ @file = Tempfile.new
86
+ @file.write Process.pid
87
+ @file.rewind
85
88
  unless safe
86
89
  Dir.chdir '/'
87
90
  File.umask 0000
@@ -103,9 +106,12 @@ class SimpleDaemon
103
106
 
104
107
  # Try and read the existing pid from the pid file and signal HUP to
105
108
  # process.
106
- def self.kill pidfile
107
- opid = open(pidfile).read.strip.to_i
108
- Process.kill "HUP", opid
109
+ def self.kill_pid
110
+ unless @file.nil?
111
+ opid = @file.read
112
+ Process.kill "HUP", opid
113
+ @file.unlink
114
+ end
109
115
  rescue TypeError
110
116
  $stdout.puts "#{pidfile} was empty: TypeError"
111
117
  rescue Errno::ENOENT
data/lib/flick/system.rb CHANGED
@@ -1,28 +1,48 @@
1
1
  module Flick
2
2
  module System
3
+
4
+ include Sys #load sys-proctable methods
5
+
3
6
  def self.setup_system_dir dir_name
4
- %x(mkdir #{dir_name} >> /dev/null 2>&1)
7
+ Dir.mkdir dir_name unless File.exists? dir_name
5
8
  end
6
-
9
+
7
10
  def self.clean_system_dir dir_name, udid
8
- %x(rm #{dir_name}/#{udid}* >> /dev/null 2>&1)
11
+ Dir.glob("#{dir_name}/*#{udid}*").each do |file|
12
+ File.delete file
13
+ end
9
14
  end
10
-
11
- def self.process_running? type, udid
12
- `pgrep -f #{type}-#{udid}`.to_i > 0
15
+
16
+ def self.find_pid string
17
+ processes = ProcTable.ps.find_all { |x| x.cmdline.include? string }
18
+ processes.map { |p| p.pid } rescue []
13
19
  end
14
-
15
- def self.kill_process type, udid
16
- if self.process_running? type, udid
17
- pid = `pgrep -f flick-#{type}-#{udid}`.to_i
18
- `kill #{pid}` unless pid.zero?
19
- end
20
- if type == "video"
21
- pid = `pgrep -f #{udid}-`.to_i
22
- `kill #{pid}` unless pid.zero?
20
+
21
+ def self.kill_pids pid_array
22
+ return if pid_array.empty?
23
+ pid_array.each { |p| Process.kill 'SIGKILL', p }
24
+ end
25
+
26
+ def self.process_running? string
27
+ pid = self.find_pid string
28
+ unless pid.empty?
29
+ puts "PROCESSING IS RUNNING!!!"
30
+ true
31
+ else
32
+ false
23
33
  end
24
34
  end
25
-
35
+
36
+ def self.kill_process type, udid
37
+ pids = self.find_pid "#{type}-#{udid}"
38
+ self.kill_pids pids
39
+ end
40
+
41
+ def self.kill string
42
+ pids = self.find_pid string
43
+ self.kill_pids pids
44
+ end
45
+
26
46
  def self.video_length file
27
47
  (`ffmpeg -i #{file} 2>&1 | grep Duration | cut -d ' ' -f 4 | sed s/,//`).strip
28
48
  end
data/lib/flick/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Flick
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
data/lib/flick/video.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  class Video
2
2
 
3
3
  attr_accessor :action, :platform, :driver, :image_count, :seconds, :extended, :udid, :format
4
-
4
+
5
5
  def initialize options
6
6
  Flick::Checker.action options[:action]
7
7
  Flick::Checker.platform options[:platform]
@@ -20,19 +20,19 @@ class Video
20
20
  self.udid = self.driver.udid
21
21
  self.format = options[:format]
22
22
  end
23
-
23
+
24
24
  def android
25
25
  platform == "android"
26
26
  end
27
-
27
+
28
28
  def ios
29
29
  platform == "ios"
30
30
  end
31
-
31
+
32
32
  def run
33
33
  self.send(action)
34
34
  end
35
-
35
+
36
36
  def start
37
37
  driver.clear_files
38
38
  puts "\nStarting Recoder!!!"
@@ -49,7 +49,7 @@ class Video
49
49
  start_screenshot_record
50
50
  end
51
51
  end
52
-
52
+
53
53
  def stop
54
54
  puts "\nStopping Recorder!!!"
55
55
  if driver.recordable?
@@ -60,99 +60,94 @@ class Video
60
60
  sleep 1
61
61
  driver.clear_files
62
62
  end
63
-
64
- private
65
-
63
+
64
+ private
65
+
66
66
  def start_record
67
67
  Flick::System.kill_process "video", udid
68
68
  $0 = "flick-video-#{udid}"
69
- SimpleDaemon.daemonize! "/tmp/#{udid}-pidfile"
69
+ SimpleDaemon.daemonize!
70
70
  command = -> do
71
- driver.screenrecord "#{udid}-single"
71
+ driver.screenrecord "video-#{udid}-single"
72
72
  end
73
73
  command.call
74
74
  end
75
-
76
- def is_recording?
77
- !(`pgrep -f #{udid}-`).empty?
78
- end
79
-
75
+
80
76
  def loop_record
81
77
  Flick::System.kill_process "video", udid
82
78
  $0 = "flick-video-#{udid}"
83
- SimpleDaemon.daemonize! "/tmp/#{udid}-pidfile"
79
+ SimpleDaemon.daemonize!
84
80
  command = -> do
85
81
  count = "%03d" % 1
86
82
  loop do
87
- unless is_recording?
88
- driver.screenrecord "#{udid}-#{count}"
83
+ unless Flick::System.process_running? "#{udid}-"
84
+ driver.screenrecord "video-#{udid}-#{count}"
89
85
  count.next!
90
86
  end
91
87
  end
92
88
  end
93
89
  command.call
94
90
  end
95
-
91
+
96
92
  def stop_record
97
93
  Flick::System.kill_process "video", udid
98
94
  sleep 5 #wait for video process to finish
99
- driver.pull_files
100
- files = (`ls #{driver.flick_dir}/#{udid}*.mp4`).split("\n")
95
+ driver.pull_files "video"
96
+ files = Dir.glob("#{driver.flick_dir}/video-#{udid}*.mp4")
101
97
  return if files.empty?
102
98
  files.each { |file| system("mp4box -cat #{file} #{driver.flick_dir}/#{driver.name}.mp4") }
103
99
  puts "Saving to #{driver.outdir}/#{driver.name}.#{format}"
104
100
  if format == "gif"
105
101
  gif
106
102
  else
107
- %x(nohup mv #{driver.flick_dir}/#{driver.name}.mp4 #{driver.outdir}/#{driver.name}.mp4)
103
+ File.rename "#{driver.flick_dir}/#{driver.name}.mp4", "#{driver.outdir}/#{driver.name}.mp4"
108
104
  end
109
105
  end
110
-
106
+
111
107
  def start_screenshot_record
112
108
  Flick::System.kill_process "screenshot", udid
113
109
  puts "Process will stop after #{image_count} screenshots.\n"
114
110
  $0 = "flick-screenshot-#{udid}"
115
- SimpleDaemon.daemonize! "/tmp/#{udid}-pidfile"
111
+ SimpleDaemon.daemonize!
116
112
  command = -> do
117
113
  count = "%03d" % 1
118
114
  loop do
119
- if count.to_i < image_count
120
- driver.screenshot "#{udid}-#{count}"
115
+ if count.to_i <= image_count
116
+ driver.screenshot "screenshot-#{udid}-#{count}"
121
117
  count.next!; sleep seconds
122
118
  else
123
- puts "\nStop count exceeded. Saving to #{driver.outdir}/#{driver.name}.#{format}".red
124
- self.send(format)
119
+ stop_screenshot_recording
125
120
  break
126
121
  end
127
122
  end
128
123
  end
129
124
  command.call
130
125
  end
131
-
132
- def stop_screenshot_recording
133
- Flick::System.kill_process "screenshot", udid
134
- `rm /tmp/#{udid}-pidfile >> /dev/null 2>&1`
135
- driver.pull_files if android
126
+
127
+ def stop_screenshot_recording
128
+ driver.pull_files "screenshot" if android
136
129
  puts "Saving to #{driver.outdir}/#{driver.name}.#{format}"
137
130
  self.send(format)
138
131
  end
139
-
132
+
140
133
  def gif
141
134
  convert_images_to_mp4 unless driver.recordable?
142
- %x(nohup ffmpeg -i #{driver.flick_dir}/#{driver.name}.mp4 -pix_fmt rgb24 #{driver.outdir}/#{driver.name}.gif)
135
+ %x(ffmpeg -loglevel quiet -i #{driver.flick_dir}/#{driver.name}.mp4 -pix_fmt rgb24 #{driver.outdir}/#{driver.name}.gif)
143
136
  end
144
-
137
+
145
138
  def mp4
146
139
  convert_images_to_mp4
147
- %x(nohup mv #{driver.flick_dir}/#{driver.name}.mp4 #{driver.outdir}/#{driver.name}.mp4) unless format == "gif"
140
+ File.rename "#{driver.flick_dir}/#{driver.name}.mp4", "#{driver.outdir}/#{driver.name}.mp4" unless format == "gif"
148
141
  end
149
-
142
+
150
143
  def convert_images_to_mp4
151
144
  remove_zero_byte_images
152
- %x(nohup ffmpeg -framerate 1 -pattern_type glob -i '#{driver.flick_dir}/#{udid}*.png' -c:v libx264 -pix_fmt yuv420p #{driver.flick_dir}/#{driver.name}.mp4)
145
+ %x(ffmpeg -loglevel quiet -framerate 1 -pattern_type glob -i '#{driver.flick_dir}/screenshot-#{udid}*.png' -c:v libx264 -pix_fmt yuv420p #{driver.flick_dir}/#{driver.name}.mp4)
153
146
  end
154
-
147
+
155
148
  def remove_zero_byte_images
156
- %x(nohup find #{driver.flick_dir} -type f -size 0 | xargs rm '#{udid}*.png' -f)
149
+ Dir.glob("#{driver.flick_dir}/screenshot-#{udid}*.png").each do |f|
150
+ File.delete f if File.zero? f
151
+ end
157
152
  end
158
153
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flick
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-03-18 00:00:00.000000000 Z
11
+ date: 2016-07-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -166,6 +166,60 @@ dependencies:
166
166
  - - ">="
167
167
  - !ruby/object:Gem::Version
168
168
  version: 1.6.1
169
+ - !ruby/object:Gem::Dependency
170
+ name: os
171
+ requirement: !ruby/object:Gem::Requirement
172
+ requirements:
173
+ - - "~>"
174
+ - !ruby/object:Gem::Version
175
+ version: 0.9.6
176
+ type: :runtime
177
+ prerelease: false
178
+ version_requirements: !ruby/object:Gem::Requirement
179
+ requirements:
180
+ - - "~>"
181
+ - !ruby/object:Gem::Version
182
+ version: 0.9.6
183
+ - !ruby/object:Gem::Dependency
184
+ name: sys-proctable
185
+ requirement: !ruby/object:Gem::Requirement
186
+ requirements:
187
+ - - "~>"
188
+ - !ruby/object:Gem::Version
189
+ version: '1.1'
190
+ - - ">="
191
+ - !ruby/object:Gem::Version
192
+ version: 1.1.1
193
+ type: :runtime
194
+ prerelease: false
195
+ version_requirements: !ruby/object:Gem::Requirement
196
+ requirements:
197
+ - - "~>"
198
+ - !ruby/object:Gem::Version
199
+ version: '1.1'
200
+ - - ">="
201
+ - !ruby/object:Gem::Version
202
+ version: 1.1.1
203
+ - !ruby/object:Gem::Dependency
204
+ name: apktools
205
+ requirement: !ruby/object:Gem::Requirement
206
+ requirements:
207
+ - - "~>"
208
+ - !ruby/object:Gem::Version
209
+ version: 0.7.1
210
+ - - ">="
211
+ - !ruby/object:Gem::Version
212
+ version: 0.7.1
213
+ type: :runtime
214
+ prerelease: false
215
+ version_requirements: !ruby/object:Gem::Requirement
216
+ requirements:
217
+ - - "~>"
218
+ - !ruby/object:Gem::Version
219
+ version: 0.7.1
220
+ - - ">="
221
+ - !ruby/object:Gem::Version
222
+ version: 0.7.1
169
223
  description: A CLI with helpful QA tools for iOS and Android from the command line
170
224
  email:
171
225
  - justin.ison@gmail.com
@@ -192,6 +246,7 @@ files:
192
246
  - lib/flick/info.rb
193
247
  - lib/flick/ios.rb
194
248
  - lib/flick/log.rb
249
+ - lib/flick/manager.rb
195
250
  - lib/flick/screenshot.rb
196
251
  - lib/flick/simple_daemon.rb
197
252
  - lib/flick/system.rb
@@ -224,4 +279,3 @@ specification_version: 4
224
279
  summary: A CLI to capture screenshots, video, logs, and device information for Android
225
280
  (Devices & Emulators) and iOS (Devices).
226
281
  test_files: []
227
- has_rdoc: