soperf 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ M2U1YTRhNGVmMGE0NjYzNjZhNzE1ZjkyZjE5M2YxYTYwYjIyNWM5Ng==
5
+ data.tar.gz: !binary |-
6
+ ZDY1MDliMTZmM2Q4MWE3ZjY5ZWQzM2ZjOTJmYTdiYTllYWE1YmExYg==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ MjZkZTE1OTliMTA4ODQzMWU0MjMzZjQxYWQ1M2ZiODczY2NlYTNlYzE0ZGI0
10
+ NGMzMTZkZDU2OTdiN2E2OTkzNTljYTRkMmRlMGIzOGU1Nzg3MWU3MjY4NWRi
11
+ NjI3ZTM3NTBlNGUxZjg2MzY3ZDUxZjRhNTY1ZDBmYjZhM2RjYjU=
12
+ data.tar.gz: !binary |-
13
+ N2VjNTc1N2E0ZTBkYjJiYjFmYTQ1ZGE0ZmUyOGZlNGIyMzdmNDM0NTQ1N2I4
14
+ Nzg2ZTU5MmE1ZWE3ZDM4NTVlMGEwNzEwNjE1MDk1ZTZhODEwNGRkMTU4NzRm
15
+ NWUyNjBjOTEwYjFmZWVkOGM1YWJjNjRkZTJlZmFjZTViZDJmNGU=
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ /lib/soperf/test.rb
2
+ /gems/*
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/CHANGELOG.md ADDED
@@ -0,0 +1,10 @@
1
+ #SoPerf Changelog
2
+ ##v0.1
3
+
4
+ - Initial Commit
5
+
6
+ **TODO:**
7
+
8
+ - Add support for multiple ranges within a row (start/stop/start)
9
+ - Generally refactor and clean
10
+ - Decide what the heck a 'tick' is
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,21 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ soperf (0.1.1)
5
+ colorize
6
+ highline
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ colorize (0.7.5)
12
+ highline (1.6.21)
13
+ rake (10.3.2)
14
+
15
+ PLATFORMS
16
+ ruby
17
+
18
+ DEPENDENCIES
19
+ bundler (~> 1.7)
20
+ rake (~> 10.0)
21
+ soperf!
data/LICENSE.txt ADDED
@@ -0,0 +1,13 @@
1
+ Copyright (c) 2014 wwboynton@gmail.com
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
4
+ documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
5
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
6
+ persons to whom the Software is furnished to do so, subject to the following conditions:
7
+
8
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
9
+
10
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
11
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
12
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
13
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,20 @@
1
+ # Soperf
2
+
3
+ SoPerf is a parallel performance metric chart rendering gem. It displays start and end times for different processes in a visual chart.
4
+
5
+ ## Usage
6
+
7
+ DATA = {parser: {somedata: ['blah'], start: 500, end: 1133},
8
+ executor: {somedata: ['blah'], start: 30, end: 100},
9
+ grouper: {somedata: ['blah'], start: 300, end: 560},
10
+ merger: {somedata: ['blah'], start: 20, end: 240}}
11
+
12
+ puts SoPerf::Chart.new(DATA).render {width: 120}
13
+
14
+ ##Parameters
15
+
16
+ SoPerf::Chart.render can take optional parameters in the form of a hash
17
+
18
+ color: Specify whether or not to color the chart (defaults to true)
19
+ width: Specify an optionally forced width (defaults to fit to terminal)
20
+ summary: Toggle displaying summary data
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
data/lib/soperf.rb ADDED
@@ -0,0 +1,3 @@
1
+ module SoPerf
2
+ VERSION = "0.1.1"
3
+ end
@@ -0,0 +1,21 @@
1
+ #!/bin/env ruby
2
+ # encoding: utf-8
3
+
4
+ module SoPerf
5
+ module BoxDrawingChars
6
+ TICK_CHAR = '━'
7
+ TICK_ON_LINE_CHAR = '╋'
8
+ TICK_ON_LINE_END_CHAR = '┫'
9
+ TICK_ON_LINE_START_CHAR = '┣'
10
+ LINE_CHAR = '┃'
11
+ TOP_LEFT_CORNER_CHAR = '┏'
12
+ TOP_RIGHT_CORNER_CHAR = '┓'
13
+ BOTTOM_LEFT_CORNER_CHAR = '┗'
14
+ BOTTOM_RIGHT_CORNER_CHAR = '┛'
15
+ BOTTOM_ON_LINE_CHAR = '┻'
16
+ TOP_ON_LINE_CHAR = '┳'
17
+ WHITESPACE_CHAR = ' '
18
+ SINGLE_SPACE_LINE = 'H'
19
+ NEWLINE = "\n"
20
+ end
21
+ end
@@ -0,0 +1,199 @@
1
+ #!/bin/env ruby
2
+ # encoding: utf-8
3
+
4
+ require 'colorize'
5
+ require 'highline'
6
+ require_relative 'row'
7
+ require_relative 'box_drawing_chars'
8
+ require_relative 'terminal_helper'
9
+ require_relative 'time_helper'
10
+ require_relative 'color_helper'
11
+
12
+ module SoPerf
13
+ class Chart
14
+ attr_accessor :data_hash, :duration, :scale
15
+ include BoxDrawingChars
16
+ include TerminalHelper
17
+ include TimeHelper
18
+
19
+ def initialize(data_hash)
20
+ @data_hash = Hash[data_hash.sort_by { |_, v| v[:start] }] #sort the data by start time
21
+ @chart = {}
22
+ set_up_rows
23
+ @duration = data_range(:round) #Find the full range of the data, rounded up
24
+ @longest_name = data_hash.map { |k, v| k.to_s.length }.max #The longest name of the results
25
+ @text_info = @data_hash.map { |k, v| "#{@chart[k].name(true)}: #{translate_time(v[:start])}-#{translate_time(v[:end])}" }
26
+ @forced_width = nil
27
+ end
28
+
29
+ def usable_space #Space for the chart, accounting for printing row names
30
+ @forced_width ? @forced_width : terminal_width - spacer.length - 10
31
+ end
32
+
33
+ def ideal_scale
34
+ (usable_space.to_f)/duration
35
+ end
36
+
37
+ def spacer(i=0)
38
+ i = i.length if i.is_a?(String)
39
+ spacer = ''
40
+ (@longest_name-i).times { spacer<<' ' }
41
+ spacer
42
+ end
43
+
44
+ def data_range(round=:round)
45
+ range = data_hash.map { |_, v| v[:end] }.max
46
+ round==:round ? ((range/10.0).ceil*10) : range
47
+ end
48
+
49
+ def set_up_rows
50
+ ColorPicker.reset_color_index
51
+ data_hash.each { |k, v| @chart[k] = Row.new(k, (v[:start]..v[:end])) }
52
+ end
53
+
54
+ def render(options_hash={})
55
+ options_hash[:color] = true unless options_hash.has_key?(:color)
56
+ @forced_width = options_hash[:width] if options_hash.has_key?(:width)
57
+ show_summary = options_hash.has_key?(:summary) ? options_hash[:summary] : true
58
+ result = ''
59
+ @scale = ideal_scale
60
+ set_up_rows
61
+
62
+ @chart.each_pair { |_, row| result << row.render(duration, scale, scaled_tick_locations) + NEWLINE }
63
+
64
+ rendered = "#{container(result)}\n#{(show_summary ? print_text_info : '')}"
65
+ reset
66
+ options_hash[:color] ? rendered : (rendered.uncolorize)
67
+ end
68
+
69
+ def container(rendered_string)
70
+ plain_string = rendered_string.split("\n")[0].uncolorize
71
+ width = ([plain_string.rindex(TICK_ON_LINE_END_CHAR)||0, (plain_string.rindex(LINE_CHAR)||0)].max)+1
72
+ top = []
73
+ width.times { top << TICK_CHAR }
74
+ top[0] = TOP_LEFT_CORNER_CHAR
75
+ top[-1] = TOP_RIGHT_CORNER_CHAR
76
+ scaled_tick_locations.each { |i| top[i] = TOP_ON_LINE_CHAR if top[i] == TICK_CHAR }
77
+
78
+ bottom = top.dup.join
79
+ top = top.join
80
+ bottom[0], bottom[-1] = BOTTOM_LEFT_CORNER_CHAR, BOTTOM_RIGHT_CORNER_CHAR
81
+ bottom.gsub!(TOP_ON_LINE_CHAR, BOTTOM_ON_LINE_CHAR)
82
+
83
+
84
+ "#{spacer(-1) + top}\n#{add_names(rendered_string)}#{spacer(-1) + bottom}\n#{x_axis_legend}"
85
+ end
86
+
87
+ def closest(arr, val) #returns the index of the closest-matching element of the array
88
+ closest = arr.map { |elem| (elem-val).abs }
89
+ closest.index(closest.min)
90
+ end
91
+
92
+ def tick_locations
93
+ @tick_locations ||=
94
+ (
95
+ result = [] #locations of the ticks
96
+ tick_values = [] #value for every single space on the chart
97
+ (duration+1).times { |i| tick_values<<((data_range.to_f/duration)*i) }
98
+ pretty_tick_values.each { |val| result<<closest(tick_values, val) }
99
+ result
100
+ )
101
+ end
102
+
103
+ def pretty_tick_values
104
+ @pretty_tick_values ||=
105
+ (
106
+ arr = [0]
107
+ (data_range(:round)/tick_freq).times { arr<<arr.last+tick_freq }
108
+ arr
109
+ )
110
+ end
111
+
112
+ def tick_freq
113
+ @tick_freq ||=
114
+ (
115
+ result = 10
116
+ while (result*10)/duration<data_range/5 && result*10 < data_range
117
+ result*=10
118
+ end
119
+ ideal_scale > 1 ? result/=2 : result #If we've upscaled at all, add more tick lines to compensate.
120
+ data_range(:round)/result < usable_space/20 ? result/=2 : result #If we've got too few lines, add more!
121
+ result
122
+ )
123
+ end
124
+
125
+ def scaled_tick_locations
126
+ @scaled_tick_locations ||= tick_locations.map { |val| val*scale }
127
+ end
128
+
129
+ def add_names(rendered_string)
130
+ lines = rendered_string.split("\n")
131
+ result = ''
132
+ @chart.each_with_index do |(k, row), i|
133
+ result<< "#{spacer(row.name)}#{row.name(true)}:#{lines[i]}\n"
134
+ end
135
+ result
136
+ end
137
+
138
+ def x_axis_legend
139
+ x_axis = []
140
+ ticks = tick_locations
141
+ usable_space.times { x_axis<<' ' }
142
+
143
+ odd_tick_locations = scaled_tick_locations.values_at(*scaled_tick_locations.each_index.select { |i| i.even? })
144
+ odd_ticks = ticks.values_at(*ticks.each_index.select { |i| i.even? })
145
+
146
+ odd_tick_locations.each_with_index do |l, i|
147
+ x_axis[l]=odd_ticks[i]
148
+ end
149
+
150
+ x_axis.map! do |val|
151
+ val.is_a?(Integer) ? translate_time(val) : ' '
152
+ end
153
+
154
+ spacer('time')+"#{'time'}:#{fill_in_labels(x_axis)}"
155
+ end
156
+
157
+ def fill_in_labels(arr)
158
+ result = ''
159
+ leave_blank = 0
160
+ arr.each do |val|
161
+ if val == ' ' &&
162
+ if leave_blank>0
163
+ result << ''
164
+ leave_blank-=1
165
+ else
166
+ result<<val
167
+ end
168
+ else
169
+ leave_blank += (str = "#{val}").length-1
170
+ result<<str
171
+ end
172
+ end
173
+ result
174
+ end
175
+
176
+ def print_text_info
177
+ result = spacer(-1)
178
+
179
+ @text_info.length.times do |i|
180
+ if (result.split("\n")[-1] + @text_info[i]).uncolorize.length > usable_space
181
+ result << "\n" + spacer(-1) + @text_info[i] + ", "
182
+ else
183
+ result << "#{@text_info[i]}, "
184
+ end
185
+ end
186
+ result[0..-3]
187
+ end
188
+
189
+ def reset
190
+ @scaled_tick_locations = nil
191
+ @tick_locations = nil
192
+ @tick_freq = nil
193
+ @pretty_tick_values = nil
194
+ @forced_width = nil
195
+ @show_summary = nil
196
+ end
197
+
198
+ end
199
+ end
@@ -0,0 +1,68 @@
1
+ require_relative 'box_drawing_chars'
2
+
3
+ module SoPerf
4
+ class ColorPicker
5
+ include BoxDrawingChars
6
+ @@use_color = true
7
+
8
+ def self.use_color(bool=true)
9
+ @@use_color = bool
10
+ end
11
+
12
+ def self.use_color?
13
+ @@use_color
14
+ end
15
+
16
+ def self.reset_color_index
17
+ @@color_index = -1
18
+ end
19
+
20
+ def self.pick_a_color
21
+ colors = [:cyan, :green, :red, :yellow, :magenta, :light_blue, :light_green, :light_red, :light_yellow]
22
+ @@color_index ||= -1
23
+ @@color_index += 1
24
+ @@color_index =0 if @@color_index>colors.length-1
25
+ colors[@@color_index]
26
+ end
27
+
28
+ def self.colorize(str, color)
29
+ if @@use_color
30
+ str.gsub(
31
+ TICK_CHAR, ColorPicker.color(TICK_CHAR, color)).gsub(
32
+ TICK_ON_LINE_CHAR, ColorPicker.color(TICK_ON_LINE_CHAR, color)).gsub(
33
+ TICK_ON_LINE_START_CHAR, ColorPicker.color(TICK_ON_LINE_START_CHAR, color)).gsub(
34
+ TICK_ON_LINE_END_CHAR, ColorPicker.color(TICK_ON_LINE_END_CHAR, color)).gsub(
35
+ SINGLE_SPACE_LINE, ColorPicker.color(SINGLE_SPACE_LINE, color))
36
+ end
37
+ end
38
+
39
+ def self.color(str, color)
40
+ if @@use_color
41
+ case color
42
+ when :cyan
43
+ str.cyan
44
+ when :green
45
+ str.green
46
+ when :red
47
+ str.red
48
+ when :yellow
49
+ str.yellow
50
+ when :magenta
51
+ str.magenta
52
+ when :light_blue
53
+ str.light_blue
54
+ when :light_green
55
+ str.light_green
56
+ when :light_red
57
+ str.light_red
58
+ when :light_yellow
59
+ str.light_yellow
60
+ else
61
+ str
62
+ end
63
+ else
64
+ str
65
+ end
66
+ end
67
+ end
68
+ end
data/lib/soperf/row.rb ADDED
@@ -0,0 +1,122 @@
1
+ #!/bin/env ruby
2
+ # encoding: utf-8
3
+
4
+ require 'colorize'
5
+ require_relative 'color_helper'
6
+ require_relative 'box_drawing_chars'
7
+ require_relative 'time_helper'
8
+
9
+ module SoPerf
10
+ class Row
11
+ include BoxDrawingChars
12
+ include TimeHelper
13
+ attr_accessor :contents, :color, :raw_range
14
+
15
+ def initialize(name, raw_range, color = ColorPicker.pick_a_color)
16
+ @name = name.to_s
17
+ @color = color
18
+ @raw_range = raw_range
19
+ @use_color = true
20
+ @raw_duration = raw_range.last-raw_range.first
21
+ @pretty_duration = translate_time(@raw_duration)
22
+ end
23
+
24
+ def self.reset_color_index
25
+ @@color_index = -1
26
+ end
27
+
28
+ def point_in_range?(range, int)
29
+ range.member?(int) ? 1 : 0
30
+ end
31
+
32
+ def raw_array(duration, range=raw_range)
33
+ contents = []
34
+ range = (range.first.round..range.last)
35
+ duration.times { |i| contents<<point_in_range?(range, i) }
36
+ contents
37
+ end
38
+
39
+ def scaled_range(scale)
40
+ (raw_range.first*scale..raw_range.last*scale)
41
+ end
42
+
43
+ def name(colored = false)
44
+ colored ? ColorPicker.color(@name, color) : @name
45
+ end
46
+
47
+ def render(duration, scale=1, tick_locations=[], use_color=true)
48
+ range = scaled_range(scale)
49
+ duration = (duration*scale).ceil.to_i
50
+ @use_color = use_color
51
+ arr = raw_array(duration, range)
52
+ translate(arr, tick_locations)
53
+ end
54
+
55
+ def add_duration(str)
56
+ look_ahead_spaces = (@pretty_duration.to_s).length+1
57
+ label = ColorPicker.color(@pretty_duration.to_s, color)
58
+ if str.rindex(TICK_ON_LINE_END_CHAR)
59
+ index = str.rindex(TICK_ON_LINE_END_CHAR)
60
+ char = TICK_ON_LINE_END_CHAR
61
+ elsif str.rindex (SINGLE_SPACE_LINE)
62
+ index = str.rindex (SINGLE_SPACE_LINE)
63
+ char = SINGLE_SPACE_LINE
64
+ else
65
+ return str
66
+ end
67
+
68
+ if /#{char}\s{#{@pretty_duration.length}}/.match(str[index+1..index+look_ahead_spaces])
69
+ return str.gsub /#{char}.{0,#{look_ahead_spaces}}/, "#{char} #{label}"
70
+ else
71
+ last_line_index = str.rindex(LINE_CHAR)
72
+ return str + " #{label}" if last_line_index.nil?
73
+ if str[last_line_index-look_ahead_spaces..last_line_index].index(char)
74
+ str[last_line_index+1] = " #{label}"
75
+ return str
76
+ else
77
+ return str.gsub /#{char}.{0,#{look_ahead_spaces}}/, "#{char} #{label}"
78
+ end
79
+ end
80
+ end
81
+
82
+ def translate(contents, tick_locations)
83
+ tick_locations = tick_locations.values_at(* tick_locations.each_index.select { |i| i.even? })
84
+ str = ''
85
+ contents.each_with_index do |x, i|
86
+ look_ahead = (i==contents.length-1 ? nil : contents[i+1])
87
+ look_behind = (i==0 ? nil : contents[i-1])
88
+
89
+ if x==0
90
+ str<<WHITESPACE_CHAR
91
+ elsif x==1
92
+ if i==0
93
+ str<<TICK_ON_LINE_START_CHAR
94
+ elsif i==contents.length-1
95
+ str<<TICK_ON_LINE_END_CHAR
96
+ elsif look_ahead == look_behind && look_behind == 0
97
+ str<<SINGLE_SPACE_LINE
98
+ elsif look_behind == 0
99
+ str<<TICK_ON_LINE_START_CHAR
100
+ elsif look_ahead == 0
101
+ str<<TICK_ON_LINE_END_CHAR
102
+ else
103
+ str<<TICK_CHAR
104
+ end
105
+ end
106
+ end
107
+
108
+ tick_locations.each do |i|
109
+ str[i]=TICK_ON_LINE_CHAR if str[i]==TICK_CHAR
110
+ str[i]=LINE_CHAR if str[i]==WHITESPACE_CHAR
111
+ end
112
+
113
+ str[0] =TICK_ON_LINE_CHAR if str[0]==TICK_CHAR
114
+ str[0] =LINE_CHAR if str[0]==WHITESPACE_CHAR
115
+ str[-1]=TICK_ON_LINE_CHAR if str[-1]==TICK_CHAR
116
+ str[-1]=LINE_CHAR if str[-1]==WHITESPACE_CHAR
117
+
118
+ result = ColorPicker.colorize(add_duration(str), color)
119
+ @use_color ? result : result.uncolorize
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,7 @@
1
+ module SoPerf
2
+ module TerminalHelper
3
+ def terminal_width
4
+ HighLine::SystemExtensions.terminal_size[0]
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,32 @@
1
+ module SoPerf
2
+ module TimeHelper
3
+ def translate_time(val)
4
+ if val == 0
5
+ 0
6
+ else
7
+ res = val
8
+ if res/1000.0 >= 1
9
+ res = res/1000.0 #to seconds
10
+ if res/60.0 >= 1
11
+ res = res/60.0
12
+ if res/60.0 >= 1
13
+ res = res/60.0
14
+ if res/24 >= 1
15
+ res = res/24
16
+ "#{res.to_s.gsub(/\.0$/, '')}d"
17
+ else
18
+ "#{res.to_s.gsub(/\.0$/, '')}h"
19
+ end
20
+ else
21
+ "#{res.to_s.gsub(/\.0$/, '')}m"
22
+ end
23
+ else
24
+ "#{res.to_s.gsub(/\.0$/, '')}s"
25
+ end
26
+ else
27
+ "#{res.to_s.gsub(/\.0$/, '')}ms"
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
data/soperf.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require './lib/soperf'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "soperf"
8
+ spec.version = SoPerf::VERSION
9
+ spec.authors = ["Wesley Boynton"]
10
+ spec.email = ["wwboynton@gmail.com"]
11
+ spec.licenses = ['MIT']
12
+ spec.summary = %q{Ascii table generator for performance metrics}
13
+ spec.description = %q{SoPerf generates colored ascii charts for displaying parallel performance metrics in the terminal.}
14
+
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_runtime_dependency 'highline'
21
+ spec.add_runtime_dependency 'colorize'
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.7"
24
+ spec.add_development_dependency "rake", "~> 10.0"
25
+ end
@@ -0,0 +1,50 @@
1
+ #!/bin/env ruby
2
+ # encoding: utf-8
3
+
4
+ require_relative '../../lib/soperf/chart'
5
+ require_relative '../../spec/test_charts'
6
+ require 'colorize'
7
+
8
+ TOP_LEFT_CORNER_CHAR = '┏'
9
+ TOP_RIGHT_CORNER_CHAR = '┓'
10
+ LINE_CHAR = '┃'
11
+ TICK_ON_LINE_START_CHAR = '┣'
12
+
13
+ describe 'SoPerf::Chart' do
14
+ before(:all) do
15
+ @test_chart = SoPerf::Chart.new(TestCharts::TEST1)
16
+ end
17
+
18
+ it 'should order entries by start time' do
19
+ expect(@test_chart.data_hash).to eq Hash[TestCharts::TEST1.sort_by { |_, v| v[:start] }]
20
+ end
21
+
22
+ it 'should render uncolorized content' do
23
+ rendered = @test_chart.render color: false
24
+ expect(rendered).to eq rendered.uncolorize
25
+ end
26
+
27
+ it 'should render colorized content' do
28
+ rendered = @test_chart.render color: true
29
+ expect(rendered).to_not eq rendered.uncolorize
30
+ end
31
+
32
+ it 'should render to a specified width' do
33
+ rendered = @test_chart.render width: 80
34
+ first_line = rendered.uncolorize.split("\n")[0]
35
+ max_line_length = first_line.index(TOP_RIGHT_CORNER_CHAR)-first_line.index(TOP_LEFT_CORNER_CHAR)+1
36
+ expect(max_line_length).to eq 80
37
+ end
38
+
39
+ it 'should correctly align row names' do
40
+ rendered = @test_chart.render summary: false
41
+ names = rendered.uncolorize.split("\n")[1..-3].map do |elem|
42
+ index = elem.index(LINE_CHAR)
43
+ if index.nil? || (index && elem.index(TICK_ON_LINE_START_CHAR) && elem.index(TICK_ON_LINE_START_CHAR)<index)
44
+ index = elem.index(TICK_ON_LINE_START_CHAR)
45
+ end
46
+ elem[0..index-1]
47
+ end
48
+ names.each { |name| expect(/.\:/.match(name[-2..-1])).to be_truthy }
49
+ end
50
+ end
@@ -0,0 +1,34 @@
1
+ #!/bin/env ruby
2
+ # encoding: utf-8
3
+
4
+ require_relative '../../lib/soperf/color_helper'
5
+
6
+ describe 'SoPerf::ColorPicker' do
7
+ before(:all) do
8
+ @picker = lambda { SoPerf::ColorPicker.pick_a_color }
9
+ end
10
+
11
+ before(:each) do
12
+ SoPerf::ColorPicker.reset_color_index
13
+ end
14
+
15
+ it 'should not select the same color twice consecutively' do
16
+ color1 = @picker.call
17
+ color2 = @picker.call
18
+ 15.times do
19
+ expect(color1).to_not eq color2
20
+ color1 = color2
21
+ color2 = @picker.call
22
+ end
23
+ end
24
+
25
+ it 'should reset the color index' do
26
+ 5.times do |i|
27
+ SoPerf::ColorPicker.reset_color_index
28
+ color1 = @picker.call
29
+ i.times { @picker.call }
30
+ SoPerf::ColorPicker.reset_color_index
31
+ expect(@picker.call).to eq color1
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,65 @@
1
+ #!/bin/env ruby
2
+ # encoding: utf-8
3
+
4
+ require_relative '../../lib/soperf/row'
5
+ TICK_CHAR = '━'
6
+ LINE_CHAR = '┃'
7
+ TICK_ON_LINE_CHAR = '╋'
8
+ TICK_ON_LINE_END_CHAR = '┫'
9
+ TICK_ON_LINE_START_CHAR = '┣'
10
+ SINGLE_SPACE_LINE = 'H'
11
+
12
+ describe 'SoPerf::Row' do
13
+ before(:all) do
14
+ name = 'test_row'
15
+ raw_range = 734..3549
16
+ tiny_raw_range = 734..745
17
+ @duration = 7140
18
+ @scale = 0.02689075630252101
19
+ @ticks = [0.0, 13.445378151260504, 26.89075630252101,
20
+ 40.33613445378152, 53.78151260504202,
21
+ 67.22689075630252, 80.67226890756304,
22
+ 94.11764705882354, 107.56302521008404,
23
+ 121.00840336134455, 134.45378151260505,
24
+ 147.89915966386556, 161.34453781512607,
25
+ 174.78991596638656, 188.23529411764707]
26
+ @row = SoPerf::Row.new(name, raw_range)
27
+ @tiny_row = SoPerf::Row.new(name, tiny_raw_range)
28
+ end
29
+
30
+ it 'should render uncolorized content' do
31
+ rendered = @row.render(@duration, @scale, @ticks, false)
32
+ expect(rendered).to eq rendered.uncolorize
33
+ end
34
+
35
+ it 'should render colorized content' do
36
+ rendered = @row.render(@duration, @scale, @ticks, true)
37
+ expect(rendered).to_not eq rendered.uncolorize
38
+ end
39
+
40
+ it 'should render with tick lines' do
41
+ rendered = @row.render(@duration, @scale, @ticks).uncolorize
42
+ expect(rendered.scan(LINE_CHAR).length>3).to be true
43
+ expect(rendered.index(TICK_ON_LINE_CHAR)).to_not be nil
44
+ end
45
+
46
+ it 'should render without tick lines' do
47
+ rendered = @row.render(@duration, @scale).uncolorize
48
+ expect(rendered.scan(LINE_CHAR).length==2).to be true
49
+ expect(rendered.index(TICK_ON_LINE_CHAR)).to be nil
50
+ end
51
+
52
+ context 'should translate all binary representations into utf-8 symbols' do
53
+ it 'with a long graph line' do
54
+ rendered = @row.render(@duration, @scale).uncolorize
55
+ matches = /^#{LINE_CHAR}\s*#{TICK_ON_LINE_START_CHAR}#{TICK_CHAR}*#{TICK_ON_LINE_END_CHAR}\s\b.*\b\s*#{LINE_CHAR}$/.match(rendered)
56
+ expect(matches.to_a.length==1).to be true
57
+ end
58
+ it 'with a one-character graph line' do
59
+
60
+ rendered = @tiny_row.render(@duration, @scale).uncolorize
61
+ matches = /^#{LINE_CHAR}\s*#{SINGLE_SPACE_LINE}\s\b.*\b\s*#{LINE_CHAR}$/.match(rendered)
62
+ expect(matches.to_a.length==1).to be true
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,28 @@
1
+ #!/bin/env ruby
2
+ # encoding: utf-8
3
+
4
+ require_relative '../../lib/soperf/time_helper'
5
+
6
+ describe 'SoPerf::TimeHelper' do
7
+ include SoPerf::TimeHelper
8
+
9
+ it 'should leave milliseconds under 1000 alone' do
10
+ expect(translate_time(500)).to eq '500ms'
11
+ end
12
+
13
+ it 'should translate ms over 1000 into seconds' do
14
+ expect(translate_time(1000)).to eq '1s'
15
+ end
16
+
17
+ it 'should translate ms greater than or equal to 60000 into minutes' do
18
+ expect(translate_time(60000)).to eq '1m'
19
+ end
20
+
21
+ it 'should translate ms greater than or equal to 3600000 into hours' do
22
+ expect(translate_time(3600000)).to eq '1h'
23
+ end
24
+
25
+ it 'should translate ms greater than or equal to 86400000 into days' do
26
+ expect(translate_time(86400000)).to eq '1d'
27
+ end
28
+ end
@@ -0,0 +1,89 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # The generated `.rspec` file contains `--require spec_helper` which will cause this
4
+ # file to always be loaded, without a need to explicitly require it in any files.
5
+ #
6
+ # Given that it is always loaded, you are encouraged to keep this file as
7
+ # light-weight as possible. Requiring heavyweight dependencies from this file
8
+ # will add to the boot time of your test suite on EVERY test run, even for an
9
+ # individual file that may not need all of that loaded. Instead, consider making
10
+ # a separate helper file that requires the additional dependencies and performs
11
+ # the additional setup, and require it from the spec files that actually need it.
12
+ #
13
+ # The `.rspec` file also contains a few flags that are not defaults but that
14
+ # users commonly want.
15
+ #
16
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
17
+ RSpec.configure do |config|
18
+ # rspec-expectations config goes here. You can use an alternate
19
+ # assertion/expectation library such as wrong or the stdlib/minitest
20
+ # assertions if you prefer.
21
+ config.expect_with :rspec do |expectations|
22
+ # This option will default to `true` in RSpec 4. It makes the `description`
23
+ # and `failure_message` of custom matchers include text for helper methods
24
+ # defined using `chain`, e.g.:
25
+ # be_bigger_than(2).and_smaller_than(4).description
26
+ # # => "be bigger than 2 and smaller than 4"
27
+ # ...rather than:
28
+ # # => "be bigger than 2"
29
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
30
+ end
31
+
32
+ # rspec-mocks config goes here. You can use an alternate test double
33
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
34
+ config.mock_with :rspec do |mocks|
35
+ # Prevents you from mocking or stubbing a method that does not exist on
36
+ # a real object. This is generally recommended, and will default to
37
+ # `true` in RSpec 4.
38
+ mocks.verify_partial_doubles = true
39
+ end
40
+
41
+ # The settings below are suggested to provide a good initial experience
42
+ # with RSpec, but feel free to customize to your heart's content.
43
+ =begin
44
+ # These two settings work together to allow you to limit a spec run
45
+ # to individual examples or groups you care about by tagging them with
46
+ # `:focus` metadata. When nothing is tagged with `:focus`, all examples
47
+ # get run.
48
+ config.filter_run :focus
49
+ config.run_all_when_everything_filtered = true
50
+
51
+ # Limits the available syntax to the non-monkey patched syntax that is recommended.
52
+ # For more details, see:
53
+ # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
54
+ # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
55
+ # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
56
+ config.disable_monkey_patching!
57
+
58
+ # This setting enables warnings. It's recommended, but in some cases may
59
+ # be too noisy due to issues in dependencies.
60
+ config.warnings = true
61
+
62
+ # Many RSpec users commonly either run the entire suite or an individual
63
+ # file, and it's useful to allow more verbose output when running an
64
+ # individual spec file.
65
+ if config.files_to_run.one?
66
+ # Use the documentation formatter for detailed output,
67
+ # unless a formatter has already been configured
68
+ # (e.g. via a command-line flag).
69
+ config.default_formatter = 'doc'
70
+ end
71
+
72
+ # Print the 10 slowest examples and example groups at the
73
+ # end of the spec run, to help surface which specs are running
74
+ # particularly slow.
75
+ config.profile_examples = 10
76
+
77
+ # Run specs in random order to surface order dependencies. If you find an
78
+ # order dependency and want to debug it, you can fix the order by providing
79
+ # the seed, which is printed after each run.
80
+ # --seed 1234
81
+ config.order = :random
82
+
83
+ # Seed global randomization in this process using the `--seed` CLI option.
84
+ # Setting this allows you to use `--seed` to deterministically reproduce
85
+ # test failures related to randomization by passing the same `--seed` value
86
+ # as the one that triggered the failure.
87
+ Kernel.srand config.seed
88
+ =end
89
+ end
@@ -0,0 +1,7 @@
1
+ module TestCharts
2
+ TEST1 = {parser: {start: 0, end: 7133},
3
+ executor: {start: rand(0..1000), end: rand(150+11..1100)},
4
+ grouper: {start: rand(0..1000), end: 1000},
5
+ merger: {start: rand(0..1000), end: rand(10+11..5100)},
6
+ parser12: {start: rand(0..1000), end: rand(10+11..5100)}}
7
+ end
metadata ADDED
@@ -0,0 +1,129 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: soperf
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Wesley Boynton
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-01-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: highline
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ! '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ! '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: colorize
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ! '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '1.7'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '1.7'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '10.0'
69
+ description: SoPerf generates colored ascii charts for displaying parallel performance
70
+ metrics in the terminal.
71
+ email:
72
+ - wwboynton@gmail.com
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - .gitignore
78
+ - .rspec
79
+ - CHANGELOG.md
80
+ - Gemfile
81
+ - Gemfile.lock
82
+ - LICENSE.txt
83
+ - README.md
84
+ - Rakefile
85
+ - lib/soperf.rb
86
+ - lib/soperf/box_drawing_chars.rb
87
+ - lib/soperf/chart.rb
88
+ - lib/soperf/color_helper.rb
89
+ - lib/soperf/row.rb
90
+ - lib/soperf/terminal_helper.rb
91
+ - lib/soperf/time_helper.rb
92
+ - soperf.gemspec
93
+ - spec/lib/chart_spec.rb
94
+ - spec/lib/color_picker_spec.rb
95
+ - spec/lib/row_spec.rb
96
+ - spec/lib/time_helper_spec.rb
97
+ - spec/spec_helper.rb
98
+ - spec/test_charts.rb
99
+ homepage:
100
+ licenses:
101
+ - MIT
102
+ metadata: {}
103
+ post_install_message:
104
+ rdoc_options: []
105
+ require_paths:
106
+ - lib
107
+ required_ruby_version: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ! '>='
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ required_rubygems_version: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ! '>='
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ requirements: []
118
+ rubyforge_project:
119
+ rubygems_version: 2.2.2
120
+ signing_key:
121
+ specification_version: 4
122
+ summary: Ascii table generator for performance metrics
123
+ test_files:
124
+ - spec/lib/chart_spec.rb
125
+ - spec/lib/color_picker_spec.rb
126
+ - spec/lib/row_spec.rb
127
+ - spec/lib/time_helper_spec.rb
128
+ - spec/spec_helper.rb
129
+ - spec/test_charts.rb