startup-time 1.1.1 → 1.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
  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