soperf 0.1.1

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 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