######################################################################################################################## # OpenStudio(R), Copyright (c) 2008-2018, Alliance for Sustainable Energy, LLC. All rights reserved. # # Redistribution and use in source and binary forms, with or without modification, are permitted provided that the # following conditions are met: # # (1) Redistributions of source code must retain the above copyright notice, this list of conditions and the following # disclaimer. # # (2) Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the # following disclaimer in the documentation and/or other materials provided with the distribution. # # (3) Neither the name of the copyright holder nor the names of any contributors may be used to endorse or promote # products derived from this software without specific prior written permission from the respective party. # # (4) Other than as required in clauses (1) and (2), distributions in any form of modifications or other derivative # works may not use the "OpenStudio" trademark, "OS", "os", or any other confusingly similar designation without # specific prior written permission from Alliance for Sustainable Energy, LLC. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER, THE UNITED STATES GOVERNMENT, OR ANY CONTRIBUTORS BE LIABLE FOR # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ######################################################################################################################## module OpenStudioMeasureTester class RubocopResult attr_reader :error_status attr_reader :file_issues attr_reader :file_info attr_reader :file_warnings attr_reader :file_errors attr_reader :total_measures attr_reader :total_files attr_reader :total_issues attr_reader :total_info attr_reader :total_warnings attr_reader :total_errors attr_reader :summary attr_reader :by_measure def initialize(path_to_results) @path_to_results = path_to_results @error_status = false @total_files = 0 @total_issues = 0 @total_errors = 0 @total_warnings = 0 @total_info = 0 @total_measures = 0 @summary = {} @by_measure = {} parse_results to_file end def parse_results Dir[File.join(@path_to_results,'rubocop-results.xml')].each do |file| puts "Parsing Rubocop report #{file}" measure_names = [] @total_files = 0 doc = REXML::Document.new(File.open(file)).root puts "Finished reading #{file}" # Go through the XML and find all the measure names first doc.elements.each('//checkstyle/file') do |rc_file| @total_files += 1 measure_name = rc_file.attributes['name'] if measure_name # If the user runs the tests from within the directory (where the measure.rb) file exists, then the # measure name is not included in the XML. Therefore, the measure_name will need to be abstracted by # the name of the directory relative to where the test_results are. if measure_name.include? File::SEPARATOR parts = measure_name.split(File::SEPARATOR) if parts.last == 'measure.rb' measure_names << parts[-2] end elsif measure_name == 'measure.rb' # the measure name must be extracted from the directory parts = file.split(File::SEPARATOR) measure_names << parts[-4] end end end @total_measures = measure_names.length # now go find the specific data about the measure measure_names.each do |measure_name| mhash = {} mhash['measure_issues'] = 0 mhash['measure_info'] = 0 mhash['measure_warnings'] = 0 mhash['measure_errors'] = 0 mhash['files'] = [] cn = '' doc.elements.each('//checkstyle/file') do |rc_file| # Allow processing when the file is just the measure.rb if rc_file.attributes['name'] != 'measure.rb' if !rc_file.attributes['name'].include? measure_name next end end # Save off the file information fhash = {} if rc_file.attributes['name'].include? File::SEPARATOR fhash['file_name'] = rc_file.attributes['name'].split(File::SEPARATOR)[-1] else fhash['file_name'] = rc_file.attributes['name'] end fhash['violations'] = [] # get the class name out of the measure file! wow, okay... sure why not. if fhash['file_name'] == 'measure.rb' if File.exist? rc_file.attributes['name'] File.readlines(rc_file.attributes['name']).each do |line| if (line.include? 'class') && line.split(' ')[0] == 'class' cn = line.split(' ')[1].gsub /_?[tT]est\z/, '' break end end else puts "Could not find measure to parse for extracting class name in Rubocop. PWD: #{Dir.pwd} filename: #{rc_file.attributes['name']}" end end @file_issues = 0 @file_info = 0 @file_warnings = 0 @file_errors = 0 violations = [] rc_file.elements.each('error') do |rc_error| @file_issues += 1 if rc_error.attributes['severity'] == 'info' @file_info += 1 elsif rc_error.attributes['severity'] == 'warning' @file_warnings += 1 elsif rc_error.attributes['severity'] == 'error' @file_errors += 1 end violations << { line: rc_error.attributes['line'], column: rc_error.attributes['column'], severity: rc_error.attributes['severity'], message: rc_error.attributes['message'] } end fhash['issues'] = @file_issues fhash['info'] = @file_info fhash['warning'] = @file_warnings fhash['error'] = @file_errors fhash['violations'] = violations mhash['measure_issues'] += @file_issues mhash['measure_info'] += @file_info mhash['measure_warnings'] += @file_warnings mhash['measure_errors'] += @file_errors @total_issues += @file_issues @total_info += @file_info @total_warnings += @file_warnings @total_errors += @file_errors mhash['files'] << fhash end @by_measure[cn] = mhash end end puts "Total files: #{@total_files}" puts "Total issues: #{@total_issues} (#{@total_info} info, #{@total_warnings} warnings, #{@total_errors} errors)" @error_status = true if @total_errors > 0 end def to_hash results = {} results['total_measures'] = @total_measures results['total_files'] = @total_files results['total_issues'] = @total_issues results['total_info'] = @total_info results['total_warnings'] = @total_warnings results['total_errors'] = @total_errors results['by_measure'] = @by_measure results end def to_file # save as a json and have something else parse it/plot it. res_hash = to_hash @summary = res_hash FileUtils.mkdir_p "#{@path_to_results}/" unless Dir.exist? "#{@path_to_results}/" File.open("#{@path_to_results}/rubocop.json", 'w') do |file| file << JSON.pretty_generate(res_hash) end end end end