Permalink
Browse files

Improve validation.

  • Loading branch information...
1 parent 879b386 commit 4023e0503d80630ac804f293d8e92c2aefabdced @threedaymonk threedaymonk committed Mar 22, 2012
Showing with 120 additions and 25 deletions.
  1. +7 −1 lib/sibyl/errors.rb
  2. +29 −12 lib/sibyl/graph.rb
  3. +20 −5 lib/sibyl/unit.rb
  4. +62 −5 test/graph_validation_test.rb
  5. +2 −2 test/step_test.rb
View
@@ -1,3 +1,9 @@
module Sibyl
- ValidationError = Class.new(RuntimeError)
+ RuleError = Class.new(StandardError)
+ InvalidGraph = Class.new(RuleError)
+ InvalidNode = Class.new(RuleError)
+
+ UserError = Class.new(RuntimeError)
+ InvalidInput = Class.new(UserError)
+ PreconditionFailed = Class.new(UserError)
end
View
@@ -1,3 +1,4 @@
+require "sibyl/errors"
require "sibyl/parser"
require "sibyl/ruby_transform"
require "tsort"
@@ -16,11 +17,16 @@ def initialize(source)
@steps_by_name = Hash[@steps.map { |s| [s.name, s] }]
end
- def valid?
- @steps.any? &&
- !has_unreachable_steps? &&
- !has_unresolved_targets? &&
- !has_cycles?
+ def validate!
+ validate_has_steps!
+ @steps.each do |step|
+ step.validate!
+ end
+ validate_no_unreachable_steps!
+ validate_no_unresolved_targets!
+ validate_no_cycles!
+ rescue InvalidNode => e
+ raise InvalidGraph.new(e)
end
def l10n_keys
@@ -30,19 +36,30 @@ def l10n_keys
end
private
- def has_unreachable_steps?
- (step_names - target_names - [first_step_name]).any?
+ def validate_has_steps!
+ if @steps.empty?
+ raise InvalidGraph, "no steps"
+ end
end
- def has_unresolved_targets?
- (target_names - step_names).any?
+ def validate_no_unreachable_steps!
+ unreachable = step_names - target_names - [first_step_name]
+ if unreachable.any?
+ raise InvalidGraph, "unreachable step(s): #{unreachable.join("; ")}"
+ end
end
- def has_cycles?
+ def validate_no_unresolved_targets!
+ unresolved = target_names - step_names
+ if unresolved.any?
+ raise InvalidGraph, "unresolved step(s): #{unresolved.join("; ")}"
+ end
+ end
+
+ def validate_no_cycles!
tsort
- false
rescue TSort::Cyclic
- true
+ raise InvalidGraph, "graph is cyclic"
end
def first_step_name
View
@@ -61,6 +61,10 @@ def branches
body.select(&:branch?)
end
+ def options
+ body.select(&:option?)
+ end
+
def statements
body.select(&:statement?)
end
@@ -82,15 +86,23 @@ def compute(input, context)
result = o.compute(context)
return result if result
end
- raise ValidationError
+ raise InvalidInput
end
def l10n_keys
prefix = keyify(name)
standard = ["#{prefix}.title"]
- body.select(&:option?).inject(standard) { |keys, option|
- keys << "#{prefix}.options.#{option.text}"
- }
+ standard + options.map { |option| "#{prefix}.options.#{option.text}" }
+ end
+
+ def validate!
+ if type.to_s == "option"
+ raise InvalidNode, "option step '#{name}' has no options" if options.none?
+ raise InvalidNode, "option step '#{name}' has non-option branches" if (branches - options).any?
+ else
+ raise InvalidNode, "non-option step '#{name}' has options" if options.any?
+ raise InvalidNode, "step '#{name}' has no outputs" if branches.none?
+ end
end
private
@@ -110,6 +122,9 @@ def exits
def body
[]
end
+
+ def validate!
+ end
end
class Jump < Node
@@ -176,7 +191,7 @@ class Reject < AbstractStatement
construct_with :expression
def execute(context)
- raise ValidationError if evaluate(context)
+ raise PreconditionFailed if evaluate(context)
end
end
end
@@ -13,7 +13,7 @@ def graph(source)
outcome b
})
- assert g.valid?
+ g.validate!
end
it "should be invalid if a step is unreachable" do
@@ -25,7 +25,9 @@ def graph(source)
outcome c
})
- refute g.valid?
+ assert_raises Sibyl::InvalidGraph do
+ g.validate!
+ end
end
it "should be invalid if a target is unresolved" do
@@ -36,13 +38,17 @@ def graph(source)
outcome b
})
- refute g.valid?
+ assert_raises Sibyl::InvalidGraph do
+ g.validate!
+ end
end
it "should be invalid if there are no steps" do
g = graph("")
- refute g.valid?
+ assert_raises Sibyl::InvalidGraph do
+ g.validate!
+ end
end
it "should be invalid if there are cycles" do
@@ -55,6 +61,57 @@ def graph(source)
outcome c
})
- refute g.valid?
+ assert_raises Sibyl::InvalidGraph do
+ g.validate!
+ end
+ end
+
+ it "should be invalid if a multiple step has no options" do
+ g = graph(%{
+ step number a
+ go -> c
+ step option b
+ outcome c
+ })
+
+ assert_raises Sibyl::InvalidGraph do
+ g.validate!
+ end
+ end
+
+ it "should be invalid if a multiple step has a go" do
+ g = graph(%{
+ step option a
+ option foo -> b
+ go -> c
+ outcome b
+ outcome c
+ })
+
+ assert_raises Sibyl::InvalidGraph do
+ g.validate!
+ end
+ end
+
+ it "should be invalid if a non-multiple step has options" do
+ g = graph(%{
+ step number a
+ option foo -> b
+ outcome b
+ })
+
+ assert_raises Sibyl::InvalidGraph do
+ g.validate!
+ end
+ end
+
+ it "should be invalid if a step has no go declarations" do
+ g = graph(%{
+ step number a
+ })
+
+ assert_raises Sibyl::InvalidGraph do
+ g.validate!
+ end
end
end
View
@@ -130,7 +130,7 @@ def ruby(source)
go -> b
})
- assert_raises Sibyl::ValidationError do
+ assert_raises Sibyl::PreconditionFailed do
step.compute(2, OpenStruct.new)
end
assert_equal "b", step.compute(1, OpenStruct.new)
@@ -143,7 +143,7 @@ def ruby(source)
option bar -> c
})
- assert_raises Sibyl::ValidationError do
+ assert_raises Sibyl::InvalidInput do
step.compute "baz", OpenStruct.new
end
end

0 comments on commit 4023e05

Please sign in to comment.