Skip to content

Instantly share code, notes, and snippets.

@melekes
Created August 30, 2013 16:06
Show Gist options
  • Save melekes/6391422 to your computer and use it in GitHub Desktop.
Save melekes/6391422 to your computer and use it in GitHub Desktop.
Test coverage Dashing widget

Test coverage

Test coverage

Description

Dashing widget to display test coverage.

The widget is based on JenkinsBuild, which shows you a completion percentage while Jenkins builds the project and zero in other cases. Actually, if you think about it, build takes only a small amount of time (of course, if your project is not building for half an hour). So we decided to find a more reasonable use of this state and show percentage of test coverage.

In our company, we try to cover most of our projects, so for us it is very helpful!

Requirements

The Dashing job fetches JENKINS_URI/job/#{job_name}/ws/coverage/index.html and trying to get test coverage percentage for this project. At this point it is able to recognize the results of RCov or SimpleCov. If you're using something else, you just have to tell the job where to look for the percentage in html file (see test_coverage.rb for details).

Installation

  1. Install TestCoverage widget

The files test_coverage.coffee, test_coverage.html and test_coverage.scss go in the /widget/test_coverage directory. The test_coverage.rb goes in the /jobs directory.

Otherwise you can install this plugin typing:

  dashing install GIST_ID
  1. Change the JENKINS_URI to your correct uri for Jenkins, eg.:

    JENKINS_URI = URI.parse("http://ci.yourserver.com")

  2. Put all the jobnames and pre job names in the config/jenkins_jobs.yml.

  3. Add the following block to your dashingboard.erb file:

<li data-row="1" data-col="1" data-sizex="1" data-sizey="1">
  <div data-id="my_app" data-view="TestCoverage" data-title="My App" data-min="0" data-max="100"></div>
</li>
# Elements mapping for Jenkins builds.
#
# To create a binding with the same id for an element and for a job:
# <element_name>:
#
# To specify job and pre_job names:
# <element_name>:
# job: <job_name>
# pre_job: <pre_job_name>
# my_app:
# job: my-app
class Dashing.TestCoverage extends Dashing.Widget
@accessor 'value', Dashing.AnimatedValue
@accessor 'bgColor', ->
if @get('currentResult') == "SUCCESS"
"#96bf48"
else if @get('currentResult') == "FAILURE"
"#D26771"
else if @get('currentResult') == "PREBUILD"
"#ff9618"
else
"#999"
constructor: ->
super
@observe 'value', (value) ->
$(@node).find(".jenkins-build").val(value).trigger('change')
ready: ->
meter = $(@node).find(".jenkins-build")
$(@node).fadeOut().css('background-color', @get('bgColor')).fadeIn()
meter.attr("data-bgcolor", meter.css("background-color"))
meter.attr("data-fgcolor", meter.css("color"))
meter.knob()
onData: (data) ->
if data.currentResult
if data.currentResult isnt data.lastResult
$(@node).fadeOut().css('background-color', @get('bgColor')).fadeIn()
else
$(@node).css('background-color', @get('bgColor'))
<h1 class="title" data-bind="title"></h1>
<input class="jenkins-build" data-angleOffset=-125 data-angleArc=250 data-width=200 data-readOnly=true data-bind-value="value | shortenedNumber" data-bind-data-min="min" data-bind-data-max="max">
<p class="last-built" data-bind="lastBuilt"></p>
<p class="updated-at" data-bind="updatedAtMessage"></p>
require 'net/http'
require 'json'
require 'time'
JENKINS_URI = URI.parse("http://localhost:8080")
# the key of this mapping must be a unique identifier for your job, the according value must be the name that is specified in jenkins
job_mapping = YAML.load(File.open(File.join(File.dirname(__FILE__), "..", "config", "jenkins_jobs.yml")))
job_mapping.each {|name, job| job_mapping[name] ||= {"job" => name}}
def get_number_of_failing_tests(job_name)
info = get_json_for_job(job_name, 'lastCompletedBuild')
info['actions'][4]['failCount']
end
def get_completion_percentage(job_name)
build_info = get_json_for_job(job_name)
prev_build_info = get_json_for_job(job_name, 'lastCompletedBuild')
return 0 if not build_info["building"]
last_duration = (prev_build_info["duration"] / 1000).round(2)
current_duration = (Time.now.to_f - build_info["timestamp"] / 1000).round(2)
return 99 if current_duration >= last_duration
((current_duration * 100) / last_duration).round(0)
end
def get_json_for_job(job_name, build = 'lastBuild')
uri = JENKINS_URI.dup
uri.path = "/job/#{job_name}/#{build}/api/json"
response = Net::HTTP.get(uri)
JSON.parse(response)
end
def get_coverage(job_name)
coverage_page = get_coverage_page(job_name)
if coverage_page.match '<a href="http://github.com/relevance/rcov">'
matched = /<tt class=''>([.0-9]+)%<\/tt>.*?coverage_total/m.match(coverage_page)
coverage = matched ? matched[1] : 0
elsif coverage_page.match '<a href="http://github.com/colszowka/simplecov">'
matched = /<span class="group_name">All Files<\/span>.*?covered_percent"><span[^>]*>([.0-9]+)%/m.match(coverage_page)
coverage = matched ? matched[1] : 0
else
coverage = 0
end
coverage.to_i
end
def get_coverage_page(job_name)
uri = JENKINS_URI.dup
uri.path = "/job/#{job_name}/ws/coverage/index.html"
response = Net::HTTP.get(uri)
response
end
logger = Logger.new "#{Dir.pwd}/log/jenkins_build.log"
logger.formatter = Logger::Formatter.new
logger.formatter.datetime_format = "%Y-%m-%d %H:%M:%S.%3N"
logger.level = Logger::INFO
logger.debug job_mapping
def do_job(title, jenkins_project, logger)
logger.debug "Start #{title}"
last_status = jenkins_project[:current_status]
logger.debug "Get json #{title}"
build_info = get_json_for_job(jenkins_project["job"])
jenkins_project[:current_status] = build_info["result"]
if build_info["building"]
jenkins_project[:current_status] = "BUILDING"
elsif jenkins_project[:pre_job]
pre_build_info = get_json_for_job(jenkins_project[:pre_job])
jenkins_project[:current_status] = "PREBUILD" if pre_build_info["building"]
end
logger.debug "Get coverage #{title}"
coverage = get_coverage(jenkins_project["job"])
logger.debug "Send event #{title}"
send_event(title, {
currentResult: jenkins_project[:current_status],
lastResult: last_status,
timestamp: build_info["timestamp"],
value: coverage
})
logger.debug "Finish #{title}"
end
SCHEDULER.in '5s' do |job|
loop do
logger.info "New jenkins job"
job_mapping.each do|title, jenkins_project|
begin
do_job(title, jenkins_project, logger)
logger.debug "Sleep"
sleep 1
rescue StandardError => e
logger.error e
end
end
logger.info "Finish jenkins job"
end
end
// ----------------------------------------------------------------------------
// Sass declarations
// ----------------------------------------------------------------------------
$background-color: #4b5f24;
$title-color: rgba(255, 255, 255, 1);
$moreinfo-color: rgba(255, 255, 255, 0.3);
$meter-background: rgba(20, 20, 20, 1);
// ----------------------------------------------------------------------------
// Widget-jenkins-build styles
// ----------------------------------------------------------------------------
.widget-jenkins-build {
background-color: $background-color;
input.jenkins-build {
background-color: $meter-background;
color: #fff;
}
.title {
color: $title-color;
}
.more-info {
color: $moreinfo-color;
}
.updated-at {
color: rgba(0, 0, 0, 1);
}
}
@AAverin
Copy link

AAverin commented Jan 25, 2016

Any way to provide credentials for authentication?

@AAverin
Copy link

AAverin commented Jan 25, 2016

Also, what about other coverage tools, like jacoco, for example?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment