test/unit/graphviz_test.rb in rails-erd-0.2.0 vs test/unit/graphviz_test.rb in rails-erd-0.3.0

- old
+ new

@@ -1,140 +1,298 @@ require File.expand_path("../test_helper", File.dirname(__FILE__)) class GraphvizTest < ActiveSupport::TestCase def setup - RailsERD.options.file_type = :dot + RailsERD.options.filetype = :png + RailsERD.options.warn = false load "rails_erd/diagram/graphviz.rb" end def teardown - FileUtils.rm "ERD.dot" rescue nil + FileUtils.rm Dir["ERD.*"] rescue nil RailsERD::Diagram.send :remove_const, :Graphviz end - def diagram - @diagram ||= Diagram::Graphviz.new(Domain.generate).tap do |diagram| + def diagram(options = {}) + @diagram ||= Diagram::Graphviz.new(Domain.generate(options), options).tap do |diagram| diagram.generate end end def find_dot_nodes(diagram) [].tap do |nodes| - diagram.graph.each_node do |node| + diagram.graph.each_node do |name, node| nodes << node end end end - def find_dot_edges(diagram) + def find_dot_node(diagram, name) + diagram.graph.get_node(name) + end + + def find_dot_node_pairs(diagram) [].tap do |edges| diagram.graph.each_edge do |edge| edges << [edge.node_one, edge.node_two] end end end + def find_dot_edges(diagram) + [].tap do |edges| + diagram.graph.each_edge do |edge| + edges << edge + end + end + end + + def find_dot_edge_styles(diagram) + find_dot_edges(diagram).map { |e| [e[:arrowtail].to_s.tr('"', ''), e[:arrowhead].to_s.tr('"', '')] } + end + # Diagram properties ======================================================= test "file name should depend on file type" do create_simple_domain begin - assert_equal "ERD.svg", Diagram::Graphviz.create(:file_type => :svg) + assert_equal "ERD.svg", Diagram::Graphviz.create(:filetype => :svg) ensure FileUtils.rm "ERD.svg" rescue nil end end + test "rank direction should be lr for horizontal orientation" do + create_simple_domain + assert_equal '"LR"', diagram(:orientation => :horizontal).graph[:rankdir].to_s + end + + test "rank direction should be tb for vertical orientation" do + create_simple_domain + assert_equal '"TB"', diagram(:orientation => :vertical).graph[:rankdir].to_s + end + # Diagram generation ======================================================= - test "create should create output based on domain model" do + test "create should create output for domain with attributes" do create_model "Foo", :bar => :references, :column => :string do belongs_to :bar end create_model "Bar", :column => :string Diagram::Graphviz.create - assert File.exists?("ERD.dot") + assert File.exists?("ERD.png") end - test "create should create output based on domain without attributes" do - create_model "Foo", :bar => :references do - belongs_to :bar - end - create_model "Bar" + test "create should create output for domain without attributes" do + create_simple_domain Diagram::Graphviz.create + assert File.exists?("ERD.png") + end + + test "create should write to file with dot extension if type is dot" do + create_simple_domain + Diagram::Graphviz.create :filetype => :dot assert File.exists?("ERD.dot") end + + test "create should write to file with dot extension without requiring graphviz" do + create_simple_domain + begin + GraphViz.class_eval do + alias_method :old_output_and_errors_from_command, :output_and_errors_from_command + def output_and_errors_from_command(*args); raise end + end + assert_nothing_raised do + Diagram::Graphviz.create :filetype => :dot + end + ensure + GraphViz.class_eval do + alias_method :output_and_errors_from_command, :old_output_and_errors_from_command + end + end + end - test "create should create vertical output based on domain model" do + test "create should create output for domain with attributes if orientation is vertical" do create_model "Foo", :bar => :references, :column => :string do belongs_to :bar end create_model "Bar", :column => :string Diagram::Graphviz.create(:orientation => :vertical) - assert File.exists?("ERD.dot") + assert File.exists?("ERD.png") end - test "create should create vertical output based on domain without attributes" do - create_model "Foo", :bar => :references do - belongs_to :bar - end - create_model "Bar" + test "create should create output for domain if orientation is vertical" do + create_simple_domain Diagram::Graphviz.create(:orientation => :vertical) - assert File.exists?("ERD.dot") + assert File.exists?("ERD.png") end test "create should not create output if there are no connected models" do Diagram::Graphviz.create rescue nil - assert !File.exists?("ERD.dot") + assert !File.exists?("ERD.png") end test "create should abort and complain if there are no connected models" do message = nil begin Diagram::Graphviz.create rescue => e message = e.message end - assert_match /No \(connected\) entities found/, message + assert_match /No entities found/, message end + test "create should write to given file name plus extension if present" do + begin + create_simple_domain + Diagram::Graphviz.create :filename => "foobar" + assert File.exists?("foobar.png") + ensure + FileUtils.rm "foobar.png" rescue nil + end + end + # Graphviz output ========================================================== test "generate should create directed graph" do create_simple_domain assert_equal "digraph", diagram.graph.type end + test "generate should add title to graph" do + create_simple_domain + assert_equal '"Domain model\n\n"', diagram.graph.graph[:label].to_s + end + + test "generate should add title with application name to graph" do + begin + Object::Quux = Module.new + Object::Quux::Application = Class.new + Object::Rails = Struct.new(:application).new(Object::Quux::Application.new) + create_simple_domain + assert_equal '"Quux domain model\n\n"', diagram.graph.graph[:label].to_s + ensure + Object::Quux.send :remove_const, :Application + Object.send :remove_const, :Quux + Object.send :remove_const, :Rails + end + end + + test "generate should omit title if set to false" do + create_simple_domain + assert_equal "", diagram(:title => false).graph.graph[:label].to_s + end + test "generate should create node for each entity" do create_model "Foo", :bar => :references do belongs_to :bar end create_model "Bar" - assert_equal ["Bar", "Foo"], find_dot_nodes(diagram).sort + assert_equal ["Bar", "Foo"], find_dot_nodes(diagram).map(&:id).sort end test "generate should add label for entities" do create_model "Foo", :bar => :references do belongs_to :bar end create_model "Bar" - assert_match %r{<\w+.*?>Bar</\w+>}, - diagram.graph.get_node(find_dot_nodes(diagram).first)[:label].to_gv + assert_match %r{<\w+.*?>Bar</\w+>}, find_dot_node(diagram, "Bar")[:label].to_gv end test "generate should add attributes to entity labels" do create_model "Foo", :bar => :references do belongs_to :bar end create_model "Bar", :column => :string - assert_match %r{<\w+.*?>column <\w+.*?>str</\w+.*?>}, - diagram.graph.get_node(find_dot_nodes(diagram).first)[:label].to_gv + assert_match %r{<\w+.*?>column <\w+.*?>string</\w+.*?>}, find_dot_node(diagram, "Bar")[:label].to_gv end + test "generate should not add any attributes to entity labels if attributes is set to false" do + create_model "Jar", :contents => :string + create_model "Lid", :jar => :references do + belongs_to :jar + end + assert_not_match %r{contents}, find_dot_node(diagram(:attributes => false), "Jar")[:label].to_gv + end test "generate should create edge for each relationship" do create_model "Foo", :bar => :references do belongs_to :bar end create_model "Bar", :foo => :references do belongs_to :foo end - assert_equal [["Bar", "Foo"], ["Foo", "Bar"]], find_dot_edges(diagram).sort + assert_equal [["Bar", "Foo"], ["Foo", "Bar"]], find_dot_node_pairs(diagram).sort + end + + test "node records should have direction reversing braces for vertical orientation" do + create_model "Book", :author => :references do + belongs_to :author + end + create_model "Author", :name => :string + assert_match %r(\A<\{\s*<.*\|.*>\s*\}>\Z)m, find_dot_node(diagram(:orientation => :vertical), "Author")[:label].to_gv + end + + test "node records should not have direction reversing braces for horizontal orientation" do + create_model "Book", :author => :references do + belongs_to :author + end + create_model "Author", :name => :string + assert_match %r(\A<\s*<.*\|.*>\s*>\Z)m, find_dot_node(diagram(:orientation => :horizontal), "Author")[:label].to_gv + end + + # Simple notation style ==================================================== + test "generate should use no style for one to one cardinalities with simple notation" do + create_one_to_one_assoc_domain + assert_equal [["none", "none"]], find_dot_edge_styles(diagram(:notation => :simple)) + end + + test "generate should use normal arrow head for one to many cardinalities with simple notation" do + create_one_to_many_assoc_domain + assert_equal [["none", "normal"]], find_dot_edge_styles(diagram(:notation => :simple)) + end + + test "generate should use normal arrow head and tail for many to many cardinalities with simple notation" do + create_many_to_many_assoc_domain + assert_equal [["normal", "normal"]], find_dot_edge_styles(diagram(:notation => :simple)) + end + + # Advanced notation style =================================================== + test "generate should use open dots for one to one cardinalities with advanced notation" do + create_one_to_one_assoc_domain + assert_equal [["odot", "odot"]], find_dot_edge_styles(diagram(:notation => :advanced)) + end + + test "generate should use dots for mandatory one to one cardinalities with advanced notation" do + create_one_to_one_assoc_domain + One.class_eval do + validates_presence_of :other + end + assert_equal [["odot", "dot"]], find_dot_edge_styles(diagram(:notation => :advanced)) + end + + test "generate should use normal arrow and open dot head with dot tail for one to many cardinalities with advanced notation" do + create_one_to_many_assoc_domain + assert_equal [["odot", "odotnormal"]], find_dot_edge_styles(diagram(:notation => :advanced)) + end + + test "generate should use normal arrow and dot head for mandatory one to many cardinalities with advanced notation" do + create_one_to_many_assoc_domain + One.class_eval do + validates_presence_of :many + end + assert_equal [["odot", "dotnormal"]], find_dot_edge_styles(diagram(:notation => :advanced)) + end + + test "generate should use normal arrow and open dot head and tail for many to many cardinalities with advanced notation" do + create_many_to_many_assoc_domain + assert_equal [["odotnormal", "odotnormal"]], find_dot_edge_styles(diagram(:notation => :advanced)) + end + + test "generate should use normal arrow and dot tail and head for mandatory many to many cardinalities with advanced notation" do + create_many_to_many_assoc_domain + Many.class_eval do + validates_presence_of :more + end + More.class_eval do + validates_presence_of :many + end + assert_equal [["dotnormal", "dotnormal"]], find_dot_edge_styles(diagram(:notation => :advanced)) end end