kwala 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README +62 -0
 - data/bin/kwala +39 -0
 - data/lib/kwala.rb +59 -0
 - data/lib/kwala/actions/code_change.rb +128 -0
 - data/lib/kwala/actions/code_coverage.rb +303 -0
 - data/lib/kwala/actions/code_duplication.rb +222 -0
 - data/lib/kwala/actions/code_formatting.rb +358 -0
 - data/lib/kwala/actions/cycle_detection.rb +118 -0
 - data/lib/kwala/actions/cyclomatic_complexity.rb +159 -0
 - data/lib/kwala/actions/dead_code.rb +68 -0
 - data/lib/kwala/actions/executable_files_check.rb +111 -0
 - data/lib/kwala/actions/flagged_comments.rb +128 -0
 - data/lib/kwala/actions/gem_plugin.rb +31 -0
 - data/lib/kwala/actions/loc_count.rb +153 -0
 - data/lib/kwala/actions/multi_ruby_unit_test.rb +62 -0
 - data/lib/kwala/actions/outside_links.rb +85 -0
 - data/lib/kwala/actions/rails_migrate.rb +62 -0
 - data/lib/kwala/actions/strange_requires.rb +106 -0
 - data/lib/kwala/actions/syntax_check.rb +130 -0
 - data/lib/kwala/actions/unit_test.rb +368 -0
 - data/lib/kwala/build_action.rb +88 -0
 - data/lib/kwala/build_context.rb +49 -0
 - data/lib/kwala/command_line_runner.rb +184 -0
 - data/lib/kwala/ext/demos.jar +0 -0
 - data/lib/kwala/ext/pmd.jar +0 -0
 - data/lib/kwala/ext/prefuse.jar +0 -0
 - data/lib/kwala/extensions.rb +36 -0
 - data/lib/kwala/lib/code_analyzer.rb +678 -0
 - data/lib/kwala/lib/cycle_detector.rb +793 -0
 - data/lib/kwala/lib/strange_requires_detector.rb +145 -0
 - data/lib/kwala/notification.rb +45 -0
 - data/lib/kwala/notifications/email.rb +54 -0
 - data/lib/kwala/notifications/rss.rb +151 -0
 - data/lib/kwala/project_builder_utils.rb +178 -0
 - data/lib/kwala/templates/build_template.html +33 -0
 - data/lib/kwala/templates/code_change_summary.html +27 -0
 - data/lib/kwala/templates/code_coverage_detailed.html +25 -0
 - data/lib/kwala/templates/code_coverage_summary.html +30 -0
 - data/lib/kwala/templates/code_duplication_detailed.html +45 -0
 - data/lib/kwala/templates/code_duplication_summary.html +9 -0
 - data/lib/kwala/templates/code_formatting_detailed.html +27 -0
 - data/lib/kwala/templates/code_formatting_summary.html +11 -0
 - data/lib/kwala/templates/cycle_detection_detailed.html +20 -0
 - data/lib/kwala/templates/cycle_detection_summary.html +7 -0
 - data/lib/kwala/templates/cyclomatic_complexity_summary.html +27 -0
 - data/lib/kwala/templates/executable_files_check_detailed.html +31 -0
 - data/lib/kwala/templates/executable_files_check_summary.html +20 -0
 - data/lib/kwala/templates/flagged_comments_detailed.html +22 -0
 - data/lib/kwala/templates/flagged_comments_summary.html +10 -0
 - data/lib/kwala/templates/loc_count_detailed.html +24 -0
 - data/lib/kwala/templates/loc_count_summary.html +14 -0
 - data/lib/kwala/templates/mdd/1.png +0 -0
 - data/lib/kwala/templates/mdd/2.png +0 -0
 - data/lib/kwala/templates/mdd/3.png +0 -0
 - data/lib/kwala/templates/mdd/4.png +0 -0
 - data/lib/kwala/templates/mdd/5.png +0 -0
 - data/lib/kwala/templates/outside_links_summary.html +4 -0
 - data/lib/kwala/templates/strange_requires_detailed.html +23 -0
 - data/lib/kwala/templates/strange_requires_summary.html +13 -0
 - data/lib/kwala/templates/style.css +95 -0
 - data/lib/kwala/templates/syntax_check_detailed.html +21 -0
 - data/lib/kwala/templates/syntax_check_summary.html +7 -0
 - data/lib/kwala/templates/unit_test_detailed.html +66 -0
 - data/lib/kwala/templates/unit_test_summary.html +70 -0
 - data/test/tc_build_action.rb +32 -0
 - data/test/tc_templates.rb +24 -0
 - metadata +118 -0
 
| 
         @@ -0,0 +1,145 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            #!/usr/bin/env ruby -w
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            # Copyright (c) 2006, Ubiquitous Business Technology (http://ubit.com)
         
     | 
| 
      
 4 
     | 
    
         
            +
            # All rights reserved.
         
     | 
| 
      
 5 
     | 
    
         
            +
            #
         
     | 
| 
      
 6 
     | 
    
         
            +
            # Redistribution and use in source and binary forms, with or without
         
     | 
| 
      
 7 
     | 
    
         
            +
            # modification, are permitted provided that the following conditions are
         
     | 
| 
      
 8 
     | 
    
         
            +
            # met:
         
     | 
| 
      
 9 
     | 
    
         
            +
            #
         
     | 
| 
      
 10 
     | 
    
         
            +
            #
         
     | 
| 
      
 11 
     | 
    
         
            +
            #    * Redistributions of source code must retain the above copyright
         
     | 
| 
      
 12 
     | 
    
         
            +
            #      notice, this list of conditions and the following disclaimer.
         
     | 
| 
      
 13 
     | 
    
         
            +
            #
         
     | 
| 
      
 14 
     | 
    
         
            +
            #    * Redistributions in binary form must reproduce the above
         
     | 
| 
      
 15 
     | 
    
         
            +
            #      copyright notice, this list of conditions and the following
         
     | 
| 
      
 16 
     | 
    
         
            +
            #      disclaimer in the documentation and/or other materials provided
         
     | 
| 
      
 17 
     | 
    
         
            +
            #      with the distribution.
         
     | 
| 
      
 18 
     | 
    
         
            +
            #
         
     | 
| 
      
 19 
     | 
    
         
            +
            #    * Neither the name of Ubit nor the names of its
         
     | 
| 
      
 20 
     | 
    
         
            +
            #      contributors may be used to endorse or promote products derived
         
     | 
| 
      
 21 
     | 
    
         
            +
            #      from this software without specific prior written permission.
         
     | 
| 
      
 22 
     | 
    
         
            +
            #
         
     | 
| 
      
 23 
     | 
    
         
            +
            #
         
     | 
| 
      
 24 
     | 
    
         
            +
            # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
         
     | 
| 
      
 25 
     | 
    
         
            +
            # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
         
     | 
| 
      
 26 
     | 
    
         
            +
            # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
         
     | 
| 
      
 27 
     | 
    
         
            +
            # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
         
     | 
| 
      
 28 
     | 
    
         
            +
            # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
         
     | 
| 
      
 29 
     | 
    
         
            +
            # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
         
     | 
| 
      
 30 
     | 
    
         
            +
            # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
         
     | 
| 
      
 31 
     | 
    
         
            +
            # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
         
     | 
| 
      
 32 
     | 
    
         
            +
            # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
         
     | 
| 
      
 33 
     | 
    
         
            +
            # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
         
     | 
| 
      
 34 
     | 
    
         
            +
            # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
         
     | 
| 
      
 35 
     | 
    
         
            +
            #
         
     | 
| 
      
 36 
     | 
    
         
            +
            # == Author
         
     | 
| 
      
 37 
     | 
    
         
            +
            # Michael Reinsch (mr@ubit.com)
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
            require File.dirname(__FILE__) + "/cycle_detector"   # for ReqWalker
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
            class Dir
         
     | 
| 
      
 42 
     | 
    
         
            +
              def == (other)
         
     | 
| 
      
 43 
     | 
    
         
            +
                case other
         
     | 
| 
      
 44 
     | 
    
         
            +
                when String;  return path == other
         
     | 
| 
      
 45 
     | 
    
         
            +
                when Dir;     return path == other.path
         
     | 
| 
      
 46 
     | 
    
         
            +
                when FakeDir; return path == other.path
         
     | 
| 
      
 47 
     | 
    
         
            +
                end
         
     | 
| 
      
 48 
     | 
    
         
            +
                false
         
     | 
| 
      
 49 
     | 
    
         
            +
              end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
              def is_subdir_of?(dir)
         
     | 
| 
      
 52 
     | 
    
         
            +
                return path[0..dir.path.length-1] == dir.path
         
     | 
| 
      
 53 
     | 
    
         
            +
              end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
              def cut(basedir)
         
     | 
| 
      
 56 
     | 
    
         
            +
                path
         
     | 
| 
      
 57 
     | 
    
         
            +
              end
         
     | 
| 
      
 58 
     | 
    
         
            +
            end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
            class StrangeRequiresDetector
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
              attr_writer :print_strategy
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
              def initialize(printer)
         
     | 
| 
      
 66 
     | 
    
         
            +
                @graph = Hash.new
         
     | 
| 
      
 67 
     | 
    
         
            +
                @printer = printer
         
     | 
| 
      
 68 
     | 
    
         
            +
                @file_filter = Proc.new do |files|
         
     | 
| 
      
 69 
     | 
    
         
            +
                  files.find_all do |f|
         
     | 
| 
      
 70 
     | 
    
         
            +
                    /tests\// !~ f && /extensions\// !~ f &&
         
     | 
| 
      
 71 
     | 
    
         
            +
                      /tools\// !~ f && /\/ms\// !~ f && /\/site\// !~f
         
     | 
| 
      
 72 
     | 
    
         
            +
                  end
         
     | 
| 
      
 73 
     | 
    
         
            +
                end
         
     | 
| 
      
 74 
     | 
    
         
            +
                @req_filter = Proc.new do |reqs|
         
     | 
| 
      
 75 
     | 
    
         
            +
                  reqs.find_all do |r|
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                  end
         
     | 
| 
      
 78 
     | 
    
         
            +
                end
         
     | 
| 
      
 79 
     | 
    
         
            +
              end
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
              def cut_base(filename, basename)
         
     | 
| 
      
 82 
     | 
    
         
            +
                filename.gsub(/^#{basename}/, '')
         
     | 
| 
      
 83 
     | 
    
         
            +
              end
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
              def check(dir, basedir = "")
         
     | 
| 
      
 86 
     | 
    
         
            +
                files = ReqWalker.find_ruby_files(dir)
         
     | 
| 
      
 87 
     | 
    
         
            +
                @file_filter.call(files).each do |f|
         
     | 
| 
      
 88 
     | 
    
         
            +
                  ReqWalker.get_requires(f).each do |r|
         
     | 
| 
      
 89 
     | 
    
         
            +
                    r = ReqWalker.find_file(r)
         
     | 
| 
      
 90 
     | 
    
         
            +
                    if r && /\/lib\/ruby\// !~ r
         
     | 
| 
      
 91 
     | 
    
         
            +
                      f_base = Dir.new(File.dirname(f))
         
     | 
| 
      
 92 
     | 
    
         
            +
                      r_base = Dir.new(File.dirname(r))
         
     | 
| 
      
 93 
     | 
    
         
            +
                      # well, we assume that a directory models our "class category"...
         
     | 
| 
      
 94 
     | 
    
         
            +
                      if f_base != r_base
         
     | 
| 
      
 95 
     | 
    
         
            +
                        if r_base.is_subdir_of?(f_base)
         
     | 
| 
      
 96 
     | 
    
         
            +
                          @printer.subdir_requirement(cut_base(f,basedir), cut_base(r,basedir))
         
     | 
| 
      
 97 
     | 
    
         
            +
                        else
         
     | 
| 
      
 98 
     | 
    
         
            +
                          @printer.parent_dir_requirement(cut_base(f,basedir), cut_base(r,basedir))
         
     | 
| 
      
 99 
     | 
    
         
            +
                        end
         
     | 
| 
      
 100 
     | 
    
         
            +
                      end
         
     | 
| 
      
 101 
     | 
    
         
            +
                    end
         
     | 
| 
      
 102 
     | 
    
         
            +
                  end
         
     | 
| 
      
 103 
     | 
    
         
            +
                end
         
     | 
| 
      
 104 
     | 
    
         
            +
              end
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
            end
         
     | 
| 
      
 107 
     | 
    
         
            +
             
     | 
| 
      
 108 
     | 
    
         
            +
             
     | 
| 
      
 109 
     | 
    
         
            +
            if __FILE__ == $0
         
     | 
| 
      
 110 
     | 
    
         
            +
              class STDOUTPrintStrategy
         
     | 
| 
      
 111 
     | 
    
         
            +
                def subdir_requirement(from, to)
         
     | 
| 
      
 112 
     | 
    
         
            +
                  puts "yellow: #{from} -> #{to}"
         
     | 
| 
      
 113 
     | 
    
         
            +
                end
         
     | 
| 
      
 114 
     | 
    
         
            +
                def parent_dir_requirement(from, to)
         
     | 
| 
      
 115 
     | 
    
         
            +
                  puts "red   : #{from} -> #{to}"
         
     | 
| 
      
 116 
     | 
    
         
            +
                end
         
     | 
| 
      
 117 
     | 
    
         
            +
              end
         
     | 
| 
      
 118 
     | 
    
         
            +
             
     | 
| 
      
 119 
     | 
    
         
            +
              require 'getoptlong'
         
     | 
| 
      
 120 
     | 
    
         
            +
             
     | 
| 
      
 121 
     | 
    
         
            +
              srd = StrangeRequiresDetector.new(STDOUTPrintStrategy.new)
         
     | 
| 
      
 122 
     | 
    
         
            +
              dir = "./"
         
     | 
| 
      
 123 
     | 
    
         
            +
             
     | 
| 
      
 124 
     | 
    
         
            +
              begin
         
     | 
| 
      
 125 
     | 
    
         
            +
                options = GetoptLong.new(
         
     | 
| 
      
 126 
     | 
    
         
            +
                                         ["-d","--dir", GetoptLong::REQUIRED_ARGUMENT]
         
     | 
| 
      
 127 
     | 
    
         
            +
                                         )
         
     | 
| 
      
 128 
     | 
    
         
            +
             
     | 
| 
      
 129 
     | 
    
         
            +
                options.each do |opt,arg|
         
     | 
| 
      
 130 
     | 
    
         
            +
                  case opt
         
     | 
| 
      
 131 
     | 
    
         
            +
                  when "-d"
         
     | 
| 
      
 132 
     | 
    
         
            +
                    dir = arg
         
     | 
| 
      
 133 
     | 
    
         
            +
                  else
         
     | 
| 
      
 134 
     | 
    
         
            +
                    STDERR.puts "Unknown command #{ opt }"
         
     | 
| 
      
 135 
     | 
    
         
            +
                    exit 1
         
     | 
| 
      
 136 
     | 
    
         
            +
                  end
         
     | 
| 
      
 137 
     | 
    
         
            +
                end
         
     | 
| 
      
 138 
     | 
    
         
            +
              rescue => err
         
     | 
| 
      
 139 
     | 
    
         
            +
                STDERR.puts "Error invalid command"
         
     | 
| 
      
 140 
     | 
    
         
            +
                exit 1
         
     | 
| 
      
 141 
     | 
    
         
            +
              end
         
     | 
| 
      
 142 
     | 
    
         
            +
             
     | 
| 
      
 143 
     | 
    
         
            +
              srd.check(dir)
         
     | 
| 
      
 144 
     | 
    
         
            +
             
     | 
| 
      
 145 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,45 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
             
     | 
| 
      
 2 
     | 
    
         
            +
            class Notification
         
     | 
| 
      
 3 
     | 
    
         
            +
              @@notifications = []
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
              def self.context=(context)
         
     | 
| 
      
 6 
     | 
    
         
            +
                @context = context
         
     | 
| 
      
 7 
     | 
    
         
            +
              end 
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
              def self.context
         
     | 
| 
      
 10 
     | 
    
         
            +
                @context
         
     | 
| 
      
 11 
     | 
    
         
            +
              end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
              def self.notify(event, result, data)
         
     | 
| 
      
 14 
     | 
    
         
            +
                self.notifications.each { |n| n.notify(event, result, data) }
         
     | 
| 
      
 15 
     | 
    
         
            +
              end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
              def self.add_notification(obj)
         
     | 
| 
      
 18 
     | 
    
         
            +
                self.notifications << obj
         
     | 
| 
      
 19 
     | 
    
         
            +
              end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
              def self.notifications
         
     | 
| 
      
 22 
     | 
    
         
            +
                if @@notifications.empty?
         
     | 
| 
      
 23 
     | 
    
         
            +
                  Dir.glob(NOTIFICATION_DIR + "/*.rb").sort.each do |file|
         
     | 
| 
      
 24 
     | 
    
         
            +
                    begin
         
     | 
| 
      
 25 
     | 
    
         
            +
                      require file
         
     | 
| 
      
 26 
     | 
    
         
            +
                    rescue Exception => err
         
     | 
| 
      
 27 
     | 
    
         
            +
                      $stderr.puts "Notification error loading #{file}", err.class, err.message
         
     | 
| 
      
 28 
     | 
    
         
            +
                    end
         
     | 
| 
      
 29 
     | 
    
         
            +
                  end
         
     | 
| 
      
 30 
     | 
    
         
            +
                end
         
     | 
| 
      
 31 
     | 
    
         
            +
                @@notifications
         
     | 
| 
      
 32 
     | 
    
         
            +
              end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
              def notify(event, result = nil, data = nil)
         
     | 
| 
      
 35 
     | 
    
         
            +
                if self.respond_to?(event)
         
     | 
| 
      
 36 
     | 
    
         
            +
                  self.send(event, result, data, Notification.context)
         
     | 
| 
      
 37 
     | 
    
         
            +
                else
         
     | 
| 
      
 38 
     | 
    
         
            +
                  undefined_event(event, result, data, Notification.context)
         
     | 
| 
      
 39 
     | 
    
         
            +
                end
         
     | 
| 
      
 40 
     | 
    
         
            +
              end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
              def undefined_event(event, result, data, context)
         
     | 
| 
      
 43 
     | 
    
         
            +
              end
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,54 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'net/smtp'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            class EmailNotification < Notification
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
              def unit_test(result, data, context)
         
     | 
| 
      
 6 
     | 
    
         
            +
                # TODO: Probably should move this to a configuration based
         
     | 
| 
      
 7 
     | 
    
         
            +
                from_addr = ""
         
     | 
| 
      
 8 
     | 
    
         
            +
                to_addr = [ "" ]
         
     | 
| 
      
 9 
     | 
    
         
            +
                smtp_server = ""
         
     | 
| 
      
 10 
     | 
    
         
            +
                smtp_port = 25
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                # Get the time and set the status msg
         
     | 
| 
      
 13 
     | 
    
         
            +
                now = Time.now
         
     | 
| 
      
 14 
     | 
    
         
            +
                status_msg = "Unit tests run #{result.to_s} at"
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                # just file name for now
         
     | 
| 
      
 17 
     | 
    
         
            +
                data ||= {}
         
     | 
| 
      
 18 
     | 
    
         
            +
                data = Array(data[:filtered_results]).map do |d|
         
     | 
| 
      
 19 
     | 
    
         
            +
                  d[:file_name]
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                if !data.empty?
         
     | 
| 
      
 23 
     | 
    
         
            +
                  failure_msg = <<-MSG
         
     | 
| 
      
 24 
     | 
    
         
            +
            Test files that reported failures:
         
     | 
| 
      
 25 
     | 
    
         
            +
            #{data.join("\n")}
         
     | 
| 
      
 26 
     | 
    
         
            +
            MSG
         
     | 
| 
      
 27 
     | 
    
         
            +
                else
         
     | 
| 
      
 28 
     | 
    
         
            +
                  failure_msg = ''
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                # Create the SMTP envelope
         
     | 
| 
      
 32 
     | 
    
         
            +
                msgstr = <<-END_OF_MESSAGE
         
     | 
| 
      
 33 
     | 
    
         
            +
            From: Kwala Continuous Integration <EMAIL>
         
     | 
| 
      
 34 
     | 
    
         
            +
            To: Edge Dev Mailing List <EMAIL>
         
     | 
| 
      
 35 
     | 
    
         
            +
            Subject: [Kwala] edge build #{context.vcs_num} #{result}
         
     | 
| 
      
 36 
     | 
    
         
            +
            Date: #{now.strftime("%a, %d %b %Y %X +0900")}
         
     | 
| 
      
 37 
     | 
    
         
            +
            Message-Id: <#{now.strftime("%m%d%Y%H%M%S")}@EMAIL>
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
            Result Summary:  #{result.to_s}
         
     | 
| 
      
 40 
     | 
    
         
            +
            See Results at https://BASEURL/kwala/
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
            #{failure_msg}
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
            Commit Info:
         
     | 
| 
      
 45 
     | 
    
         
            +
            #{context.vcs_info}
         
     | 
| 
      
 46 
     | 
    
         
            +
                END_OF_MESSAGE
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                # Send the message
         
     | 
| 
      
 49 
     | 
    
         
            +
                Net::SMTP.start(smtp_server, smtp_port) do |smtp|
         
     | 
| 
      
 50 
     | 
    
         
            +
                  smtp.send_message(msgstr, from_addr, *to_addr)
         
     | 
| 
      
 51 
     | 
    
         
            +
                end
         
     | 
| 
      
 52 
     | 
    
         
            +
              end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,151 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'pstore'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'rss/2.0'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'rss/utils'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            class RSSNotification < Notification
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
              def unit_test(result, data, context)
         
     | 
| 
      
 8 
     | 
    
         
            +
                @test_results = data[:test_results]
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                update_pstore(context)
         
     | 
| 
      
 11 
     | 
    
         
            +
                make_rss(context)
         
     | 
| 
      
 12 
     | 
    
         
            +
              end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
              protected
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
              PSTORE_FILE_NAME = 'prev_test_results.pstore'
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
              RSS_FILE_NAME = 'unit_test_results.rss'
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
              class UnitTestResult
         
     | 
| 
      
 21 
     | 
    
         
            +
                attr_reader :filename, :test_results, :errors, :warnings, :first_seen
         
     | 
| 
      
 22 
     | 
    
         
            +
                attr_accessor :last_seen
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                def initialize(result_hash, timestamp)
         
     | 
| 
      
 25 
     | 
    
         
            +
                  @filename = result_hash[:file_name]
         
     | 
| 
      
 26 
     | 
    
         
            +
                  @errors = result_hash[:sys_errors]
         
     | 
| 
      
 27 
     | 
    
         
            +
                  @warnings = result_hash[:warnings]
         
     | 
| 
      
 28 
     | 
    
         
            +
                  @first_seen = @last_seen = timestamp
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                  @test_results = Hash.new
         
     | 
| 
      
 31 
     | 
    
         
            +
                  result_hash[:version].each do |r|
         
     | 
| 
      
 32 
     | 
    
         
            +
                    if !r[:test_results].empty?
         
     | 
| 
      
 33 
     | 
    
         
            +
                      @test_results[r[:version_name]] = r[:test_results]
         
     | 
| 
      
 34 
     | 
    
         
            +
                    end
         
     | 
| 
      
 35 
     | 
    
         
            +
                  end
         
     | 
| 
      
 36 
     | 
    
         
            +
                end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                def pass?
         
     | 
| 
      
 39 
     | 
    
         
            +
                  @errors.empty? && @warnings.empty? && @test_results.empty?
         
     | 
| 
      
 40 
     | 
    
         
            +
                end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                def ==(obj)
         
     | 
| 
      
 43 
     | 
    
         
            +
                  res = obj.kind_of?(UnitTestResult)
         
     | 
| 
      
 44 
     | 
    
         
            +
                  res &= obj.filename == @filename
         
     | 
| 
      
 45 
     | 
    
         
            +
                  res &= obj.errors == @errors
         
     | 
| 
      
 46 
     | 
    
         
            +
                  res &= obj.warnings == @warnings
         
     | 
| 
      
 47 
     | 
    
         
            +
                  res &= obj.test_results == @test_results
         
     | 
| 
      
 48 
     | 
    
         
            +
                  res
         
     | 
| 
      
 49 
     | 
    
         
            +
                end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                def to_rss_item(compare_timestamp)
         
     | 
| 
      
 52 
     | 
    
         
            +
                  if @last_seen < compare_timestamp
         
     | 
| 
      
 53 
     | 
    
         
            +
                    defect_status = "Fixed"
         
     | 
| 
      
 54 
     | 
    
         
            +
                  else
         
     | 
| 
      
 55 
     | 
    
         
            +
                    defect_status = "New"
         
     | 
| 
      
 56 
     | 
    
         
            +
                  end
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                  item = RSS::Rss::Channel::Item.new
         
     | 
| 
      
 59 
     | 
    
         
            +
                  item.title = "#{@first_seen.strftime("%b %d, %H:%M")} - #{defect_status} defects in #{@filename}"
         
     | 
| 
      
 60 
     | 
    
         
            +
                  item.pubDate = @first_seen
         
     | 
| 
      
 61 
     | 
    
         
            +
                  item.description = defect_details_html
         
     | 
| 
      
 62 
     | 
    
         
            +
                  item
         
     | 
| 
      
 63 
     | 
    
         
            +
                end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                protected
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                def system_messages_html(messages, type)
         
     | 
| 
      
 68 
     | 
    
         
            +
                  details = ""
         
     | 
| 
      
 69 
     | 
    
         
            +
                  if !messages.empty?
         
     | 
| 
      
 70 
     | 
    
         
            +
                    details << "<h2>System #{type}s</h2>\n<pre>\n"
         
     | 
| 
      
 71 
     | 
    
         
            +
                    details << RSS::Utils.html_escape(messages.join)
         
     | 
| 
      
 72 
     | 
    
         
            +
                    details << "\n</pre>\n"
         
     | 
| 
      
 73 
     | 
    
         
            +
                  end
         
     | 
| 
      
 74 
     | 
    
         
            +
                  details
         
     | 
| 
      
 75 
     | 
    
         
            +
                end
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                def test_result_html
         
     | 
| 
      
 78 
     | 
    
         
            +
                  details = ""
         
     | 
| 
      
 79 
     | 
    
         
            +
                  @test_results.each do |ruby_ver, defects|
         
     | 
| 
      
 80 
     | 
    
         
            +
                    details << "<h2>Test Results for #{ruby_ver}</h2>\n"
         
     | 
| 
      
 81 
     | 
    
         
            +
                    defects.each do |d|
         
     | 
| 
      
 82 
     | 
    
         
            +
                      details << "<pre>\n"
         
     | 
| 
      
 83 
     | 
    
         
            +
                      details << RSS::Utils.html_escape(d[:test_info])
         
     | 
| 
      
 84 
     | 
    
         
            +
                      details << "\n</pre>\n"
         
     | 
| 
      
 85 
     | 
    
         
            +
                    end
         
     | 
| 
      
 86 
     | 
    
         
            +
                  end
         
     | 
| 
      
 87 
     | 
    
         
            +
                  details
         
     | 
| 
      
 88 
     | 
    
         
            +
                end
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
                def defect_details_html
         
     | 
| 
      
 91 
     | 
    
         
            +
                  details = ""
         
     | 
| 
      
 92 
     | 
    
         
            +
                  details << test_result_html
         
     | 
| 
      
 93 
     | 
    
         
            +
                  details << system_messages_html(@errors, "Error")
         
     | 
| 
      
 94 
     | 
    
         
            +
                  details << system_messages_html(@warnings, "Warning")
         
     | 
| 
      
 95 
     | 
    
         
            +
                  details
         
     | 
| 
      
 96 
     | 
    
         
            +
                end
         
     | 
| 
      
 97 
     | 
    
         
            +
              end
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
      
 99 
     | 
    
         
            +
              def create_pstore(context)
         
     | 
| 
      
 100 
     | 
    
         
            +
                PStore.new(File.join(context.output_directory, PSTORE_FILE_NAME))
         
     | 
| 
      
 101 
     | 
    
         
            +
              end
         
     | 
| 
      
 102 
     | 
    
         
            +
             
     | 
| 
      
 103 
     | 
    
         
            +
              def update_pstore(context)
         
     | 
| 
      
 104 
     | 
    
         
            +
                create_pstore(context).transaction do |store|
         
     | 
| 
      
 105 
     | 
    
         
            +
                  run_timestamp = Time.now
         
     | 
| 
      
 106 
     | 
    
         
            +
                  del_timestamp = run_timestamp - 12*60*60 # 12 hours
         
     | 
| 
      
 107 
     | 
    
         
            +
                  prev_test_results = store.fetch(:prev_test_results, Hash.new)
         
     | 
| 
      
 108 
     | 
    
         
            +
             
     | 
| 
      
 109 
     | 
    
         
            +
                  prev_test_results.delete_if do |key, res|
         
     | 
| 
      
 110 
     | 
    
         
            +
                    res.last_seen <= del_timestamp
         
     | 
| 
      
 111 
     | 
    
         
            +
                  end
         
     | 
| 
      
 112 
     | 
    
         
            +
             
     | 
| 
      
 113 
     | 
    
         
            +
                  @test_results.each do |cur_res_hash|
         
     | 
| 
      
 114 
     | 
    
         
            +
                    cur_res = UnitTestResult.new(cur_res_hash, run_timestamp)
         
     | 
| 
      
 115 
     | 
    
         
            +
                    next if cur_res.pass?
         
     | 
| 
      
 116 
     | 
    
         
            +
             
     | 
| 
      
 117 
     | 
    
         
            +
                    prev_res = prev_test_results[cur_res.filename]
         
     | 
| 
      
 118 
     | 
    
         
            +
                    if prev_res.nil? || cur_res != prev_res
         
     | 
| 
      
 119 
     | 
    
         
            +
                      # new defects
         
     | 
| 
      
 120 
     | 
    
         
            +
                      prev_test_results[cur_res.filename] = cur_res
         
     | 
| 
      
 121 
     | 
    
         
            +
                    else
         
     | 
| 
      
 122 
     | 
    
         
            +
                      # same'ol defect
         
     | 
| 
      
 123 
     | 
    
         
            +
                      prev_res.last_seen = run_timestamp
         
     | 
| 
      
 124 
     | 
    
         
            +
                    end
         
     | 
| 
      
 125 
     | 
    
         
            +
                  end
         
     | 
| 
      
 126 
     | 
    
         
            +
             
     | 
| 
      
 127 
     | 
    
         
            +
                  store[:last_run_timestamp] = run_timestamp
         
     | 
| 
      
 128 
     | 
    
         
            +
                  store[:prev_test_results] = prev_test_results
         
     | 
| 
      
 129 
     | 
    
         
            +
                end
         
     | 
| 
      
 130 
     | 
    
         
            +
              end
         
     | 
| 
      
 131 
     | 
    
         
            +
             
     | 
| 
      
 132 
     | 
    
         
            +
              def make_rss(context)
         
     | 
| 
      
 133 
     | 
    
         
            +
                rss = RSS::Rss.new("2.0")
         
     | 
| 
      
 134 
     | 
    
         
            +
                chan = RSS::Rss::Channel.new
         
     | 
| 
      
 135 
     | 
    
         
            +
                chan.title = chan.description = "Unit Test Results for #{context.project_name}"
         
     | 
| 
      
 136 
     | 
    
         
            +
                rss.channel = chan
         
     | 
| 
      
 137 
     | 
    
         
            +
             
     | 
| 
      
 138 
     | 
    
         
            +
                create_pstore(context).transaction(true) do |store|
         
     | 
| 
      
 139 
     | 
    
         
            +
                  last_run_timestamp = store.fetch(:last_run_timestamp, 0)
         
     | 
| 
      
 140 
     | 
    
         
            +
                  test_results = store.fetch(:prev_test_results, Hash.new)
         
     | 
| 
      
 141 
     | 
    
         
            +
             
     | 
| 
      
 142 
     | 
    
         
            +
                  test_results.each do |key, res|
         
     | 
| 
      
 143 
     | 
    
         
            +
                    chan.items << res.to_rss_item(last_run_timestamp)
         
     | 
| 
      
 144 
     | 
    
         
            +
                  end
         
     | 
| 
      
 145 
     | 
    
         
            +
                end
         
     | 
| 
      
 146 
     | 
    
         
            +
             
     | 
| 
      
 147 
     | 
    
         
            +
                File.open(File.join(context.output_directory, RSS_FILE_NAME), "w") {|f| f.puts(rss.to_s) }
         
     | 
| 
      
 148 
     | 
    
         
            +
              end
         
     | 
| 
      
 149 
     | 
    
         
            +
             
     | 
| 
      
 150 
     | 
    
         
            +
             
     | 
| 
      
 151 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,178 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module ProjectBuilderUtils
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
              def ProjectBuilderUtils.find_ruby_files(dir)
         
     | 
| 
      
 4 
     | 
    
         
            +
                ruby_files = Array.new
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                Find.find(dir) do |path|
         
     | 
| 
      
 7 
     | 
    
         
            +
                  if File.directory?(path)
         
     | 
| 
      
 8 
     | 
    
         
            +
                    # Ignore all directories starting with .
         
     | 
| 
      
 9 
     | 
    
         
            +
                    if File.basename(path)[0] == ?. || File.basename(path) =~ /vendor/
         
     | 
| 
      
 10 
     | 
    
         
            +
                      Find.prune
         
     | 
| 
      
 11 
     | 
    
         
            +
                    else
         
     | 
| 
      
 12 
     | 
    
         
            +
                      next
         
     | 
| 
      
 13 
     | 
    
         
            +
                    end
         
     | 
| 
      
 14 
     | 
    
         
            +
                  elsif File.file?(path)
         
     | 
| 
      
 15 
     | 
    
         
            +
                    case path.to_s
         
     | 
| 
      
 16 
     | 
    
         
            +
                    when /\.rb$/
         
     | 
| 
      
 17 
     | 
    
         
            +
                      ruby_files << path
         
     | 
| 
      
 18 
     | 
    
         
            +
                    when /\.rb~$/
         
     | 
| 
      
 19 
     | 
    
         
            +
                      next
         
     | 
| 
      
 20 
     | 
    
         
            +
                    when ProjectBuilderUtils.contains_ruby_execution_command?(path)
         
     | 
| 
      
 21 
     | 
    
         
            +
                      ruby_files << path
         
     | 
| 
      
 22 
     | 
    
         
            +
                    else
         
     | 
| 
      
 23 
     | 
    
         
            +
                    end
         
     | 
| 
      
 24 
     | 
    
         
            +
                  end
         
     | 
| 
      
 25 
     | 
    
         
            +
                end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                ruby_files = ruby_files.sort
         
     | 
| 
      
 28 
     | 
    
         
            +
              end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
              def ProjectBuilderUtils.strip_base_dir(base_dir, file)
         
     | 
| 
      
 31 
     | 
    
         
            +
                m = /(#{base_dir})(.*)/.match(file)
         
     | 
| 
      
 32 
     | 
    
         
            +
                m[2][1..-1]
         
     | 
| 
      
 33 
     | 
    
         
            +
              end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
              def ProjectBuilderUtils.get_ruby_and_unit_test_files(dir=Dir.pwd)
         
     | 
| 
      
 36 
     | 
    
         
            +
                ruby_files = ProjectBuilderUtils.find_ruby_files(dir)
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                # remove all test case files
         
     | 
| 
      
 39 
     | 
    
         
            +
                test_files, ruby_files = ruby_files.partition do |f|
         
     | 
| 
      
 40 
     | 
    
         
            +
                  f =~ /\/(tc|ts)_.+\.rb$|.*_test.rb$/
         
     | 
| 
      
 41 
     | 
    
         
            +
                end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                # XXX: UGLY HACK TO AVOID SIMULATION
         
     | 
| 
      
 44 
     | 
    
         
            +
                test_files.delete_if { |f| f =~ /test\/simulation\// }
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                [ruby_files.uniq, test_files.sort.uniq]
         
     | 
| 
      
 47 
     | 
    
         
            +
              end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
              def ProjectBuilderUtils.clean_trailing_slash(dir)
         
     | 
| 
      
 50 
     | 
    
         
            +
                if dir[dir.size - 1] == ?/
         
     | 
| 
      
 51 
     | 
    
         
            +
                  if dir.size > 1
         
     | 
| 
      
 52 
     | 
    
         
            +
                    dir = dir[0..dir.size - 2]
         
     | 
| 
      
 53 
     | 
    
         
            +
                  else
         
     | 
| 
      
 54 
     | 
    
         
            +
                    dir = "."
         
     | 
| 
      
 55 
     | 
    
         
            +
                  end
         
     | 
| 
      
 56 
     | 
    
         
            +
                end
         
     | 
| 
      
 57 
     | 
    
         
            +
                dir
         
     | 
| 
      
 58 
     | 
    
         
            +
              end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
              def ProjectBuilderUtils.expand_template(template, data)
         
     | 
| 
      
 62 
     | 
    
         
            +
                str = StringIO.new
         
     | 
| 
      
 63 
     | 
    
         
            +
                template.expand(str, data)
         
     | 
| 
      
 64 
     | 
    
         
            +
                str.string
         
     | 
| 
      
 65 
     | 
    
         
            +
              end
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
              def ProjectBuilderUtils.load_actions
         
     | 
| 
      
 68 
     | 
    
         
            +
                Dir.glob(ACTION_DIR + "/*.rb").sort.each do |action_file|
         
     | 
| 
      
 69 
     | 
    
         
            +
                  require(action_file)
         
     | 
| 
      
 70 
     | 
    
         
            +
                end
         
     | 
| 
      
 71 
     | 
    
         
            +
              end
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
              def ProjectBuilderUtils.contains_ruby_execution_command?(path)
         
     | 
| 
      
 74 
     | 
    
         
            +
                File.open(path, "r") do |f|
         
     | 
| 
      
 75 
     | 
    
         
            +
                  return (f.read(1024).to_s =~ /^#!.*ruby/)
         
     | 
| 
      
 76 
     | 
    
         
            +
                end
         
     | 
| 
      
 77 
     | 
    
         
            +
              end
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
              def ProjectBuilderUtils.expand_summary_template(data, project_name, summaries)
         
     | 
| 
      
 81 
     | 
    
         
            +
                template = TemplateFile.new("#{TEMPLATE_DIR}/build_template.html")
         
     | 
| 
      
 82 
     | 
    
         
            +
                data[:action_summaries] = summaries
         
     | 
| 
      
 83 
     | 
    
         
            +
                ProjectBuilderUtils.expand_template(template, data)
         
     | 
| 
      
 84 
     | 
    
         
            +
              end
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
              def ProjectBuilderUtils.make_summary_report(context, summaries)
         
     | 
| 
      
 87 
     | 
    
         
            +
                if context.vcs_num
         
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
      
 89 
     | 
    
         
            +
                  context.amrita_data[:vcs] = {
         
     | 
| 
      
 90 
     | 
    
         
            +
                    :vcs_num => context.vcs_num,
         
     | 
| 
      
 91 
     | 
    
         
            +
                    :vcs_info => context.vcs_info
         
     | 
| 
      
 92 
     | 
    
         
            +
                  }
         
     | 
| 
      
 93 
     | 
    
         
            +
                end
         
     | 
| 
      
 94 
     | 
    
         
            +
                context.build_complete
         
     | 
| 
      
 95 
     | 
    
         
            +
             
     | 
| 
      
 96 
     | 
    
         
            +
                exp_res = ProjectBuilderUtils.expand_summary_template(context.amrita_data, context.project_name, summaries)
         
     | 
| 
      
 97 
     | 
    
         
            +
                if context.output_directory
         
     | 
| 
      
 98 
     | 
    
         
            +
                  sum_file = "#{context.output_directory}/index.html"
         
     | 
| 
      
 99 
     | 
    
         
            +
                  File.open(sum_file, "w") do |f|
         
     | 
| 
      
 100 
     | 
    
         
            +
                    f.puts exp_res
         
     | 
| 
      
 101 
     | 
    
         
            +
                  end
         
     | 
| 
      
 102 
     | 
    
         
            +
                else
         
     | 
| 
      
 103 
     | 
    
         
            +
                  STDOUT.puts exp_res
         
     | 
| 
      
 104 
     | 
    
         
            +
                end
         
     | 
| 
      
 105 
     | 
    
         
            +
              end
         
     | 
| 
      
 106 
     | 
    
         
            +
             
     | 
| 
      
 107 
     | 
    
         
            +
              # Pull this into better place
         
     | 
| 
      
 108 
     | 
    
         
            +
              def ProjectBuilderUtils.calc_score(context, action_instances)
         
     | 
| 
      
 109 
     | 
    
         
            +
                scores = action_instances.map { |a| a.score }
         
     | 
| 
      
 110 
     | 
    
         
            +
                scores.compact!
         
     | 
| 
      
 111 
     | 
    
         
            +
             
     | 
| 
      
 112 
     | 
    
         
            +
                # If action doesn't calculate score, just default to 1 for now
         
     | 
| 
      
 113 
     | 
    
         
            +
                if !scores.empty?
         
     | 
| 
      
 114 
     | 
    
         
            +
                  total = scores.inject(0) { |s,v| s + v }
         
     | 
| 
      
 115 
     | 
    
         
            +
             
     | 
| 
      
 116 
     | 
    
         
            +
                  #    puts "Total : #{total} SIZE: #{scores.size}"
         
     | 
| 
      
 117 
     | 
    
         
            +
                  score = ((total / (scores.size * 10).to_f) * 10).ceil
         
     | 
| 
      
 118 
     | 
    
         
            +
                  img_num = ProjectBuilderUtils.image_for_score(score)
         
     | 
| 
      
 119 
     | 
    
         
            +
                  #    puts "!!!!!!!!!!!!!!!!!! SCORE : #{score} IMG #{img_num} !!!!!!!!!!!"
         
     | 
| 
      
 120 
     | 
    
         
            +
                else
         
     | 
| 
      
 121 
     | 
    
         
            +
                  img_num = 1
         
     | 
| 
      
 122 
     | 
    
         
            +
                end
         
     | 
| 
      
 123 
     | 
    
         
            +
                # Mascot Driven Development!
         
     | 
| 
      
 124 
     | 
    
         
            +
                context.amrita_data[:mdd] = Amrita.a(:src => "mdd/#{img_num}.png")
         
     | 
| 
      
 125 
     | 
    
         
            +
              end
         
     | 
| 
      
 126 
     | 
    
         
            +
             
     | 
| 
      
 127 
     | 
    
         
            +
              def ProjectBuilderUtils.image_for_score(score)
         
     | 
| 
      
 128 
     | 
    
         
            +
                case score
         
     | 
| 
      
 129 
     | 
    
         
            +
                when 8..10 : 1
         
     | 
| 
      
 130 
     | 
    
         
            +
                when 6..7 : 2
         
     | 
| 
      
 131 
     | 
    
         
            +
                when 4..5 : 3
         
     | 
| 
      
 132 
     | 
    
         
            +
                when 3..2 : 4
         
     | 
| 
      
 133 
     | 
    
         
            +
                else
         
     | 
| 
      
 134 
     | 
    
         
            +
                  5
         
     | 
| 
      
 135 
     | 
    
         
            +
                end
         
     | 
| 
      
 136 
     | 
    
         
            +
              end
         
     | 
| 
      
 137 
     | 
    
         
            +
             
     | 
| 
      
 138 
     | 
    
         
            +
              def ProjectBuilderUtils.make_detailed_reports(context, action_instances)
         
     | 
| 
      
 139 
     | 
    
         
            +
                if context.output_directory
         
     | 
| 
      
 140 
     | 
    
         
            +
                  action_instances.each do |action|
         
     | 
| 
      
 141 
     | 
    
         
            +
                    det_file, det_res = action.detailed_display(context)
         
     | 
| 
      
 142 
     | 
    
         
            +
                    if det_file
         
     | 
| 
      
 143 
     | 
    
         
            +
                      File.open(det_file, "w") do |f|
         
     | 
| 
      
 144 
     | 
    
         
            +
                        f.puts det_res
         
     | 
| 
      
 145 
     | 
    
         
            +
                      end
         
     | 
| 
      
 146 
     | 
    
         
            +
                    end
         
     | 
| 
      
 147 
     | 
    
         
            +
                  end
         
     | 
| 
      
 148 
     | 
    
         
            +
                end
         
     | 
| 
      
 149 
     | 
    
         
            +
             
     | 
| 
      
 150 
     | 
    
         
            +
                ProjectBuilderUtils.calc_score(context, action_instances)
         
     | 
| 
      
 151 
     | 
    
         
            +
              end
         
     | 
| 
      
 152 
     | 
    
         
            +
             
     | 
| 
      
 153 
     | 
    
         
            +
              # returns an array of the actions that were successfully built
         
     | 
| 
      
 154 
     | 
    
         
            +
              def ProjectBuilderUtils.build_actions(action_instances, context)
         
     | 
| 
      
 155 
     | 
    
         
            +
                success = Array.new
         
     | 
| 
      
 156 
     | 
    
         
            +
             
     | 
| 
      
 157 
     | 
    
         
            +
                action_instances.each do |action|  
         
     | 
| 
      
 158 
     | 
    
         
            +
                  begin
         
     | 
| 
      
 159 
     | 
    
         
            +
                    action.build_action(context)
         
     | 
| 
      
 160 
     | 
    
         
            +
                    success << action
         
     | 
| 
      
 161 
     | 
    
         
            +
                  rescue KwalaAbortBuildException => err        
         
     | 
| 
      
 162 
     | 
    
         
            +
                    # We want to failed action, but stop the others
         
     | 
| 
      
 163 
     | 
    
         
            +
                    success << action
         
     | 
| 
      
 164 
     | 
    
         
            +
                    break
         
     | 
| 
      
 165 
     | 
    
         
            +
                  end
         
     | 
| 
      
 166 
     | 
    
         
            +
                end    
         
     | 
| 
      
 167 
     | 
    
         
            +
             
     | 
| 
      
 168 
     | 
    
         
            +
                success
         
     | 
| 
      
 169 
     | 
    
         
            +
              end
         
     | 
| 
      
 170 
     | 
    
         
            +
             
     | 
| 
      
 171 
     | 
    
         
            +
              def ProjectBuilderUtils.build_summaries(action_instances, context)
         
     | 
| 
      
 172 
     | 
    
         
            +
                action_instances.map do |action|
         
     | 
| 
      
 173 
     | 
    
         
            +
                  { :summary => action.summary_display(context) }
         
     | 
| 
      
 174 
     | 
    
         
            +
                end
         
     | 
| 
      
 175 
     | 
    
         
            +
              end
         
     | 
| 
      
 176 
     | 
    
         
            +
             
     | 
| 
      
 177 
     | 
    
         
            +
             
     | 
| 
      
 178 
     | 
    
         
            +
            end
         
     |