est 0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,3 @@
1
+ default: --format pretty
2
+ travis: --format progress
3
+ html_report: --format progress --format html --out=features_report.html
@@ -0,0 +1,59 @@
1
+ # encoding: utf-8
2
+ #
3
+ # Copyright (c) 2014 TechnoPark Corp.
4
+ # Copyright (c) 2014 Yegor Bugayenko
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ # of this software and associated documentation files (the 'Software'), to deal
8
+ # in the Software without restriction, including without limitation the rights
9
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ # copies of the Software, and to permit persons to whom the Software is
11
+ # furnished to do so, subject to the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be included in all
14
+ # copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ # SOFTWARE.
23
+
24
+ lib = File.expand_path('../lib', __FILE__)
25
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
26
+ require 'est/version'
27
+
28
+ Gem::Specification.new do |s|
29
+ s.specification_version = 2 if s.respond_to? :specification_version=
30
+ if s.respond_to? :required_rubygems_version=
31
+ s.required_rubygems_version = Gem::Requirement.new('>= 0')
32
+ end
33
+ s.rubygems_version = '2.2.2'
34
+ s.required_ruby_version = '>= 1.9.3'
35
+ s.name = 'est'
36
+ s.version = Est::VERSION
37
+ s.license = 'MIT'
38
+ s.summary = 'Estimates Automated'
39
+ s.description = 'Estimate project size'
40
+ s.authors = ['Yegor Bugayenko']
41
+ s.email = 'yegor@teamed.io'
42
+ s.homepage = 'http://github.com/teamed/est'
43
+ s.files = `git ls-files`.split($RS)
44
+ s.executables = s.files.grep(/^bin\//) { |f| File.basename(f) }
45
+ s.test_files = s.files.grep(/^(test|spec|features)\//)
46
+ s.rdoc_options = ['--charset=UTF-8']
47
+ s.extra_rdoc_files = ['README.md', 'LICENSE.txt']
48
+ s.add_runtime_dependency 'nokogiri', '1.6.5'
49
+ s.add_runtime_dependency 'ruby-filemagic', '0.6.1'
50
+ s.add_runtime_dependency 'slop', '3.6.0'
51
+ s.add_runtime_dependency 'rake', '10.4.2'
52
+ s.add_development_dependency 'coveralls', '0.7.2'
53
+ s.add_development_dependency 'rdoc', '4.2.0'
54
+ s.add_development_dependency 'cucumber', '1.3.17'
55
+ s.add_development_dependency 'minitest', '5.5.0'
56
+ s.add_development_dependency 'rubocop', '0.24.1'
57
+ s.add_development_dependency 'rubocop-rspec', '1.2.1'
58
+ s.add_development_dependency 'rspec-rails', '3.1.0'
59
+ end
@@ -0,0 +1,51 @@
1
+ Feature: Command Line Processing
2
+ As an estimator I want to be able to
3
+ call Est as a command line tool
4
+
5
+ Scenario: Help can be printed
6
+ When I run bin/est with "-h"
7
+ Then Exit code is zero
8
+ And Stdout contains "-v, --verbose"
9
+
10
+ Scenario: Version can be printed
11
+ When I run bin/est with "--version"
12
+ Then Exit code is zero
13
+
14
+ Scenario: Simple estimate calculating
15
+ Given I have a "sample.est" file with content:
16
+ """
17
+ id: 789
18
+ date: 19-08-2014
19
+ author: Yegor Bugayenko
20
+ method: champions.pert
21
+ scope:
22
+ 1: basic Sinatra scaffolding
23
+ 2: front-end HAML files
24
+ 3: SASS stylesheet
25
+ 4: five model classes with unit tests
26
+ 5: PostgreSQL migrations
27
+ 6: Cucumber tests for PostgreSQL
28
+ 7: Capybara tests for HTML front
29
+ 8: CasperJS tests
30
+ 9: achieve 80% test coverage
31
+ champions:
32
+ 7:
33
+ worst-case: 40
34
+ best-case: 10
35
+ most-likely: 18
36
+ 4:
37
+ worst-case: 30
38
+ best-case: 8
39
+ most-likely: 16
40
+ """
41
+ When I run bin/est with "-v -d . -f out.xml"
42
+ Then Exit code is zero
43
+ And Stdout contains "reading ."
44
+ And XML file "out.xml" matches "/estimate[total='54']"
45
+
46
+ Scenario: Rejects unknown options
47
+ Given I have a "test.est" file with content:
48
+ """
49
+ """
50
+ When I run bin/est with "--some-unknown-option"
51
+ Then Exit code is not zero
@@ -0,0 +1,22 @@
1
+ Feature: Gem Package
2
+ As a source code writer I want to be able to
3
+ package the Gem into .gem file
4
+
5
+ Scenario: Gem can be packaged
6
+ Given I have a "execs.rb" file with content:
7
+ """
8
+ #!/usr/bin/env ruby
9
+ require 'rubygems'
10
+ spec = Gem::Specification::load('./spec.rb')
11
+ fail 'no executables' if spec.executables.empty?
12
+ """
13
+ When I run bash with
14
+ """
15
+ set -e
16
+ cd est
17
+ gem build est.gemspec
18
+ gem specification --ruby est-*.gem > ../spec.rb
19
+ cd ..
20
+ ruby execs.rb
21
+ """
22
+ Then Exit code is zero
@@ -0,0 +1,110 @@
1
+ # encoding: utf-8
2
+ #
3
+ # Copyright (c) 2014 TechnoPark Corp.
4
+ # Copyright (c) 2014 Yegor Bugayenko
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ # of this software and associated documentation files (the 'Software'), to deal
8
+ # in the Software without restriction, including without limitation the rights
9
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ # copies of the Software, and to permit persons to whom the Software is
11
+ # furnished to do so, subject to the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be included in all
14
+ # copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. IN NO EVENT SHALL THE
19
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ # SOFTWARE.
23
+
24
+ require 'est'
25
+ require 'nokogiri'
26
+ require 'tmpdir'
27
+ require 'slop'
28
+ require 'English'
29
+
30
+ Before do
31
+ @cwd = Dir.pwd
32
+ @dir = Dir.mktmpdir('test')
33
+ FileUtils.mkdir_p(@dir) unless File.exist?(@dir)
34
+ Dir.chdir(@dir)
35
+ @opts = Slop.parse ['-v', '-s', @dir] do
36
+ on 'v', 'verbose'
37
+ on 's', 'source', argument: :required
38
+ end
39
+ end
40
+
41
+ After do
42
+ Dir.chdir(@cwd)
43
+ FileUtils.rm_rf(@dir) if File.exist?(@dir)
44
+ end
45
+
46
+ Given(/^I have a "([^"]*)" file with content:$/) do |file, text|
47
+ FileUtils.mkdir_p(File.dirname(file)) unless File.exist?(file)
48
+ File.open(file, 'w') do |f|
49
+ f.write(text)
50
+ end
51
+ end
52
+
53
+ When(/^I run est$/) do
54
+ @xml = Nokogiri::XML.parse(Est::Base.new(@opts).xml)
55
+ end
56
+
57
+ Then(/^XML matches "([^"]+)"$/) do |xpath|
58
+ fail "XML doesn't match \"#{xpath}\":\n#{@xml}" if @xml.xpath(xpath).empty?
59
+ end
60
+
61
+ When(/^I run est it fails with "([^"]*)"$/) do |txt|
62
+ begin
63
+ Est::Base.new(@opts).xml
64
+ passed = true
65
+ rescue Est::Error => ex
66
+ unless ex.message.include?(txt)
67
+ raise "Est failed but exception doesn't contain \"#{txt}\": #{ex.message}"
68
+ end
69
+ end
70
+ fail "Est didn't fail" if passed
71
+ end
72
+
73
+ When(/^I run bin\/est with "([^"]*)"$/) do |arg|
74
+ home = File.join(File.dirname(__FILE__), '../..')
75
+ @stdout = `ruby -I#{home}/lib #{home}/bin/est #{arg}`
76
+ @exitstatus = $CHILD_STATUS.exitstatus
77
+ end
78
+
79
+ Then(/^Stdout contains "([^"]*)"$/) do |txt|
80
+ unless @stdout.include?(txt)
81
+ fail "STDOUT doesn't contain '#{txt}':\n#{@stdout}"
82
+ end
83
+ end
84
+
85
+ Then(/^Stdout is empty$/) do
86
+ fail "STDOUT is not empty:\n#{@stdout}" unless @stdout == ''
87
+ end
88
+
89
+ Then(/^XML file "([^"]+)" matches "([^"]+)"$/) do |file, xpath|
90
+ fail "File #{file} doesn't exit" unless File.exist?(file)
91
+ xml = Nokogiri::XML.parse(File.read(file))
92
+ xml.remove_namespaces!
93
+ if xml.xpath(xpath).empty?
94
+ fail "XML file #{file} doesn't match \"#{xpath}\":\n#{xml}"
95
+ end
96
+ end
97
+
98
+ Then(/^Exit code is zero$/) do
99
+ fail "Non-zero exit code #{@exitstatus}" unless @exitstatus == 0
100
+ end
101
+
102
+ Then(/^Exit code is not zero$/) do
103
+ fail 'Zero exit code' if @exitstatus == 0
104
+ end
105
+
106
+ When(/^I run bash with$/) do |text|
107
+ FileUtils.copy_entry(@cwd, File.join(@dir, 'est'))
108
+ @stdout = `#{text}`
109
+ @exitstatus = $CHILD_STATUS.exitstatus
110
+ end
@@ -0,0 +1,25 @@
1
+ # encoding: utf-8
2
+ #
3
+ # Copyright (c) 2014 TechnoPark Corp.
4
+ # Copyright (c) 2014 Yegor Bugayenko
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ # of this software and associated documentation files (the 'Software'), to deal
8
+ # in the Software without restriction, including without limitation the rights
9
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ # copies of the Software, and to permit persons to whom the Software is
11
+ # furnished to do so, subject to the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be included in all
14
+ # copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. IN NO EVENT SHALL THE
19
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ # SOFTWARE.
23
+
24
+ require 'simplecov'
25
+ require 'est'
@@ -0,0 +1,123 @@
1
+ # encoding: utf-8
2
+ #
3
+ # Copyright (c) 2014 TechnoPark Corp.
4
+ # Copyright (c) 2014 Yegor Bugayenko
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ # of this software and associated documentation files (the 'Software'), to deal
8
+ # in the Software without restriction, including without limitation the rights
9
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ # copies of the Software, and to permit persons to whom the Software is
11
+ # furnished to do so, subject to the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be included in all
14
+ # copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. IN NO EVENT SHALL THE
19
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ # SOFTWARE.
23
+
24
+ require 'est/version'
25
+ require 'est/estimates'
26
+ require 'nokogiri'
27
+ require 'logger'
28
+ require 'time'
29
+
30
+ # Est main module.
31
+ # Author:: Yegor Bugayenko (yegor@teamed.io)
32
+ # Copyright:: Copyright (c) 2014 Yegor Bugayenko
33
+ # License:: MIT
34
+ module Est
35
+ # If it breaks.
36
+ class Error < StandardError
37
+ end
38
+
39
+ # If it violates XSD schema.
40
+ class SchemaError < Error
41
+ end
42
+
43
+ # Get logger.
44
+ def self.log
45
+ unless @logger
46
+ @logger = Logger.new(STDOUT)
47
+ @logger.formatter = proc { |severity, _, _, msg|
48
+ "#{severity}: #{msg.dump}\n"
49
+ }
50
+ @logger.level = Logger::ERROR
51
+ end
52
+ @logger
53
+ end
54
+
55
+ class << self
56
+ attr_writer :logger
57
+ end
58
+
59
+ # Code base abstraction
60
+ class Base
61
+ # Ctor.
62
+ # +opts+:: Options
63
+ def initialize(opts)
64
+ @opts = opts
65
+ Est.log.level = Logger::INFO if @opts.verbose?
66
+ Est.log.info "my version is #{Est::VERSION}"
67
+ end
68
+
69
+ # Generate XML.
70
+ def xml
71
+ dir = @opts.dir? ? @opts[:dir] : Dir.pwd
72
+ Est.log.info "reading #{dir}"
73
+ estimates = Estimates.new(dir)
74
+ sanitize(
75
+ Nokogiri::XML::Builder.new do |xml|
76
+ xml << "<?xml-stylesheet type='text/xsl' href='#{xsl}'?>"
77
+ xml.estimate(attrs) do
78
+ xml.total estimates.total
79
+ xml.ests do
80
+ estimates.iterate.each do |est|
81
+ xml.est('id' => est.id) do
82
+ xml.date est.date
83
+ xml.total est.total
84
+ xml.author est.author
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end.to_xml
90
+ )
91
+ end
92
+
93
+ private
94
+
95
+ def attrs
96
+ {
97
+ 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
98
+ 'xsi:noNamespaceSchemaLocation' => "#{host('xsd')}/#{Est::VERSION}.xsd",
99
+ 'version' => Est::VERSION,
100
+ 'date' => Time.now.utc.iso8601
101
+ }
102
+ end
103
+
104
+ def host(suffix)
105
+ "http://est-#{suffix}.teamed.io"
106
+ end
107
+
108
+ def xsl
109
+ "#{host('xsl')}/#{Est::VERSION}.xsl"
110
+ end
111
+
112
+ def sanitize(xml)
113
+ xsd = Nokogiri::XML::Schema(
114
+ File.read(File.join(File.dirname(__FILE__), '../assets/est.xsd'))
115
+ )
116
+ errors = xsd.validate(Nokogiri::XML(xml)).map(&:message)
117
+ errors.each { |e| Est.log.error e }
118
+ Est.log.error(xml) unless errors.empty?
119
+ fail SchemaError, errors.join('; ') unless errors.empty?
120
+ xml
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,65 @@
1
+ # encoding: utf-8
2
+ #
3
+ # Copyright (c) 2014 TechnoPark Corp.
4
+ # Copyright (c) 2014 Yegor Bugayenko
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ # of this software and associated documentation files (the 'Software'), to deal
8
+ # in the Software without restriction, including without limitation the rights
9
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ # copies of the Software, and to permit persons to whom the Software is
11
+ # furnished to do so, subject to the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be included in all
14
+ # copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. IN NO EVENT SHALL THE
19
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ # SOFTWARE.
23
+
24
+ require 'est/version'
25
+ require 'est/methods/champions'
26
+ require 'logger'
27
+ require 'yaml'
28
+
29
+ # Single estimate.
30
+ # Author:: Yegor Bugayenko (yegor@teamed.io)
31
+ # Copyright:: Copyright (c) 2014 Yegor Bugayenko
32
+ # License:: MIT
33
+ module Est
34
+ # Estimate.
35
+ class Estimate
36
+ # Ctor.
37
+ # +file+:: File with YAML estimate
38
+ def initialize(file)
39
+ @yaml = YAML.load_file(file)
40
+ fail "failed to read file #{file}" unless @yaml
41
+ end
42
+
43
+ # Get id.
44
+ def id
45
+ @yaml['id']
46
+ end
47
+
48
+ # Get date.
49
+ def date
50
+ Date.strptime(@yaml['date'], '%d-%m-%Y')
51
+ end
52
+
53
+ # Get author.
54
+ def author
55
+ @yaml['author']
56
+ end
57
+
58
+ # Get total estimate.
59
+ def total
60
+ method = @yaml['method']
61
+ fail "unsupported method #{method}" unless method == 'champions.pert'
62
+ Champions.new(@yaml).total
63
+ end
64
+ end
65
+ end