29 Commits

Author SHA1 Message Date
39f95575da Version bump to v0.2.1
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
ci/woodpecker/release/woodpecker Pipeline was successful
2025-08-13 14:57:45 -04:00
6e1ab89209 Updating version number in appinfo
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2025-08-13 14:57:19 -04:00
9cd6c78741 Adding missing step from Docker dev
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2025-08-13 14:54:02 -04:00
5b730df803 Adding build badge to README
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2025-08-13 14:52:34 -04:00
0ce4a3ecee Updated README
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2025-08-13 14:46:38 -04:00
12ece12394 Fixed some lints
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2025-08-13 14:13:26 -04:00
05c20b5811 Adding a model association test
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2025-08-13 11:06:55 -04:00
bd822664b0 Added some model tests
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2025-08-13 09:57:04 -04:00
3f0efce0d8 Fixed a few lints; changed rake task name to test:lint
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2025-08-12 23:56:46 -04:00
eeedc57cd3 Overhauled configuration so that it's a bit more useful in more spots; configuration now properly loads an environment config as well as defaults; updated some woodpecker config
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2025-08-12 23:47:38 -04:00
164eea1bde Updated Woodpecker config for 3.x
All checks were successful
ci/woodpecker/manual/woodpecker Pipeline was successful
2025-08-12 21:59:59 -04:00
85fe3b0b38 Removing broken ruby test versions for now
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2025-08-12 21:55:03 -04:00
519955e57a Fixing unit tests
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2025-08-12 21:30:17 -04:00
96b746822c Fixing unit tests
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2025-08-12 21:27:17 -04:00
eac833fd6d Adding db:migrate step to tests; adding two new versions of Ruby to test against
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2025-08-12 20:29:57 -04:00
ad391c84a4 Fixing ruby 3.4 test
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2025-08-12 20:23:50 -04:00
641c9315bc Adding Ruby 3.4 testing to Woodpecker
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2025-08-12 20:03:20 -04:00
3a136865b0 Added some more tests; changed URLs for model list pages and added redirects
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2025-08-12 18:22:22 -04:00
Gregory Ballantine
f40d69a98d Using before hook for index route
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2025-08-12 16:26:48 -04:00
Gregory Ballantine
40cfdcc2a3 Changed naming from Routes to Controllers; fixed some Sinatra modular layout stuff; added RSpec for testing and some basic tests
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2025-08-12 16:15:43 -04:00
Gregory Ballantine
260d0d1268 Added rspec testing. It should work, but doesn't for unknown reasons 2025-08-12 15:35:46 -04:00
Gregory Ballantine
e1f5bd3950 Updating to Sinatra 4.1
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2025-08-12 14:27:22 -04:00
Gregory Ballantine
1f0c481105 Refactored app to more explicitly require gems/modules that are used per-file
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2025-08-12 14:12:32 -04:00
Gregory Ballantine
dd8e419e52 Switched over to a modular Sinatra app layout
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2025-08-12 13:54:25 -04:00
Gregory Ballantine
c74ca114d8 Fixed a logic error with removing benchmarks from a test; cleaned up some linter errors
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2025-08-12 12:19:25 -04:00
0a1037e79a Modified the front-end to display averaged results up to two decimals
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2025-08-11 23:39:01 -04:00
Gregory Ballantine
bc5ae4962f Fixed the placeholder for benchmark add/edit pages
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2025-07-31 14:07:58 -04:00
Gregory Ballantine
ec2bf45a6e Fixed the test edit page
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2025-07-31 14:06:08 -04:00
57163b10e4 The report test selection resets when you change the benchmark
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2025-07-30 23:29:53 -04:00
43 changed files with 602 additions and 109 deletions

3
.gitignore vendored
View File

@@ -65,3 +65,6 @@ node_modules/
# Compiled assets
public/css/
public/js/
# Ignore production configuration files to protect against credential leaks
config/production.yaml

View File

@@ -1,4 +1,6 @@
require: rubocop-sequel
plugins:
- rubocop-rspec
- rubocop-sequel
AllCops:
NewCops: enable

View File

@@ -1,11 +1,32 @@
pipeline:
steps:
setup:
image: ruby:3.4
env:
RACK_ENV: testing
commands:
- gem install rake
- bundle config set --local path "vendor/bundle"
- bundle install
- rake db:migrate
test_ruby34:
image: ruby:3.4
env:
RACK_ENV: testing
commands:
- gem install rake
- bundle config set --local path "vendor/bundle"
- rake test:unit
group: tests
style:
image: ruby:3.4
env:
RACK_ENV: testing
commands:
- 'gem install rake'
- 'bundle config set --local path "vendor/bundle"'
- 'bundle install'
- 'rake test:rubocop'
- gem install rake
- bundle config set --local path "vendor/bundle"
- rake test:lint
gitea_release:
image: plugins/gitea-release
@@ -15,5 +36,5 @@ pipeline:
base_url: https://git.metaunix.net
title: "${CI_COMMIT_TAG}"
when:
event: tag
event:
- tag

11
Gemfile
View File

@@ -1,7 +1,7 @@
source 'https://rubygems.org'
gem 'sinatra', '~> 3.2'
gem 'sinatra-contrib', '~> 3.2'
gem 'sinatra', '~> 4.1'
gem 'sinatra-contrib', '~> 4.1'
gem 'puma', '~> 6.6'
gem 'sequel', '~> 5.92'
@@ -15,5 +15,12 @@ group :development, :test do
# rubocop and extensions for code style
gem 'rubocop'
gem 'rubocop-rspec'
gem 'rubocop-sequel'
end
group :test do
gem 'rspec'
gem 'rack-test'
gem 'database_cleaner-sequel'
end

View File

@@ -4,6 +4,11 @@ GEM
ast (2.4.3)
base64 (0.3.0)
bigdecimal (3.2.2)
database_cleaner-core (2.0.1)
database_cleaner-sequel (2.0.2)
database_cleaner-core (~> 2.0.0)
sequel
diff-lcs (1.6.2)
ffi (1.17.2-aarch64-linux-gnu)
ffi (1.17.2-aarch64-linux-musl)
ffi (1.17.2-arm-linux-gnu)
@@ -33,10 +38,16 @@ GEM
puma (6.6.0)
nio4r (~> 2.0)
racc (1.8.1)
rack (2.2.17)
rack-protection (3.2.0)
rack (3.2.0)
rack-protection (4.1.1)
base64 (>= 0.1.0)
rack (~> 2.2, >= 2.2.4)
logger (>= 1.6.0)
rack (>= 3.0.0, < 4)
rack-session (2.1.1)
base64 (>= 0.1.0)
rack (>= 3.0.0)
rack-test (2.2.0)
rack (>= 1.3)
rainbow (3.1.1)
rb-fsevent (0.11.2)
rb-inotify (0.11.1)
@@ -44,6 +55,19 @@ GEM
regexp_parser (2.10.0)
rerun (0.14.0)
listen (~> 3.0)
rspec (3.13.1)
rspec-core (~> 3.13.0)
rspec-expectations (~> 3.13.0)
rspec-mocks (~> 3.13.0)
rspec-core (3.13.5)
rspec-support (~> 3.13.0)
rspec-expectations (3.13.5)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0)
rspec-mocks (3.13.5)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0)
rspec-support (3.13.4)
rubocop (1.76.1)
json (~> 2.3)
language_server-protocol (~> 3.17.0.2)
@@ -58,6 +82,9 @@ GEM
rubocop-ast (1.45.1)
parser (>= 3.3.7.2)
prism (~> 1.4)
rubocop-rspec (3.6.0)
lint_roller (~> 1.1)
rubocop (~> 1.72, >= 1.72.1)
rubocop-sequel (0.4.1)
lint_roller (~> 1.1)
rubocop (>= 1.72.1, < 2)
@@ -65,16 +92,18 @@ GEM
ruby2_keywords (0.0.5)
sequel (5.93.0)
bigdecimal
sinatra (3.2.0)
sinatra (4.1.1)
logger (>= 1.6.0)
mustermann (~> 3.0)
rack (~> 2.2, >= 2.2.4)
rack-protection (= 3.2.0)
rack (>= 3.0.0, < 4)
rack-protection (= 4.1.1)
rack-session (>= 2.0.0, < 3)
tilt (~> 2.0)
sinatra-contrib (3.2.0)
sinatra-contrib (4.1.1)
multi_json (>= 0.0.2)
mustermann (~> 3.0)
rack-protection (= 3.2.0)
sinatra (= 3.2.0)
rack-protection (= 4.1.1)
sinatra (= 4.1.1)
tilt (~> 2.0)
sqlite3 (2.7.0-aarch64-linux-gnu)
sqlite3 (2.7.0-aarch64-linux-musl)
@@ -104,14 +133,18 @@ PLATFORMS
x86_64-linux-musl
DEPENDENCIES
database_cleaner-sequel
logger
puma (~> 6.6)
rack-test
rerun
rspec
rubocop
rubocop-rspec
rubocop-sequel
sequel (~> 5.92)
sinatra (~> 3.2)
sinatra-contrib (~> 3.2)
sinatra (~> 4.1)
sinatra-contrib (~> 4.1)
sqlite3 (~> 2.6)
BUNDLED WITH

View File

@@ -1,5 +1,7 @@
# Game Data
![Build badge](https://builds.metaunix.net/api/badges/84/status.svg)
Web-based tool to store and organize PC hardware gaming benchmarks.
## Project Goals
@@ -7,28 +9,58 @@ Web-based tool to store and organize PC hardware gaming benchmarks.
The goals of this project are to:
* Record benchmarking results from multiple devices - e.g. log from a laptop or a phone.
* Group results into tests - it's good practice to run a benchmark multiple times for accuracy.
* Group results into tests to keep track of different testing configurations.
* Encourage running tests multiple times - it's good practice to run a benchmark multiple times for accuracy.
* Create comparisons of hardware tests to compare performance.
* Generate graphs of hardware comparisons for usage in videos and articles.
## Requirements
Game Data runs on Ruby, and takes advantage of Bundler to manage code dependencies and Rake to run various tasks for maintaining the app. You can install them globally like so:
Game Data runs on Ruby, and takes advantage of [Bundler](https://bundler.io/) to manage code dependencies and [Rake](https://ruby.github.io/rake/) to run various tasks for maintaining the app. You can install them globally like so:
Debian/Ubuntu: `apt install -y ruby ruby-bundler rake`
RedHat and clones: `dnf install -y ruby rubygem-bundler rubygem-rake`
## Production Deployment
**TBD**
## Development
Install dependencies via bundler:
### Via Docker
If you'd prefer not to install dependencies and such to your local OS, you can do the development via Docker. The scripts provided in `bin/` will build Docker images for running the Ruby app and building the front-end assets via Gulp. *Both containers will automatically watch for changes.*
**Note:** Using the scripts below, the Docker images will remove themselves when stopped. This is to make clean up a bit more streamlined.
1. [Install Docker](https://docs.docker.com/engine/install/) for your OS.
2. Build the docker images for Ruby and Gulp:
`bin/docker-build.sh`
3. Run the docker images:
`bin/docker-run.sh`
4. If everything is running successfully you can open your browser and go to https://localhost:9292.
### Local/Native Development
1. Install dependencies via bundler:
`bundle install`
Perform database migrations:
2. Perform database migrations:
`rake db:migrate`
Run the server in development with auto-reloading:
3. Run the server in development with auto-reloading:
`rake server:dev`
If everything is running successfully you can open your browser and go to https://localhost:9292.
4. If everything is running successfully you can open your browser and go to https://localhost:9292.
## License
This project is available under the BSD 2-Clause license.

View File

@@ -3,18 +3,22 @@ require 'bundler/setup'
namespace :db do
desc 'Run migrations'
task :migrate, [:version] do |t, args|
require "sequel/core"
require 'sequel/core'
# load configuration
require_relative 'src/config'
conf = Config.new()
Sequel.extension :migration
version = args[:version].to_i if args[:version]
Sequel.connect('sqlite://data/gamedata.db') do |db|
Sequel::Migrator.run(db, "db/migrations", target: version)
Sequel.connect(adapter: conf.get('database.adapter'), database: conf.get('database.database')) do |db|
Sequel::Migrator.run(db, 'db/migrations', target: version)
end
end
end
namespace :server do
task :start do
ENV['APP_ENV'] = 'production'
ENV['RACK_ENV'] = 'production'
system("puma")
end
@@ -24,8 +28,12 @@ namespace :server do
end
namespace :test do
task :rubocop do
system("rubocop src/")
end
task :unit do
ENV['RACK_ENV'] = 'testing'
system("rspec")
end
task :lint do
system("rubocop src/ spec/")
end
end

View File

@@ -1,3 +1,21 @@
$ ->
# run foundation scripts
console.log('Ready.')
averageResults = (results, decimals = 2) ->
avgScore = 0
minScore = Infinity
maxScore = -Infinity
factor = (10 ^ decimals)
for result in results
avgScore += result.avg_score
minScore = Math.min(minScore, result.min_score)
maxScore = Math.max(maxScore, result.max_score)
return {
avgScore: Math.round((avgScore / results.length) * factor) / factor,
minScore: minScore,
maxScore: maxScore,
}

View File

@@ -1,6 +1,9 @@
$ ->
chartInstance = null
$('#report-benchmarks').on 'change', (e) ->
$('#report-tests option').prop('selected', false)
$('#reports-download').on 'click', (e) ->
e.preventDefault()
canvas = $('#benchmark-chart')[0]
@@ -64,21 +67,14 @@ $ ->
resultRes = await fetch("/api/v1/result/list?#{resultSearchParams}")
resultData = await resultRes.json()
avg_total = 0
min_total = 0
max_total = 0
for result in resultData
avg_total += result.avg_score
min_total += result.min_score if result.min_score
max_total += result.max_score if result.max_score
resultAverage = averageResults(resultData)
data.labels.push(testData.name)
data.datasets[0].data.push(avg_total / resultData.length)
data.datasets[0].data.push(resultAverage.avgScore)
console.log(data.datasets[0].data)
switch benchmarkData.scoring
when 'fps', 'ms'
data.datasets[1].data.push(min_total / resultData.length)
data.datasets[1].data.push(resultAverage.minScore)
catch error
console.error 'An error occurred while fetching benchmark results.', error

View File

@@ -13,14 +13,7 @@ fetchTestBenchmarkResults = (testId, benchmarkId) ->
resultRes = await fetch("/api/v1/result/list?#{resultSearchParams}")
resultData = await resultRes.json()
avg_total = 0
min_total = 0
max_total = 0
for result in resultData
avg_total += result.avg_score
min_total += result.min_score
max_total += result.max_score
resultAverage = averageResults(resultData)
tableRow = $("#results-table tr[data-benchmark-id=#{benchmarkId}]")
@@ -29,16 +22,18 @@ fetchTestBenchmarkResults = (testId, benchmarkId) ->
tableRow.append('<td>' + resultData.length + '</td>')
if resultData.length != 0
tableRow.append('<td>' + (avg_total / resultData.length) + '</td>')
tableRow.append('<td>' + resultAverage.avgScore + '</td>')
if benchmarkData.scoring == 'fps'
tableRow.append('<td>' + resultAverage.minScore + '</td>')
tableRow.append('<td>' + resultAverage.maxScore + '</td>')
else
tableRow.append('<td>N/a</td>')
if min_total != 0
tableRow.append('<td>' + (min_total / resultData.length) + '</td>')
tableRow.append('<td>' + (max_total / resultData.length) + '</td>')
tableRow.append('<td>N/a</td>')
else
tableRow.append('<td>N/a</td>')
tableRow.append('<td>N/a</td>')
tableRow.append('<td>N/a</td>')
catch error
console.error 'An error occurred while fetching benchmark results.', error

View File

@@ -1,6 +1,4 @@
# Load application config
require_relative 'src/config.rb'
$conf = Config.new(File.join(__dir__, 'config/defaults.yaml'))
root = ::File.dirname(__FILE__)
require ::File.join( root, 'src', 'server' )

View File

@@ -1,6 +1,6 @@
database:
adapter: 'sqlite'
database: 'data/gamedata.db'
server:
host: '0.0.0.0'
port: '9292'
testing:
minimum_results_required: 3

3
config/development.yaml Normal file
View File

@@ -0,0 +1,3 @@
database:
adapter: 'sqlite'
database: 'data/gamedata.db'

View File

@@ -3,7 +3,9 @@ directory app_dir
environment ENV.fetch('RACK_ENV', 'development')
bind 'tcp://0.0.0.0:9292'
require_relative '../src/config'
conf = Config.new()
bind "tcp://#{conf.get('server.host')}:#{conf.get('server.port')}"
workers 2
threads 1, 5

3
config/testing.yaml Normal file
View File

@@ -0,0 +1,3 @@
database:
adapter: 'sqlite'
database: 'data/gamedata_testing.db'

View File

@@ -0,0 +1,44 @@
# frozen_string_literal: true
require_relative '../spec_helper'
RSpec.describe(BenchmarkController) do
# /benchmark - redirects to /benchmark/list
describe 'GET /benchmark' do
before { get '/benchmark' }
it 'Benchmark base route is a redirect' do
expect(last_response).to(be_redirect)
end
it 'Benchmark base route Location header points to /benchmark/list' do
expect(last_response['Location']).to(eq("#{BASE_URL}/benchmark/list"))
end
end
# /benchmark/list - displays a table of benchmarks
describe 'GET /benchmark/list' do
before { get '/benchmark/list' }
it 'Benchmark list page returns 200.' do
expect(last_response).to(be_ok)
end
it "Benchmark list page contains 'List of benchmarks' on page." do
expect(last_response.body).to(include('List of benchmarks'))
end
end
# /benchmark/add - form for adding benchmark
describe 'GET /benchmark/add' do
before { get '/benchmark/add' }
it 'Benchmark add page returns 200.' do
expect(last_response).to(be_ok)
end
it "Benchmark add page contains 'Add new benchmark' on page." do
expect(last_response.body).to(include('Add new benchmark'))
end
end
end

View File

@@ -0,0 +1,44 @@
# frozen_string_literal: true
require_relative '../spec_helper'
RSpec.describe(HardwareController) do
# /hardware - redirects to /hardware/list
describe 'GET /hardware' do
before { get '/hardware' }
it 'Hardware base route is a redirect' do
expect(last_response).to(be_redirect)
end
it 'Hardware base route Location header points to /hardware/list' do
expect(last_response['Location']).to(eq("#{BASE_URL}/hardware/list"))
end
end
# /hardware/list - displays a table of hardwares
describe 'GET /hardware/list' do
before { get '/hardware/list' }
it 'Hardware list page returns 200.' do
expect(last_response).to(be_ok)
end
it "Hardware list page contains 'List of hardware' on page." do
expect(last_response.body).to(include('List of hardware'))
end
end
# /hardware/add - form for adding hardware
describe 'GET /hardware/add' do
before { get '/hardware/add' }
it 'Hardware add page returns 200.' do
expect(last_response).to(be_ok)
end
it "Hardware add page contains 'Add new hardware' on page." do
expect(last_response.body).to(include('Add new hardware'))
end
end
end

View File

@@ -0,0 +1,21 @@
# frozen_string_literal: true
require_relative '../spec_helper'
RSpec.describe(IndexController) do
describe 'GET /' do
before { get '/' }
it 'Dashboard returns 200.' do
expect(last_response).to(be_ok)
end
it "Dashboard contains 'Game Data' on page (nav bar should be loaded)." do
expect(last_response.body).to(include('Game Data'))
end
it "Dashboard contains 'Ruby version' on page (footer should be loaded)." do
expect(last_response.body).to(include('Ruby version'))
end
end
end

View File

@@ -0,0 +1,17 @@
# frozen_string_literal: true
require_relative '../spec_helper'
RSpec.describe(ReportsController) do
describe 'GET /report' do
before { get '/report' }
it 'Reports page returns 200.' do
expect(last_response).to(be_ok)
end
it "Reports page contains 'Generate report' on page." do
expect(last_response.body).to(include('Generate report'))
end
end
end

View File

@@ -0,0 +1,44 @@
# frozen_string_literal: true
require_relative '../spec_helper'
RSpec.describe(TestController) do
# /test - redirects to /test/list
describe 'GET /test' do
before { get '/test' }
it 'Test base route is a redirect' do
expect(last_response).to(be_redirect)
end
it 'Test base route Location header points to /test/list' do
expect(last_response['Location']).to(eq("#{BASE_URL}/test/list"))
end
end
# /test/list - displays a table of tests
describe 'GET /test/list' do
before { get '/test/list' }
it 'Test list page returns 200.' do
expect(last_response).to(be_ok)
end
it "Test list page contains 'List of tests' on page." do
expect(last_response.body).to(include('List of tests'))
end
end
# /test/add - form for adding test
describe 'GET /test/add' do
before { get '/test/add' }
it 'Test add page returns 200.' do
expect(last_response).to(be_ok)
end
it "Test add page contains 'Add new test' on page." do
expect(last_response.body).to(include('Add new test'))
end
end
end

View File

@@ -0,0 +1,27 @@
# frozen_string_literal: true
require_relative '../spec_helper'
RSpec.describe(Benchmark) do
describe 'Benchmark Creation' do
it 'Benchmark creation updates model count.' do
expect do
described_class.create(name: 'Test Benchmark', scoring: 'fps')
end.to(change(described_class, :count).by(1))
end
end
describe 'Benchmark Read' do
before { described_class.create(name: 'Test Benchmark', scoring: 'fps') }
it 'Benchmark model has name.' do
bench = described_class.first()
expect(bench.name).to(eq('Test Benchmark'))
end
it 'Benchmark model has scoring.' do
bench = described_class.first()
expect(bench.scoring).to(eq('fps'))
end
end
end

View File

@@ -0,0 +1,25 @@
# frozen_string_literal: true
require_relative '../spec_helper'
RSpec.describe(Hardware) do
describe 'Hardware Creation' do
it 'Hardware creation updates model count.' do
expect { described_class.create(name: 'Test Hardware', type: 'gpu') }.to(change(described_class, :count).by(1))
end
end
describe 'Hardware Read' do
before { described_class.create(name: 'Test Hardware', type: 'gpu') }
it 'Hardware model has name.' do
hardware = described_class.first()
expect(hardware.name).to(eq('Test Hardware'))
end
it 'Hardware model has scoring.' do
hardware = described_class.first()
expect(hardware.type).to(eq('gpu'))
end
end
end

View File

@@ -0,0 +1,36 @@
# frozen_string_literal: true
require_relative '../spec_helper'
RSpec.describe(Test) do
describe 'Test Creation' do
it 'Test creation updates model count.' do
expect { described_class.create(name: 'Test Test') }.to(change(described_class, :count).by(1))
end
end
describe 'Test Read' do
before do
described_class.create(name: 'Test Test')
end
it 'Test model has name.' do
tst = described_class.first()
expect(tst.name).to(eq('Test Test'))
end
end
describe 'Test one-to-many association with Hardware' do
it 'Test model has Hardware associated with it.' do
hardware = Hardware.create(name: 'Test Hardware', type: 'gpu')
tst = described_class.create(name: 'Test Test', hardware_id: hardware.id)
expect(tst.hardware).to(eq(hardware))
end
it 'Test model\'s hardware has name set.' do
hardware = Hardware.create(name: 'Test Hardware', type: 'gpu')
tst = described_class.create(name: 'Test Test', hardware_id: hardware.id)
expect(tst.hardware.name).to(eq('Test Hardware'))
end
end
end

35
spec/spec_helper.rb Normal file
View File

@@ -0,0 +1,35 @@
# frozen_string_literal: true
ENV['APP_ENV'] = 'test'
require_relative '../src/server'
require 'rspec'
require 'rack/test'
require 'database_cleaner/sequel'
# setting this here so all redirect tests can reference the same base URL
BASE_URL = 'http://example.org'
module RSpecMixin
include Rack::Test::Methods
def app
GameData
end
end
RSpec.configure do |config|
config.include(RSpecMixin)
config.before(:suite) do
DatabaseCleaner.strategy = :transaction
end
config.around do |suite|
DatabaseCleaner.cleaning do
suite.run
end
end
end

View File

@@ -2,6 +2,6 @@
module AppInfo
VERSION = '0.1.1'
VERSION = '0.2.1'
end

View File

@@ -6,8 +6,9 @@ require 'yaml'
class Config
DEFAULT_CONFIG = 'config/defaults.yaml'
ENVIRONMENT_CONFIG = ENV.fetch('RACK_ENV', 'development')
def initialize(config_path)
def initialize(config_path = "config/#{ENVIRONMENT_CONFIG}.yaml")
@data = YAML.load_file(DEFAULT_CONFIG)
# merge in user-defined configuration if it exists

View File

@@ -1,7 +1,14 @@
# frozen_string_literal: true
require 'sinatra/json'
require_relative 'base_controller'
require_relative '../models/benchmark'
require_relative '../models/test'
require_relative '../models/result'
# /api/v1 routes
class GameData < Sinatra::Base
class APIv1Controller < BaseController
get '/api/v1/benchmark/details' do
benchmark_id = params[:benchmark_id]

View File

@@ -0,0 +1,15 @@
# frozen_string_literal: true
require 'sinatra/base'
# BaseController - base modular Sinatra app class
class BaseController < Sinatra::Base
# Register view helpers
require_relative '../helpers'
helpers Helpers
# Set up our view engine
set :views, File.join(settings.root, '/../../views')
end

View File

@@ -1,9 +1,16 @@
# frozen_string_literal: true
require_relative 'base_controller'
require_relative '../models/benchmark'
# /benchmark routes
class GameData < Sinatra::Base
class BenchmarkController < BaseController
get '/benchmark' do
redirect('/benchmark/list')
end
get '/benchmark/list' do
benchmarks = Benchmark.reverse(:updated_at).limit(10).all()
erb :'benchmark/index', locals: {

View File

@@ -1,9 +1,17 @@
# frozen_string_literal: true
require_relative 'base_controller'
require_relative '../models/hardware'
require_relative '../models/benchmark'
# /hardware routes
class GameData < Sinatra::Base
class HardwareController < BaseController
get '/hardware' do
redirect('/hardware/list')
end
get '/hardware/list' do
hardware = Hardware.reverse(:updated_at).limit(10).all()
erb :'hardware/index', locals: {

View File

@@ -1,7 +1,10 @@
# frozen_string_literal: true
require_relative 'base_controller'
require_relative '../models/test'
# / (top-level) routes
class GameData < Sinatra::Base
class IndexController < BaseController
get '/' do
tests = Test.reverse(:updated_at).limit(10).all()

View File

@@ -1,7 +1,14 @@
# frozen_string_literal: true
require 'sinatra/json'
require_relative 'base_controller'
require_relative '../models/benchmark'
require_relative '../models/result'
require_relative '../models/test'
# /reports routes
class GameData < Sinatra::Base
class ReportsController < BaseController
get '/report' do
benchmarks = Benchmark.order(:name).all()

View File

@@ -1,7 +1,10 @@
# frozen_string_literal: true
require_relative 'base_controller'
require_relative '../models/result'
# /result routes
class GameData < Sinatra::Base
class ResultController < BaseController
post '/result/add' do
result_minimum = params[:result_minimum] if params.key?(:result_minimum)

View File

@@ -1,9 +1,18 @@
# frozen_string_literal: true
require_relative 'base_controller'
require_relative '../models/benchmark'
require_relative '../models/hardware'
require_relative '../models/test'
# /test routes
class GameData < Sinatra::Base
class TestController < BaseController
get '/test' do
redirect('/test/list')
end
get '/test/list' do
tests = Test.reverse(:updated_at).limit(10).all()
erb :'test/index', locals: {
@@ -65,9 +74,22 @@ class GameData < Sinatra::Base
tst.update(
name: params[:test_name],
type: params[:test_type]
hardware_id: params[:test_hardware],
description: params[:test_description]
)
selected_benchmarks = Array(params[:test_benchmarks])
# remove benchmarks no longer associated with the test
tst.benchmarks.dup.each do |b|
tst.remove_benchmark(b.id) unless selected_benchmarks.include?(b.id)
end
# associate the benchmarks to the test
selected_benchmarks.each do |b|
tst.add_benchmark(b) unless tst.benchmark?(b)
end
redirect "/test/#{tst.id}"
end

View File

@@ -1,5 +1,7 @@
# frozen_string_literal: true
require_relative 'appinfo'
# Helpers - view helper functions
module Helpers

View File

@@ -1,6 +0,0 @@
# frozen_string_literal: true
require_relative 'hardware'
require_relative 'benchmark'
require_relative 'result'
require_relative 'test'

View File

@@ -7,4 +7,8 @@ class Test < Sequel::Model
many_to_one :hardware
many_to_many :benchmarks
def benchmark?(benchmark_id)
return benchmarks_dataset.where(Sequel[:benchmarks][:id] => benchmark_id).any?
end
end

View File

@@ -1,10 +0,0 @@
# frozen_string_literal: true
require_relative 'index'
require_relative 'hardware'
require_relative 'benchmark'
require_relative 'reports'
require_relative 'result'
require_relative 'test'
require_relative 'api1'

38
src/server.rb Executable file → Normal file
View File

@@ -1,18 +1,32 @@
# frozen_string_literal: true
require 'sinatra/base'
require 'sinatra/json'
require 'sequel'
require 'sqlite3'
require_relative 'appinfo'
require_relative 'config'
# Load configuration from environment config file
$conf = Config.new()
# Load the Sequel timestamps plugin
Sequel::Model.plugin(:timestamps)
# Initialize Sequel gem for database actions
DB = Sequel.connect(adapter: $conf.get('database.adapter'), database: $conf.get('database.database'))
# Base app
# Load in routes (must happen after Sequel is loaded!)
require_relative 'controllers/api1'
require_relative 'controllers/benchmark'
require_relative 'controllers/hardware'
require_relative 'controllers/index'
require_relative 'controllers/reports'
require_relative 'controllers/result'
require_relative 'controllers/test'
# GameData - main app that gets launched
# - inherits from Sinatra::Base to instantiate the server
# - sets up some base app configuration
# - registers route classes with the base app
class GameData < Sinatra::Base
enable :sessions
@@ -21,16 +35,12 @@ class GameData < Sinatra::Base
enable :static
set :public_folder, File.join(__dir__, '/../public')
# Register view helpers
require_relative 'helpers'
helpers Helpers
# Set up our view engine
set :views, File.join(settings.root, '/../views')
use IndexController
use HardwareController
use BenchmarkController
use TestController
use ResultController
use ReportsController
use APIv1Controller
end
# Load routes
require_relative 'routes/init'
# Load models
require_relative 'models/init'

View File

@@ -26,7 +26,7 @@
<div class="row mb-3">
<div class="col-12">
<label for="benchmark_description">Benchmark description</label>
<textarea id="benchmark_description" class="form-control" name="benchmark_description">Enter a description/notes here.</textarea>
<textarea id="benchmark_description" class="form-control" name="benchmark_description" placeholder="Enter a description/notes here."></textarea>
</div>
</div>

View File

@@ -26,7 +26,7 @@
<div class="row mb-3">
<div class="col-12">
<label for="benchmark_description">Benchmark description</label>
<textarea id="benchmark_description" class="form-control" name="benchmark_description"><%= benchmark.description %></textarea>
<textarea id="benchmark_description" class="form-control" name="benchmark_description" placeholder="Enter a description/notes here."><%= benchmark.description %></textarea>
</div>
</div>

View File

@@ -1,3 +1,9 @@
<div class="row">
<div class="col-12">
<h1>Generate report</h1>
</div>
</div>
<div class="row">
<form class="col-12" action="/reports" method="post">
<div class="row mb-3">

View File

@@ -28,7 +28,7 @@
<label for="test_benchmarks">Benchmarks</label>
<select id="test_benchmarks" class="form-select" name="test_benchmarks[]" multiple>
<% for b in benchmarks %>
<option value="<%= b.id %>"><%= b.name %></option>
<option value="<%= b.id %>" <% if test.benchmark?(b.id) %>selected<% end %>><%= b.name %></option>
<% end %>
</select>
</div>
@@ -41,7 +41,7 @@
<div class="row">
<div class="col-12">
<input class="btn btn-primary w-100" type="submit" value="Create Test">
<input class="btn btn-primary w-100" type="submit" value="Submit Changes">
</div>
</div>
</form>