diff --git a/.gitignore b/.gitignore index 959f01ca..2e1dfacf 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,4 @@ vendor/bundle *.swp *.swo +.ruby-version diff --git a/.travis.yml b/.travis.yml index dd53fd3b..93d6ba22 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,7 +22,7 @@ before_install: tar -xvf $PWD/travis_phantomjs/phantomjs-2.1.1-linux-x86_64.tar.bz2 -C $PWD/travis_phantomjs; fi - "export DISPLAY=:99.0" - - "sh -e /etc/init.d/xvfb start" + - gem install bundler:1.17.3 script: - bundle exec rubocop - bundle exec rake test @@ -41,5 +41,14 @@ after_success: fi addons: postgresql: "9.3" + apt: + packages: + - postgresql-9.3 + - postgresql-client-9.3 + - postgresql-contrib-9.3 services: - redis-server + - xvfb +env: + global: + - PGPORT=5433 diff --git a/app/models/repo.rb b/app/models/repo.rb index 666b68a5..0993e8eb 100644 --- a/app/models/repo.rb +++ b/app/models/repo.rb @@ -17,20 +17,71 @@ def generate_sparkline_data charts = {} - self.benchmark_types.map do |benchmark_type| - benchmark_type.benchmark_result_types.each do |benchmark_result_type| - benchmark_runs = BenchmarkRun.select(:initiator_id, :result, :initiator_type).fetch_commit_benchmark_runs( - benchmark_type.category, - benchmark_result_type, - 2000 - ) - - runs = benchmark_runs.sort_by { |run| run.initiator.created_at } - chart_builder = ChartBuilder.new(runs, benchmark_result_type).build_columns - - charts[benchmark_type.category] ||= [] - charts[benchmark_type.category] << { - benchmark_result_type: benchmark_result_type.name, + query = <<~SQL + WITH min_max_dates AS ( + SELECT MIN(date_trunc('week', created_at)) AS start_week, + MAX(date_trunc('week', created_at)) AS end_week + FROM ( + SELECT created_at + FROM commits + WHERE repo_id = #{self.id} + ) AS subq + ), + weeks AS ( + SELECT generate_series(start_week, end_week, '7 days') AS weekstart + FROM min_max_dates + ) + SELECT + benchmark_result_type_id, + benchmark_type_id, + hstore_to_json(br.result) AS result, + category + FROM ( + SELECT id, commit_date FROM ( + SELECT + ROW_NUMBER() OVER(PARTITION BY w.weekstart ORDER BY c.created_at) AS row_num, + id, + created_at AS commit_date + FROM weeks w + INNER JOIN commits c + ON w.weekstart = date_trunc('week', c.created_at) AND c.repo_id = #{self.id} + ) x + WHERE row_num = 1 + ) cw + INNER JOIN benchmark_runs br + ON cw.id = br.initiator_id AND br.initiator_type = 'Commit' + INNER JOIN benchmark_types bt + ON bt.id = br.benchmark_type_id AND bt.repo_id = #{self.id} + ORDER BY category, commit_date + SQL + + raw_results = self.class.connection.execute(query).to_a + raw_results.each do |row| + row['result'] = JSON.parse(row['result']) + end + + result_types = BenchmarkResultType + .where(id: raw_results.map { |row| row['benchmark_result_type_id'] }.uniq) + .map { |res_type| [res_type.id, res_type] }.to_h + + results = {} + raw_results.each do |res| + results[res['benchmark_type_id']] ||= {} + results[res['benchmark_type_id']]['category'] = res['category'] + hash = results[res['benchmark_type_id']]['res_types'] ||= {} + arr = hash[result_types[res['benchmark_result_type_id']].name] ||= [] + arr << res['result'] + hash.sort_by { |k| k } + results[res['benchmark_type_id']]['res_types'] = hash.sort_by { |k, v| k }.to_h + end + results.values.each do |res| + category = res['category'] + next unless category + res['res_types'].each do |name, runs| + chart_builder = ChartBuilder.new(runs, nil).build_columns_hash + charts[category] ||= [] + charts[category] << { + benchmark_result_type: name, columns: chart_builder.columns } end diff --git a/app/services/chart_builder.rb b/app/services/chart_builder.rb index a341b600..46a59896 100644 --- a/app/services/chart_builder.rb +++ b/app/services/chart_builder.rb @@ -60,4 +60,29 @@ def build_columns @columns = new_columns self end + + def build_columns_hash + keys = @benchmark_runs.map { |run| run.keys }.flatten.uniq + @benchmark_runs.each do |benchmark_run| + if block_given? + version = yield(benchmark_run) + @categories ||= [] + @categories << version + end + + keys.each do |key| + @columns[key] ||= [] + @columns[key] << benchmark_run[key]&.to_f + end + end + + new_columns = [] + + @columns.each do |name, data| + new_columns << { name: name, data: data } + end + + @columns = new_columns + self + end end diff --git a/app/views/organizations/index.html.haml b/app/views/organizations/index.html.haml index 1de20bab..19cbbc0f 100644 --- a/app/views/organizations/index.html.haml +++ b/app/views/organizations/index.html.haml @@ -15,12 +15,15 @@ %td - organization.repos.each do |repo| %ul.list-inline - %li - - if !repo.releases.empty? - = link_to "#{repo.title} #{t('.releases_benchmark')}", + - if !repo.releases.empty? + %li + = link_to t('.releases_benchmark'), releases_path(organization_name: organization.name, repo_name: repo.name) - %li - - if !repo.commits.empty? - = link_to "#{repo.title} #{t('.commits_benchmark')}", + - if !repo.commits.empty? + %li + = link_to t('.commits_benchmark'), commits_path(organization_name: organization.name, repo_name: repo.name), data: { no_turbolink: true } + %li + = link_to t('.overview'), + overview_path(organization_name: organization.name, repo_name: repo.name), data: { no_turbolink: true } diff --git a/config/locales/en.yml b/config/locales/en.yml index 47345831..3d2d2b33 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -44,6 +44,7 @@ en: title: Benchmarks commits_benchmark: Commits Benchmarks releases_benchmark: Releases Benchmarks + overview: Overview repos: select_benchmark: &select_benchmark diff --git a/test/models/repo_test.rb b/test/models/repo_test.rb index 27e9a5de..b6aafb13 100644 --- a/test/models/repo_test.rb +++ b/test/models/repo_test.rb @@ -1,7 +1,109 @@ require 'test_helper' class RepoTest < ActiveSupport::TestCase - # test "the truth" do - # assert true - # end + test '#generate_sparkline_data picks first commit from every week' do + repo = create(:repo) + + mem_res_type = create(:benchmark_result_type, name: 'Memory', unit: 'rss') + ips_res_type = create(:benchmark_result_type, name: 'Ips', unit: 'i/s') + + type1 = create(:benchmark_type, repo: repo, category: 'Array map') + type2 = create(:benchmark_type, repo: repo, category: 'String to_i') + + # Week of Mon 18-05-2020 to Sun 24-05-2020 + c1 = create(:commit, repo: repo, created_at: Time.utc(2020, 5, 24, 1)) + c2 = create(:commit, repo: repo, created_at: Time.utc(2020, 5, 24, 1, 1)) + c3 = create(:commit, repo: repo, created_at: Time.utc(2020, 5, 24, 1, 1, 1)) + + # Week of Mon 25-05-2020 to Sun 31-05-2020 + c4 = create(:commit, repo: repo, created_at: Time.utc(2020, 5, 26)) + c5 = create(:commit, repo: repo, created_at: Time.utc(2020, 5, 29)) + c6 = create(:commit, repo: repo, created_at: Time.utc(2020, 5, 30)) + + # Week of Mon 01-06-2020 to Sun 07-06-2020 + c7 = create(:commit, repo: repo, created_at: Time.utc(2020, 6, 1)) + c8 = create(:commit, repo: repo, created_at: Time.utc(2020, 6, 2)) + c9 = create(:commit, repo: repo, created_at: Time.utc(2020, 6, 7)) + + commits = [c1, c2, c3, c4, c5, c6, c7, c8, c9] + commits.each_with_index do |commit, index| + [type1, type2].each do |type| + create( + :benchmark_run, + initiator_id: commit.id, + initiator_type: 'Commit', + result: { rss_kb: commit.id }, + benchmark_result_type_id: mem_res_type.id, + benchmark_type_id: type.id + ) + create( + :benchmark_run, + initiator_id: commit.id, + initiator_type: 'Commit', + result: { bench_1: commit.id, bench_2: commit.id + 1 }, + benchmark_result_type_id: ips_res_type.id, + benchmark_type_id: type.id + ) + end + end + + # We should pick the first commit from each week. + # Since the commits are spread over a period of 3 + # weeks, we should have 3 commits. These commits + # should be c1, c4 and c7. + # We will then pick the benchmark_runs records whose + # initiator_ids are the commits we picked up earlier. + data = repo.generate_sparkline_data + assert_equal( + data, + 'Array map' => [ + { + benchmark_result_type: 'Ips', + columns: [ + { + name: 'bench_1', + data: [c1.id, c4.id, c7.id] + }, + { + name: 'bench_2', + data: [c1.id + 1, c4.id + 1, c7.id + 1] + } + ], + }, + { + benchmark_result_type: 'Memory', + columns: [ + { + name: 'rss_kb', + data: [c1.id, c4.id, c7.id] + } + ] + } + ], + 'String to_i' => [ + { + benchmark_result_type: 'Ips', + columns: [ + { + name: 'bench_1', + data: [c1.id, c4.id, c7.id] + }, + { + name: 'bench_2', + data: [c1.id + 1, c4.id + 1, c7.id + 1] + } + ], + }, + { + benchmark_result_type: 'Memory', + columns: [ + { + name: 'rss_kb', + data: [c1.id, c4.id, c7.id] + } + ] + } + ] + ) + end end