startup-time 1.1.1 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 377344af2f7a7802b54695b2027e04f43d7c87bd6d9af5bfc4292300c3b226e2
4
- data.tar.gz: e2dc97fca65d09d04df5c132a8b5553f675be80b564b9dc2bc7f3a4dde1b5d01
3
+ metadata.gz: 5edbc737357924e55026244d924546b02dd43c3f875c710a4174027abb0bfc82
4
+ data.tar.gz: 8c79da2fbbbf34be5dc0bae8115b33a7ccbe681493c4efea6f86028c20ad1f2b
5
5
  SHA512:
6
- metadata.gz: ff0b44c9fac210362dab891ee279f9b9cfeaffb878e96007bc3d44cb180db835c43b86c82bd5214eb04e48ea8a8ec92616c04fd09b57b8daaf049f22c33dbcd3
7
- data.tar.gz: 5fe70d4707bee6f8c8e33957170c2d1e99b239ac921b6d4c94cb6c2a927c3840a62418a6e23335beae722ae50a687fd759949a5a34ae038164d10ff14dec2327
6
+ metadata.gz: d37390eae5748fde5435c8a261a7b6888f12c870424500124d6aa0d8ba6baa357c4de0846685013bb6f4cc3fac9b5470a4f167e265c775e1f2dcdcc520e7cb9a
7
+ data.tar.gz: 4eb78e5f8a43691d8f898b9fab7a23219e831c583836cf43fece6b3c9d1dc1d8af24170a0df7096a14a244cb4bdcb398035d7d8337a944606c0f964fa79abe97
data/CHANGELOG.md CHANGED
@@ -1,7 +1,13 @@
1
+ ## 1.2.0 - 2019-07-15
2
+
3
+ - add -t/--time option specifying the minimum length of time to run tests for
4
+ (default: 5s)
5
+ - format the ID -> group table as JSON if the --json option is provided
6
+
1
7
  ## 1.1.1 - 2019-07-13
2
8
 
3
9
  - add QuickJS
4
- - fix deno version command
10
+ - update deno version command
5
11
 
6
12
  ## 1.1.0 - 2019-02-27
7
13
 
data/README.md CHANGED
@@ -49,8 +49,8 @@ $ startup-time --only jvm
49
49
  # only run tests which finish quickly
50
50
  $ startup-time --only fast --omit slow-compile
51
51
 
52
- # increase the number of times each test is run (default: 10)
53
- $ startup-time --count 100
52
+ # minimum number of seconds to run the test suite for (default: 5)
53
+ $ startup-time --time 10
54
54
  ```
55
55
 
56
56
  ### Sample Output
@@ -106,7 +106,7 @@ USAGE:
106
106
 
107
107
  OPTIONS:
108
108
 
109
- -c, --count, --rounds INTEGER The number of times to run each test (default: 10)
109
+ -c, --count, --rounds INTEGER The number of times to run each program
110
110
  --clean Remove the build directory and exit
111
111
  (targets will be recompiled on the next run)
112
112
  -d, --dir PATH Specify the build directory
@@ -117,6 +117,8 @@ OPTIONS:
117
117
  -o, --only LIST Only run the specified tests (comma-separated list of IDs/groups)
118
118
  -O, --omit LIST Don't run the specified tests (comma-separated list of IDs/groups)
119
119
  -q, --quiet Suppress all inessential output
120
+ -t, --time INTEGER The minimum number of seconds to run the test suite for
121
+ (minimum: 2, default: 5)
120
122
  -v, --verbose Enable verbose logging
121
123
  -V, --version Display the version and exit
122
124
  ```
@@ -142,7 +144,7 @@ OPTIONS:
142
144
 
143
145
  ## VERSION
144
146
 
145
- 1.1.1
147
+ 1.2.0
146
148
 
147
149
  ## COPYRIGHT AND LICENSE
148
150
 
@@ -1,13 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'benchmark'
4
+ require 'json'
4
5
  require 'komenda'
5
6
  require 'shellwords' # for Array#shelljoin
6
7
  require 'tty/table'
7
8
 
8
- # FIXME we only need bundler/setup here, but it appears to create an incomplete
9
- # Bundler object which (sometimes) confuses Komenda as well as causing a
10
- # Gem::LoadError (for unicode-display_width)
9
+ # FIXME we only need bundler/setup here (for Bundler.with_clean_env), but it appears
10
+ # to create an incomplete Bundler object which (sometimes) confuses Komenda as well
11
+ # as causing a Gem::LoadError (for unicode-display_width)
11
12
  #
12
13
  # require 'bundler/setup'
13
14
  require 'bundler'
@@ -20,10 +21,11 @@ module StartupTime
20
21
 
21
22
  include FileUtils # for `sh`
22
23
  include Util # for `which`
23
- include Services.mixin %i[builder ids_to_groups selected_tests]
24
+ include Services.mixin %i[builder selected_tests]
24
25
 
25
26
  def initialize(args = ARGV)
26
27
  @options = Options.new(args)
28
+ @json = @options.format == :json
27
29
  @verbosity = @options.verbosity
28
30
  @times = []
29
31
 
@@ -37,20 +39,15 @@ module StartupTime
37
39
  # or print a help message) or the default command, which runs
38
40
  # the selected benchmark-tests
39
41
  def run
40
- if @verbosity == :verbose
41
- # used by StartupTime::App#time to dump the command line
42
- require 'shellwords'
43
- end
44
-
45
42
  case @options.action
46
43
  when :clean
47
44
  builder.clean!
48
45
  when :help
49
46
  puts @options.usage
50
- when :show_ids
51
- puts render_ids_to_groups
52
47
  when :version
53
48
  puts VERSION
49
+ when :show_ids
50
+ render_ids_to_groups
54
51
  else
55
52
  benchmark
56
53
  end
@@ -67,16 +64,55 @@ module StartupTime
67
64
  def benchmark
68
65
  builder.build!
69
66
 
70
- selected_tests.entries.shuffle.each do |id, test|
71
- time(id, test)
67
+ # run a test if:
68
+ #
69
+ # - its interpreter exists
70
+ # - it's a compiled executable (i.e. its compiler exists)
71
+ #
72
+ # otherwise, skip it
73
+ runnable_tests = selected_tests.each_with_object([]) do |(id, test), tests|
74
+ args = Array(test[:command])
75
+
76
+ if args.length == 1 # native executable
77
+ compiler = test[:compiler] || id
78
+ path = File.absolute_path(args.first)
79
+ next unless File.exist?(path)
80
+ else # interpreter + source
81
+ compiler = args.first
82
+ path = which(compiler)
83
+ next unless path
84
+ end
85
+
86
+ tests << {
87
+ id: id,
88
+ test: test,
89
+ args: args,
90
+ compiler: compiler,
91
+ path: path,
92
+ }
93
+ end
94
+
95
+ if runnable_tests.empty?
96
+ puts '[]' if @json
97
+ return
98
+ end
99
+
100
+ spec = @options.spec
101
+
102
+ if spec.type == :duration
103
+ spec = spec.with(value: spec.value.to_f / runnable_tests.length)
104
+ end
105
+
106
+ runnable_tests.shuffle.each do |config|
107
+ config[:spec] = spec
108
+ time(config)
72
109
  end
73
110
 
74
111
  sorted = @times.sort_by { |result| result[:time] }
75
112
 
76
- if @options.format == :json
77
- require 'json'
113
+ if @json
78
114
  puts sorted.to_json
79
- elsif !sorted.empty?
115
+ else
80
116
  pairs = sorted.map { |result| [result[:name], '%.02f' % result[:time]] }
81
117
  table = TTY::Table.new(['Test', 'Time (ms)'], pairs)
82
118
  puts unless @verbosity == :quiet
@@ -84,33 +120,20 @@ module StartupTime
84
120
  end
85
121
  end
86
122
 
87
- # an ASCII table representation of the mapping from test IDs (e.g. "scala")
88
- # to group IDs (e.g. "compiled, jvm, slow")
123
+ # print a JSON or ASCII-table representation of the mapping from test IDs
124
+ # (e.g. "scala") to group IDs (e.g. ["compiled", "jvm", "slow"])
89
125
  def render_ids_to_groups
90
- table = TTY::Table.new(%w[Test Groups], ids_to_groups)
91
- table.render
92
- end
93
-
94
- # takes a test ID and a test spec and measures how long it takes to execute
95
- # the test if either:
96
- #
97
- # - its interpreter exists
98
- # - it's a compiled executable (i.e. its compiler exists)
99
- #
100
- # otherwise, skip the test
101
- def time(id, test)
102
- args = Array(test[:command])
103
-
104
- if args.size == 1 # native executable
105
- compiler = test[:compiler] || id
106
- cmd = File.absolute_path(args.first)
107
- return unless File.exist?(cmd)
108
- else # interpreter + source
109
- compiler = args.first
110
- cmd = which(compiler)
111
- return unless cmd
126
+ if @json
127
+ puts Registry.ids_to_groups(format: :json).to_json
128
+ else
129
+ table = TTY::Table.new(%w[Test Groups], Registry.ids_to_groups)
130
+ puts table.render
112
131
  end
132
+ end
113
133
 
134
+ # takes a test configuration and measures how long it takes to execute the
135
+ # test
136
+ def time(id:, test:, args:, compiler:, path:, spec:)
114
137
  # dump the compiler/interpreter's version if running in verbose mode
115
138
  if @verbosity == :verbose
116
139
  puts
@@ -133,7 +156,7 @@ module StartupTime
133
156
  end
134
157
 
135
158
  argv0 = args.shift
136
- command = [cmd, *args]
159
+ command = [path, *args]
137
160
 
138
161
  unless @verbosity == :quiet
139
162
  if @verbosity == :verbose
@@ -160,9 +183,26 @@ module StartupTime
160
183
  # the bundler environment slows down ruby and breaks truffle-ruby,
161
184
  # so make sure it's disabled for the benchmark
162
185
  Bundler.with_clean_env do
163
- @options.rounds.times do
164
- times << Benchmark.realtime do
165
- system([cmd, argv0], *args, out: File::NULL)
186
+ if spec.type == :duration # how long to run the tests for
187
+ duration = spec.value
188
+ elapsed = 0
189
+ start = Time.now
190
+
191
+ loop do
192
+ time = Benchmark.realtime do
193
+ system([path, argv0], *args, out: File::NULL)
194
+ end
195
+
196
+ elapsed = Time.now - start
197
+ times << time
198
+
199
+ break if elapsed >= duration
200
+ end
201
+ else # how many times to run the tests
202
+ spec.value.times do
203
+ times << Benchmark.realtime do
204
+ system([path, argv0], *args, out: File::NULL)
205
+ end
166
206
  end
167
207
  end
168
208
  end
@@ -108,12 +108,12 @@ module StartupTime
108
108
  # absolute path (which may be mocked)
109
109
  test = test.merge(compiler: compiler_path)
110
110
 
111
- # pass the test object as the block's second argument. Rake passes an
112
- # instance of +Rake::TaskArguments+, a Hash-like object which provides
113
- # access to the command-line arguments for a Rake task e.g. { name:
114
- # "world" } for `rake greet[world]`. since we're not relying on Rake's
115
- # limited option-handling support, we have no use for that here, so we
116
- # simply replace it with the test data.
111
+ # pass the test object as the `file(...) { ... }` block's second
112
+ # argument. Rake passes an instance of +Rake::TaskArguments+, a Hash-like
113
+ # object which provides access to the command-line arguments for a Rake
114
+ # task e.g. { name: "world" } for `rake greet[world]`. since we're not
115
+ # relying on Rake's limited option-handling support, we have no use for
116
+ # that here, so we simply replace it with the test data.
117
117
  wrapper = ->(task, _) { yield(task, test) }
118
118
 
119
119
  # declare the prerequisites for the target file.
@@ -138,8 +138,8 @@ module StartupTime
138
138
  end
139
139
 
140
140
  # ensure each file in the source directory is mirrored to the build
141
- # directory, and add each task which ensures this as a prerequisite
142
- # of the master task (:build)
141
+ # directory, and add each task which ensures this as a prerequisite of the
142
+ # master task (:build)
143
143
  def copy_source_files
144
144
  Dir["#{SRC_DIR}/*.*"].each do |path|
145
145
  filename = File.basename(path)
@@ -153,7 +153,7 @@ module StartupTime
153
153
  end
154
154
 
155
155
  # run a shell command (string) by substituting the compiler path, source
156
- # file, and target file into the supplied template and executing the
156
+ # file, and target file into the supplied template string and executing the
157
157
  # resulting command with the test's (optional) environment hash
158
158
  def run(template, task, test)
159
159
  replacements = {
@@ -192,11 +192,12 @@ module StartupTime
192
192
  end
193
193
 
194
194
  if java_native
195
- javac = compile_if(:javac, force: true) do |task, test|
195
+ javac = compile_if(:javac, connect: false, force: true) do |task, test|
196
196
  run('%{compiler} -d . %{source}', task, test)
197
197
  end
198
198
 
199
199
  if javac
200
+ task java_native => javac
200
201
  task :build => java_native
201
202
  end
202
203
  else
@@ -2,13 +2,17 @@
2
2
 
3
3
  require 'env_paths'
4
4
  require 'optparse'
5
+ require 'values'
5
6
 
6
7
  module StartupTime
7
8
  # StartupTime::Options - a struct-like interface to the app options set or
8
9
  # overridden on the command line
9
10
  class Options
10
11
  BUILD_DIR = EnvPaths.get('startup-time', suffix: false).cache
11
- ROUNDS = 10
12
+ DEFAULT_DURATION = 5
13
+ MINIMUM_DURATION = 2
14
+
15
+ Spec = Value.new(:type, :value)
12
16
 
13
17
  attr_reader :action, :build_dir, :format, :rounds, :verbosity
14
18
 
@@ -17,14 +21,24 @@ module StartupTime
17
21
  def initialize(args)
18
22
  @action = :benchmark
19
23
  @build_dir = BUILD_DIR
24
+ @duration = DEFAULT_DURATION
20
25
  @format = :default
21
26
  @parser = nil
22
- @rounds = ROUNDS
27
+ @rounds = nil
28
+ @spec = nil
23
29
  @verbosity = :default
24
30
 
25
31
  parse! args
26
32
  end
27
33
 
34
+ def spec
35
+ @spec ||= if @rounds
36
+ Spec.with(type: :count, value: @rounds)
37
+ else
38
+ Spec.with(type: :duration, value: @duration)
39
+ end
40
+ end
41
+
28
42
  # the usage message (string) generated by the option parser for this tool
29
43
  def usage
30
44
  @parser.to_s
@@ -41,7 +55,7 @@ module StartupTime
41
55
  '--count',
42
56
  '--rounds INTEGER',
43
57
  Integer,
44
- "The number of times to execute each test (default: #{ROUNDS})"
58
+ 'The number of times to run each program'
45
59
  ) do |value|
46
60
  @rounds = value
47
61
  end
@@ -94,7 +108,7 @@ module StartupTime
94
108
  '-o',
95
109
  '--only LIST',
96
110
  Array, # comma-separated strings
97
- 'Only execute the specified tests (comma-separated list of IDs/groups)'
111
+ 'Only run the specified tests (comma-separated list of IDs/groups)'
98
112
  ) do |values|
99
113
  values.each { |value| registry.only(value.strip) }
100
114
  end
@@ -103,7 +117,7 @@ module StartupTime
103
117
  '-O',
104
118
  '--omit LIST',
105
119
  Array, # comma-separated strings
106
- "Don't execute the specified tests (comma-separated list of IDs/groups)"
120
+ "Don't run the specified tests (comma-separated list of IDs/groups)"
107
121
  ) do |values|
108
122
  values.each { |value| registry.omit(value.strip) }
109
123
  end
@@ -116,6 +130,16 @@ module StartupTime
116
130
  @verbosity = :quiet
117
131
  end
118
132
 
133
+ opts.on(
134
+ '-t',
135
+ '--time INTEGER',
136
+ Integer,
137
+ 'Specify the minimum number of seconds to run tests for',
138
+ "(minimum: #{MINIMUM_DURATION}, default: #{DEFAULT_DURATION})"
139
+ ) do |value|
140
+ @duration = [value, MINIMUM_DURATION].max
141
+ end
142
+
119
143
  opts.on(
120
144
  '-v',
121
145
  '--verbose',
@@ -4,7 +4,6 @@ require 'active_support'
4
4
  require 'active_support/core_ext/hash/indifferent_access'
5
5
  require 'active_support/core_ext/hash/slice' # XXX in core since 2.5
6
6
  require 'set'
7
- require 'shellwords'
8
7
  require 'yaml'
9
8
 
10
9
  module StartupTime
@@ -35,10 +34,16 @@ module StartupTime
35
34
  end
36
35
  end
37
36
 
38
- # returns a hash which maps test-ID keys (e.g. "scala") to their
39
- # corresponding group names (e.g. "compiled, jvm, slow")
40
- def self.ids_to_groups
41
- TESTS.entries.map { |id, test| [id, test[:groups].sort.join(', ')] }
37
+ # a hash which maps test IDs (e.g. "scala") to their corresponding group
38
+ # names (e.g. ["compiled", "jvm", "slow"])
39
+ def self.ids_to_groups(format: :ascii)
40
+ if format == :json
41
+ TESTS.entries.each_with_object({}) do |(id, test), target|
42
+ target[id] = test[:groups].sort
43
+ end
44
+ else # ASCII
45
+ TESTS.entries.map { |id, test| [id, test[:groups].sort.join(', ')] }
46
+ end
42
47
  end
43
48
 
44
49
  def initialize
@@ -8,10 +8,6 @@ module StartupTime
8
8
  # the component responsible for managing the build directory
9
9
  once(:builder) { Builder.new }
10
10
 
11
- # a hash which maps test IDs (e.g. "scala") to group names
12
- # (e.g. "compiled, jvm, slow")
13
- once(:ids_to_groups) { Registry.ids_to_groups }
14
-
15
11
  # an interface to the tests configured in resources/tests.yaml
16
12
  once(:registry) { Registry.new }
17
13
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module StartupTime
4
- VERSION = '1.1.1'
4
+ VERSION = '1.2.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: startup-time
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - chocolateboy
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-07-12 00:00:00.000000000 Z
11
+ date: 2019-07-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -122,6 +122,20 @@ dependencies:
122
122
  - - "~>"
123
123
  - !ruby/object:Gem::Version
124
124
  version: 0.4.0
125
+ - !ruby/object:Gem::Dependency
126
+ name: values
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '1.8'
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '1.8'
125
139
  - !ruby/object:Gem::Dependency
126
140
  name: wireless
127
141
  requirement: !ruby/object:Gem::Requirement