testament 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1,2 +1,4 @@
1
1
  .rvmrc
2
2
  Gemfile.lock
3
+ pkg
4
+ .testament
data/README.md CHANGED
@@ -1,9 +1,5 @@
1
1
  # Testament
2
2
 
3
- Note: readme driven development is being used -- not everything below will work yet!
4
-
5
- NOT READY FOR PRODUCTION USE
6
-
7
3
  Testament is a tool to track the cost of running tests.
8
4
 
9
5
  ## Installation
@@ -12,9 +8,15 @@ Testament is a tool to track the cost of running tests.
12
8
 
13
9
  ## Usage
14
10
 
11
+ To setup testament with your project run testament init on your project directory.
12
+
13
+ $ testament init .
14
+
15
+ This will create a .testament directory with the project config and the SQLite database that will store the test run information.
16
+
15
17
  To track the time it takes to run your specs, run them like this every time:
16
18
 
17
- $ testament record rspec spec/
19
+ $ testament record rspec spec
18
20
 
19
21
  If you have your default rake task set to run your whole test suite, then you can do this:
20
22
 
@@ -22,13 +24,25 @@ If you have your default rake task set to run your whole test suite, then you ca
22
24
 
23
25
  You may want to create an alias to rake and rspec to run it with testament track automatically.
24
26
 
25
- ## Seeing Trends
27
+ ## Reports
28
+
29
+ To view the default report, run the following in your project folder:
30
+
31
+ $ testament report
32
+
33
+ This will yield output like this:
34
+
35
+ +-----------+---------+-------+--------------+------------+
36
+ | Project | Command | Count | Average time | Total time |
37
+ +-----------+---------+-------+--------------+------------+
38
+ | testament | rspec | 4 | 6.03675 | 24.147 |
39
+ +-----------+---------+-------+--------------+------------+
26
40
 
27
- To view some stats, run this:
41
+ To see a report from just today run the following:
28
42
 
29
- $ testament stats
43
+ $ testament report today
30
44
 
31
- in your project folder.
45
+ The report definitions are stored in .testament/report. You can edit the existing reports or create new ones.
32
46
 
33
47
  ## Contributing
34
48
 
@@ -1,10 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
- require 'testament/csv_repository'
2
+ require 'testament'
3
+ require 'testament/cli'
3
4
 
4
- command = ARGV[1..-1].join(' ')
5
-
6
- start_time = Time.now
7
- system command
8
- end_time = Time.now
9
-
10
- Testament::CSVRepository.new('testament.log').store command, start_time, end_time
5
+ CLI.start
@@ -1,5 +1,9 @@
1
1
  require "testament/version"
2
2
 
3
+ gem 'activesupport', '~> 3.2.9'
4
+ gem 'sequel', '~> 3.42.0'
5
+ gem 'terminal-table', '~> 1.4.5'
6
+
7
+
3
8
  module Testament
4
- # Your code goes here...
5
9
  end
@@ -0,0 +1,51 @@
1
+ require 'thor'
2
+ require 'thor/group'
3
+ require 'testament/project'
4
+ require 'testament/report'
5
+
6
+ class CLI < Thor
7
+ include Thor::Actions
8
+
9
+ source_root(File.join(File.dirname(__FILE__), "generators"))
10
+
11
+ attr_accessor :app_name
12
+
13
+ desc "init PATH", "initialize testament in directory PATH"
14
+ def init(path)
15
+ self.app_name = File.basename File.expand_path(path)
16
+ self.destination_root = path
17
+ directory ".testament", ".testament"
18
+
19
+ require 'testament/database'
20
+ database = Testament::Database.new adapter: :sqlite, database: "#{path}/.testament/db.sqlite"
21
+ database.create_schema
22
+ end
23
+
24
+ desc "record COMMAND", "record and execute COMMAND"
25
+ def record(*command_words)
26
+ command = command_words.join(' ')
27
+ project = Testament::Project.load
28
+ project.record command
29
+ end
30
+
31
+ desc "log", "print logs"
32
+ def log
33
+ project = Testament::Project.load
34
+ puts project.log
35
+ end
36
+
37
+ desc "report [REPORT_NAME]", "run REPORT_NAME"
38
+ long_desc <<-END_TXT
39
+ Reports are stored in .testament/report. You can alter or create new reports
40
+ there.
41
+
42
+ Available reports: #{Testament::Report::Loader.new.report_names.join(' ')}
43
+ END_TXT
44
+ def report(name='default')
45
+ require 'terminal-table'
46
+ project = Testament::Project.load
47
+ report = project.report name
48
+ table = Terminal::Table.new :headings => report.headers, :rows => report.rows
49
+ puts table
50
+ end
51
+ end
@@ -0,0 +1,29 @@
1
+ require 'sequel'
2
+
3
+ module Testament
4
+ class Database
5
+ attr_reader :db
6
+
7
+ def initialize(connection_parameters)
8
+ @db = Sequel.connect connection_parameters
9
+ end
10
+
11
+ def create_schema
12
+ db.create_table :executions do
13
+ primary_key :id, type: Bignum
14
+ String :project, null: false
15
+ String :command, null: false
16
+ Time :start_time, null: false
17
+ Fixnum :elapsed_milliseconds, null: false
18
+ String :user, null: false
19
+ String :version, null: false
20
+ String :category, null: false
21
+ end
22
+ end
23
+
24
+ def record(attributes)
25
+ db[:executions].insert attributes
26
+ end
27
+ end
28
+
29
+ end
@@ -0,0 +1,51 @@
1
+ require 'yaml'
2
+ require 'erb'
3
+ require 'testament/database'
4
+
5
+ module Testament
6
+ class Project
7
+ attr_reader :name, :user, :version, :default_category
8
+
9
+ def initialize(arguments)
10
+ @database = Database.new arguments.fetch('database')
11
+ @name = arguments.fetch('project')
12
+ @user = arguments.fetch('user')
13
+ @version = arguments.fetch('version')
14
+ @default_category = arguments.fetch('default_category')
15
+ end
16
+
17
+ def record(command)
18
+ start_time = Time.now
19
+ system command
20
+ end_time = Time.now
21
+ elapsed_milliseconds = ((end_time - start_time) * 1000).round
22
+
23
+ database.record project: name,
24
+ command: command,
25
+ start_time: start_time,
26
+ elapsed_milliseconds: elapsed_milliseconds,
27
+ user: user,
28
+ version: version,
29
+ category: default_category
30
+ end
31
+
32
+ def log
33
+ database.db[:executions].order(:start_time).all
34
+ end
35
+
36
+ def report(name)
37
+ require 'testament/report'
38
+ Testament::Report.find(name).new database.db
39
+ end
40
+
41
+ def self.load
42
+ config_path = ".testament/config.yml"
43
+ config = YAML.load(ERB.new(File.read(config_path)).result)
44
+ new config
45
+ end
46
+
47
+ private
48
+
49
+ attr_reader :database
50
+ end
51
+ end
@@ -0,0 +1,32 @@
1
+ require 'active_support/core_ext/string/inflections'
2
+ require 'active_support/core_ext/time/calculations'
3
+
4
+ require 'testament/report/base'
5
+
6
+ module Testament
7
+ module Report
8
+ def self.find(name)
9
+ Loader.new.load
10
+ "Testament::Report::#{name.camelize}".constantize
11
+ end
12
+
13
+ class Loader
14
+ def load
15
+ report_paths.each do |report_path|
16
+ ::Kernel.load report_path
17
+ end
18
+ end
19
+
20
+ def report_names
21
+ report_paths.map do |report_path|
22
+ report_path[/\w+(?=\.rb$)/]
23
+ end
24
+ end
25
+
26
+ private
27
+ def report_paths
28
+ Dir.glob('.testament/report/*.rb')
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,27 @@
1
+ module Testament
2
+ module Report
3
+ class Base
4
+ attr_reader :database
5
+
6
+ def initialize(database)
7
+ @database = database
8
+ end
9
+
10
+ def headers
11
+ @headers ||= dataset.columns.map(&:to_s).map(&:humanize)
12
+ end
13
+
14
+ def rows
15
+ @rows ||= result_set.map(&:values)
16
+ end
17
+
18
+ def result_set
19
+ @result_set ||= dataset.all
20
+ end
21
+
22
+ def dataset
23
+ raise NotImplementedError
24
+ end
25
+ end
26
+ end
27
+ end
@@ -1,3 +1,3 @@
1
1
  module Testament
2
- VERSION = "0.0.1"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -0,0 +1,5 @@
1
+ require 'rspec'
2
+ require 'pry'
3
+
4
+ require 'testament'
5
+ require 'testament/database'
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ describe Testament::Database do
4
+ subject(:database) { described_class.new adapter: :sqlite }
5
+
6
+ describe 'create_schema' do
7
+ before { database.create_schema }
8
+
9
+ it 'creates executions table' do
10
+ expect(database.db.tables).to include(:executions)
11
+ end
12
+ end
13
+
14
+ describe 'record' do
15
+ before { database.create_schema }
16
+
17
+ it 'inserts row in executions table' do
18
+ database.record project: 'testament',
19
+ command: 'record',
20
+ start_time: Time.now,
21
+ elapsed_milliseconds: 42,
22
+ user: 'jack',
23
+ version: '12345678abcdef',
24
+ category: 'test'
25
+
26
+ expect(database.db[:executions].count).to eq(1)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,6 @@
1
+ require 'spec_helper'
2
+ require 'testament/project'
3
+
4
+ describe Testament::Project do
5
+
6
+ end
@@ -1,32 +1,82 @@
1
1
  require 'rspec'
2
2
  require 'pry'
3
- require 'time' # for Time.parse
3
+ require 'fileutils'
4
+ require 'securerandom'
4
5
 
5
6
  RSpec.configure do |config|
6
7
  def testament(args="")
7
8
  lib = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
8
9
  bin = File.expand_path(File.join(File.dirname(__FILE__), '..', 'bin', 'testament'))
9
- `ruby -I #{lib} #{bin} #{args}`
10
+ `cd #{directory}; ruby -I #{lib} #{bin} #{args}`
10
11
  end
11
12
  end
12
13
 
13
14
  describe 'testament' do
15
+ let(:directory) { File.expand_path(File.join(File.dirname(__FILE__), 'tmp', SecureRandom.hex)) }
16
+
17
+ before do
18
+ FileUtils.mkdir_p directory
19
+ end
20
+
21
+ after do
22
+ FileUtils.rm_rf directory
23
+ end
24
+
25
+ context 'init' do
26
+ it 'creates a .testament directory in path argument' do
27
+ testament "init #{directory}"
28
+ expect(File.directory?( File.join(directory, '.testament') )).to be_true
29
+ end
30
+ end
31
+
14
32
  context 'record' do
15
- it 'execute it\'s argument' do
16
- result = testament('record echo foo')
33
+ before do
34
+ testament "init #{directory}"
35
+ end
36
+
37
+ it 'executes a single word argument' do
38
+ result = testament 'record pwd'
39
+ expect(`pwd`.chomp).to eq(FileUtils.pwd)
40
+ end
41
+
42
+ it 'executes a multiple word argument' do
43
+ result = testament 'record echo foo'
17
44
  expect(result).to eq("foo\n")
18
45
  end
19
46
 
20
47
  it 'records the execution of the command' do
48
+ testament 'record echo foo'
49
+ output = testament 'log'
50
+ expect(output).to match(/echo foo/)
51
+ end
52
+ end
53
+
54
+ context 'log' do
55
+ before do
56
+ testament "init #{directory}"
57
+ testament('record echo foo')
58
+ end
59
+
60
+ it 'outputs the previously logged data' do
61
+ output = testament 'log'
62
+ expect(output).to match(/echo foo/)
63
+ end
64
+ end
65
+
66
+ context 'report' do
67
+ before do
68
+ testament "init #{directory}"
21
69
  testament('record echo foo')
22
- last_line = `tail -n 1 testament.log`
23
- command, start_time, end_time = last_line.split(",")
24
- expect(command).to eq('echo foo')
25
-
26
- start_time = Time.parse(start_time) rescue nil
27
- expect(start_time).to be
28
- end_time = Time.parse(end_time) rescue nil
29
- expect(end_time).to be
70
+ end
71
+
72
+ it 'runs default report when no name given' do
73
+ actual = testament 'report'
74
+ expect(actual).to match(/Command/)
75
+ end
76
+
77
+ it 'runs report by name' do
78
+ actual = testament 'report today'
79
+ expect(actual).to match(/echo foo/)
30
80
  end
31
81
  end
32
82
  end
@@ -12,6 +12,12 @@ Gem::Specification.new do |gem|
12
12
  gem.summary = %q{Time, record, and analyze test runs}
13
13
  gem.homepage = "https://github.com/JackC/testament"
14
14
 
15
+ gem.add_dependency 'activesupport', '~> 3.2.9'
16
+ gem.add_dependency 'sequel', '~> 3.42.0'
17
+ gem.add_dependency 'sqlite3', '~> 1.3.6'
18
+ gem.add_dependency 'terminal-table', '~> 1.4.5'
19
+ gem.add_dependency 'thor', '~> 0.16.0'
20
+
15
21
  gem.add_development_dependency 'rspec', '>= 2.11.0'
16
22
  gem.add_development_dependency 'pry'
17
23
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: testament
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,8 +9,88 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-12-13 00:00:00.000000000 Z
12
+ date: 2012-12-18 00:00:00.000000000 Z
13
13
  dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activesupport
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 3.2.9
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 3.2.9
30
+ - !ruby/object:Gem::Dependency
31
+ name: sequel
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 3.42.0
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 3.42.0
46
+ - !ruby/object:Gem::Dependency
47
+ name: sqlite3
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: 1.3.6
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 1.3.6
62
+ - !ruby/object:Gem::Dependency
63
+ name: terminal-table
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: 1.4.5
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: 1.4.5
78
+ - !ruby/object:Gem::Dependency
79
+ name: thor
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ~>
84
+ - !ruby/object:Gem::Version
85
+ version: 0.16.0
86
+ type: :runtime
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ version: 0.16.0
14
94
  - !ruby/object:Gem::Dependency
15
95
  name: rspec
16
96
  requirement: !ruby/object:Gem::Requirement
@@ -59,9 +139,18 @@ files:
59
139
  - Rakefile
60
140
  - bin/testament
61
141
  - lib/testament.rb
62
- - lib/testament/csv_repository.rb
142
+ - lib/testament/cli.rb
143
+ - lib/testament/database.rb
144
+ - lib/testament/generators/.testament/config.yml.tt
145
+ - lib/testament/generators/.testament/report/default.rb
146
+ - lib/testament/generators/.testament/report/today.rb
147
+ - lib/testament/project.rb
148
+ - lib/testament/report.rb
149
+ - lib/testament/report/base.rb
63
150
  - lib/testament/version.rb
64
- - spec/testament/csv_repository_spec.rb
151
+ - spec/spec_helper.rb
152
+ - spec/testament/database_spec.rb
153
+ - spec/testament/project_spec.rb
65
154
  - spec/testament_spec.rb
66
155
  - testament.gemspec
67
156
  homepage: https://github.com/JackC/testament
@@ -89,5 +178,7 @@ signing_key:
89
178
  specification_version: 3
90
179
  summary: Time, record, and analyze test runs
91
180
  test_files:
92
- - spec/testament/csv_repository_spec.rb
181
+ - spec/spec_helper.rb
182
+ - spec/testament/database_spec.rb
183
+ - spec/testament/project_spec.rb
93
184
  - spec/testament_spec.rb
@@ -1,27 +0,0 @@
1
- require 'csv'
2
-
3
- module Testament
4
- class CSVRepository
5
- attr_reader :file_name
6
-
7
- def initialize(file_name)
8
- @file_name = file_name
9
- end
10
-
11
- def store(command, start_time, end_time)
12
- ensure_file_exists
13
-
14
- CSV.open(file_name, 'ab') do |csv|
15
- csv << [command, start_time, end_time]
16
- end
17
- end
18
-
19
- private
20
- def ensure_file_exists
21
- return if File.exist?(file_name)
22
- CSV.open(file_name, 'wb') do |csv|
23
- csv << %w[command start_time end_time]
24
- end
25
- end
26
- end
27
- end
@@ -1,60 +0,0 @@
1
- require 'rspec'
2
- require 'fileutils'
3
- require 'testament/csv_repository'
4
-
5
- describe Testament::CSVRepository do
6
- before do
7
- FileUtils.rm_f file_path
8
- end
9
-
10
- subject(:repository) { Testament::CSVRepository.new file_path }
11
-
12
- let(:file_path) do
13
- 'spec/tmp/csv.log'
14
- end
15
-
16
- describe 'store' do
17
- let(:command) { 'foo' }
18
- let(:start_time) { Time.local(2012,1,1, 13,0,0) }
19
- let(:end_time) { Time.local(2012,1,1, 13,0,1) }
20
-
21
- def rows
22
- CSV.read file_path
23
- end
24
-
25
- context 'when file does not exist' do
26
- before do
27
- repository.store command, start_time, end_time
28
- end
29
-
30
- it 'creates the file' do
31
- expect(File.exist?(file_path)).to be_true
32
- end
33
-
34
- it 'writes the CSV header' do
35
- expect(rows.first).to eq(%w[command start_time end_time])
36
- end
37
-
38
- it 'writes the record to the file' do
39
- expect(rows.size).to eq(2)
40
- actual_command, actual_start_time, actual_end_time = rows.last
41
- actual_start_time = Time.parse(actual_start_time)
42
- actual_end_time = Time.parse(actual_end_time)
43
- expect(actual_command).to eq(command)
44
- expect(actual_start_time).to eq(start_time)
45
- expect(actual_end_time).to eq(end_time)
46
- end
47
- end
48
-
49
- context 'when the file already exists' do
50
- before do
51
- repository.store command, start_time, end_time
52
- repository.store command, start_time, end_time
53
- end
54
-
55
- it 'appends the record to the file' do
56
- expect(rows.size).to eq(3)
57
- end
58
- end
59
- end
60
- end