Git Product home page Git Product logo

danmayer / coverband Goto Github PK

View Code? Open in Web Editor NEW
2.4K 12.0 154.0 28.31 MB

Ruby production code coverage collection and reporting (line of code usage)

Home Page: https://github.com/danmayer/coverband

License: MIT License

Ruby 81.63% Gnuplot 0.27% CSS 10.43% HTML 3.60% JavaScript 1.92% Lua 1.87% Shell 0.08% Haml 0.04% Slim 0.05% Roff 0.11%
coverband ruby coverage-report coverage-data rack-middleware rails

coverband's Introduction

Coverband

GithubCI Coverage Status Maintainability Discord Shield

Key FeaturesInstallationCoverband Web UIAdvanced ConfigNewer FeaturesLicenseChange Log / RoadmapCode of Conduct

A gem to measure production code usage, showing a counter for the number of times each line of code is executed. Coverband allows easy configuration to collect and report on production code usage. It reports in the background via a thread, can be used as Rack middleware, or can be manually configured to meet any need.

Note: Coverband is not intended for test code coverage; for that we recommend using SimpleCov.

Key Features

The primary goal of Coverband is to give you deep insight into the production runtime usage of your application code, while having the least impact on performance possible.

  • Low performance overhead
  • Simple setup and configuration
  • Out of the box support for all standard code execution paths (web, cron, background jobs, rake tasks, etc)
  • Splits code loading usage (Rails eager load) and runtime usage metrics
  • Easy to understand actionable insights from the report
  • Mountable web interface to easily share reports

Installation

Redis

Coverband stores coverage data in Redis. The Redis endpoint is looked for in this order:

  1. ENV['COVERBAND_REDIS_URL']
  2. ENV['REDIS_URL']
  3. localhost:6379

The redis store can also be explicitly defined within the config/coverband.rb. See advanced config.

Gem Installation

Add this line to your application's Gemfile, remember to bundle install after updating:

gem 'coverband'

No custom code or middleware required

With older versions of Coverband, projects would report to redis using rack or sidekiq middleware. After Coverband 4.0, this should no longer be required and could cause performance issues. Reporting to redis is now automatically done within a background thread with no custom code needed.

See changelog.

Rails

The Railtie integration means you shouldn't need to do anything else other than ensure Coverband is required after Rails within your Gemfile.

Sinatra

For the best coverage, you want this loaded as early as possible. We recommend requiring cover band directly in the config.ru. Requiring Coverband within an initializer could also work, but you may end up missing some boot up coverage. To start collection require Coverband as early as possible.

require 'coverband'
require File.dirname(__FILE__) + '/config/environment'

use Coverband::BackgroundMiddleware
run ActionController::Dispatcher.new

Coverband Web UI

image

You can check it out locally by running the Coverband Demo App.

  • View overall coverage information

  • Drill into individual file coverage

  • View individual file details

  • Clear Coverage - disabled by default as it could be considered a dangerous operation in production. Enable with config.web_enable_clear or leave off and clear from the rake task.

    • Clear coverage report

      This will clear the coverage data. This wipes out all collected data.

    • Clear individual file coverage

      This will clear the details of the file you are looking at. This is helpful if you don't want to lose all Coverage data but made a change that you expect would impact a particular file.

  • Force coverage collection

    This triggers coverage collection on the current webserver process. Useful in development but confusing in production environments where many ruby processes are usually running.

Interpreting results

The columns in the web UI are as follows:

  • % covered - Percentage of total relevant lines covered
  • % runtime - Percentage of the runtime lines covered where runtime lines are lines that are hit after the application has been eagerly loaded
  • Lines - Total lines in the file including lines unreachable or uncover-able. An unreachable line would be an empty line with no code, comments, or end statements.
  • Relevant lines - Lines that are coverable, i.e. not empty
  • Lines runtime - Total lines minus uncoverable lines minus the lines that are only hit during eager loading of application
  • Lines missed - Relevant lines not covered
  • Avg hits/line - Total of coverage to the file divided by relevant lines.

When viewing an individual file, a line of code such as a class or method definition may appear green because it is eager loaded by the application, but still not be hit at all in runtime by actual users.

example of a file with lines not hit at runtime

Mounting as a Rack App

Coverband comes with a mountable rack app for viewing reports. For Rails this can be done in config/routes.rb with:

Rails.application.routes.draw do
  mount Coverband::Reporters::Web.new, at: '/coverage'
end

But don't forget to protect your source code with proper authentication. Something like this when using devise:

Rails.application.routes.draw do
  authenticate :user, lambda { |u| u.admin? } do
    mount Coverband::Reporters::Web.new, at: '/coverage'
  end
end

or you can enable basic auth by setting ENV['COVERBAND_PASSWORD'] or via your configuration config.password = <YOUR_COMPLEX_UNGUESSABLE_PASSWORD>

Standalone

The coverage server can also be started standalone with a rake task:

bundle exec rake coverband:coverage_server

The web UI should then be available here: http://localhost:9022/

If you want to run on an alternative port:

COVERBAND_COVERAGE_PORT=8086 bundle exec rake coverband:coverage_server

This is especially useful for projects that are api only and cannot support the mounted rack app. To get production coverage, point Coverband at your production redis server and ensure to checkout the production version of your project code locally.

 COVERBAND_REDIS_URL=redis://username:password@redis_production_server:2322 bundle exec rake coverband:coverage_server

Coverband Demo

Take Coverband for a spin on the live Heroku deployed Coverband Demo. The full source code for the demo is available to help with installation, configuration, and understanding of basic usage.

Example apps

Advanced Config

If you need to configure Coverband, this can be done by creating a config/coverband.rb file relative to your project root.

  • See lib/coverband/configuration.rb for all options
  • By default Coverband will try to store data to Redis * Redis endpoint is looked for in this order: ENV['COVERBAND_REDIS_URL'], ENV['REDIS_URL'], or localhost

Below is an example config file for a Rails 5 app:

# config/coverband.rb NOT in the initializers
Coverband.configure do |config|
  config.store = Coverband::Adapters::RedisStore.new(Redis.new(url: ENV['MY_REDIS_URL']))
  config.logger = Rails.logger

  # config options false, true. (defaults to false)
  # true and debug can give helpful and interesting code usage information
  # and is safe to use if one is investigating issues in production, but it will slightly
  # hit perf.
  config.verbose = false

  # default false. button at the top of the web interface which clears all data
  config.web_enable_clear = true

  # default false. Experimental support for routes usage tracking.
  config.track_routes = true
end

Working with environment variables

Do you use figaro, mc-settings, dotenv or something else to inject environment variables into your app? If so ensure you have that done BEFORE Coverband is required.

For example if you use dotenv, you need to do this, see https://github.com/bkeepers/dotenv#note-on-load-order

gem 'dotenv-rails', require: 'dotenv/load'
gem 'coverband'
gem 'other-gem-that-requires-env-variables'

Ignoring Files

Sometimes you have files that are known to be valuable, perhaps in other environments or something that is just run very infrequently. Opposed to having to mentally filter them out of the report, you can just have them ignored in the Coverband reporting by using config.ignore as shown below. Ignore takes a string but can also match with regex rules see how below ignores all rake tasks as an example.

config.ignore +=  ['config/application.rb',
                   'config/boot.rb',
                   'config/puma.rb',
                   'config/schedule.rb',
                   'bin/.*',
                   'config/environments/.*',
                   'lib/tasks/.*']

Ignoring Custom Gem Locations: Note, if you have your gems in a custom location under your app folder you likely want to add them to config.ignore. For example, if you have your gems not in the default ignored location of app/vendor but in app/gems, you would need to add gems/* to your ignore list.

View Tracking

Coverband allows an optional feature to track all view files that are used by an application.

This feature is enabled by default. To stop this feature, disable the feature in your Coverband config.

config.track_views = false

image

Hiding settings

Coverband provides a view of all of its current settings. Sometimes you might want to hide this view, such as when sharing coverband data with a large number of developers of varying trust levels. You can disable the settings view like so:

config.hide_settings = false

Fixing Coverage Only Shows Loading Hits

If all your coverage is being counted as loading or eager_loading coverage, and nothing is showing as runtime Coverage the initialization hook failed for some reason. The most likely reason for this issue is manually calling eager_load! on some Plugin/Gem. If you or a plugin is altering the Rails initialization process, you can manually flip Coverband to runtime coverage by calling these two lines, in an after_initialize block, in application.rb.

config.after_initialize do
  unless Coverband.tasks_to_ignore?
    Coverband.report_coverage # record the last of the loading coverage
    Coverband.runtime_coverage! # set all future coverage to runtime
  end
end

or if you know you are manually calling eager load anywhere in your initialization process immediately after call those two lines. A user reported an issue after calling ResqueWeb::Engine.eager_load! for example.

Rails.application.routes.draw do
  ResqueWeb::Engine.eager_load!
  Coverband.report_coverage
  Coverband.runtime_coverage!
end

Avoiding Cache Stampede

If you have many servers and they all hit Redis at the same time you can see spikes in your Redis CPU, and memory. This is due to a concept called cache stampede.

It is better to spread out the reporting across your servers. A simple way to do this is to add a random wiggle on your background reporting. This configuration option allows a wiggle. The right amount of wiggle depends on the number of servers you have and how willing you are to have delays in your coverage reporting. I would recommend at least 1 second per server. Note, the default wiggle is set to 30 seconds.

Add a wiggle (in seconds) to the background thread to avoid all your servers reporting at the same time:

config.reporting_wiggle = 30

Another way to avoid cache stampede is to omit some reporting on starting servers. Coverband stores the results of eager_loading to Redis at server startup. The eager_loading results are the same for all servers, so there is no need to save all results. By configuring the eager_loading results of some servers to be stored in Redis, we can reduce the load on Redis during deployment.

# To omit reporting on starting servers, need to defer saving eager_loading data
config.defer_eager_loading_data = true
# Store eager_loading data on 5% of servers
config.send_deferred_eager_loading_data = rand(100) < 5
# Store eager_loading data on servers with the environment variable
config.send_deferred_eager_loading_data = ENV.fetch('ENABLE_EAGER_LOADING_COVERAGE', false)

Redis Hash Store

Coverband on very high volume sites with many server processes reporting can have a race condition which can cause hit counts to be inaccurate. To resolve the race condition and reduce Ruby memory overhead we have introduced a new Redis storage option. This moves the some of the work from the Ruby processes to Redis. It is worth noting because of this, it has larger demands on the Redis server. So adjust your Redis instance accordingly. To help reduce the extra redis load you can also change the background reporting frequency.

  • Use a dedicated Coverband redis instance: config.store = Coverband::Adapters::HashRedisStore.new(Redis.new(url: redis_url))
  • Adjust from default 30s reporting config.background_reporting_sleep_seconds = 120

See more discussion here.

Please note that with the Redis Hash Store, everytime you load the full report, Coverband will execute HGETALL queries in your Redis server twice for every file in the project (once for runtime coverage and once for eager loading coverage). This shouldn't have a big impact in small to medium projects, but can be quite a hassle if your project has a few thousand files. To help reduce the extra redis load when getting the coverage report, you can enable get_coverage_cache (but note that when doing that, you will always get a previous version of the report, while a cache is re-populated with a newer version).

  • Use Hash Redis Store with get coverage cache: config.store = Coverband::Adapters::HashRedisStore.new(redis, get_coverage_cache: true)

Clear Coverage

Now that Coverband uses MD5 hashes there should be no reason to manually clear coverage unless one is testing, changing versions, or possibly debugging Coverband itself.

rake coverband:clear

This can also be done through the web if config.web_enable_clear is enabled.

Adding Rake Tasks outside of Rails

Rails apps should automatically include the tasks via the Railtie.

For non Rails apps, either add the below to your Rakefile or to a file included in your Rakefile such as lib/tasks/coverband.rake if you want to break it up that way.

require 'coverband'
Coverband.configure
require 'coverband/utils/tasks'

Verify it works

rake -T coverband
rake coverband:clear         # reset coverband coverage data
rake coverband:coverage      # report runtime coverband code coverage

Manually Starting Coverband

Coverband starts on require of the the library which is usually done within the Gemfile. This can be disabled by setting the COVERBAND_DISABLE_AUTO_START environment variable. This environment variable can be useful to toggle Coverband on and off in certain environments.

NOTE: That any value set for COVERBAND_DISABLE_AUTO_START is considered true, it does not match the string content but only checks the presence of the ENV variable.

In order to start Coverband manually when this flag is enabled, call Coverband.configure followed by Coverband.start.

Coverband.configure
Coverband.start

Verbose Debug / Development Mode

Note: To debug issues getting Coverband working. I recommend running in development mode, by turning verbose logging on config.verbose = true and passing in the Rails.logger config.logger = Rails.logger to the Coverband config. We respect the log level, and I would recommend log level info generally, but if you are investigating a problem Coverband logs additional data at the debug level. This makes it easy to follow in development mode. Be careful not to leave these on in production as they will affect performance.


If you are trying to debug locally wondering what code is being run during a request. The verbose modes config.verbose = true && Rails.logger.level = :debug. With true set it will output the number of lines executed per file, to the passed in log.

Solving: stack level too deep errors

If you start seeing SystemStackError: stack level too deep errors from background jobs after installing Coverband, this means there is another patch for ResqueWorker that conflicts with Coverband's patch in your application. To fix this, change Coverband gem line in your Gemfile to the following:

gem 'coverband', require: ['alternative_coverband_patch', 'coverband']

If you currently have require: false, remove the 'coverband' string from the require array above so the gem line becomes like this:

gem 'coverband', require: ['alternative_coverband_patch']

This conflict happens when a ruby method is patched twice, once using module prepend, and once using method aliasing. See this ruby issue for details. The fix is to apply all patches the same way. By default, Coverband will apply its patch using prepend, but you can change that to method aliasing by adding require: ['alternative_coverband_patch'] to the gem line as shown above.

Redis Sizing Info

A few folks have asked about what size of Redis is needed to run Coverband. I have some of our largest services with hundreds of servers on cache.m3.medium with plenty of room to spare. I run most apps on the smallest AWS Redis instances available and bump up only if needed or if I am forced to be on a shared Redis instance, which I try to avoid. On Heroku, I have used it with most of the 3rd party and also been fine on the smallest Redis instances, if you have hundreds of dynos you would likely need to scale up. Also note there is a tradeoff one can make, Coverband::Adapters::HashRedisStore will use LUA on Redis and increase the Redis load, while being nicer to your app servers and avoid potential lost data during race conditions. While the Coverband::Adapters::RedisStore uses in app memory and merging and has lower load on Redis.

Newer Features

Dead Method Scanning (ruby 2.6+)

Rake task that outputs dead methods based on current coverage data:

bundle exec rake coverband:dead_methods

Outputs:

---------------------------------------------------------------------------------------------------
| file                                  | class           | method                  | line_number |
| ./config/routes.rb                    | AdminConstraint | matches?                | 20          |
| ./app/controllers/home_controller.rb  | HomeController  | trigger_jobs            | 8           |
| ./app/controllers/home_controller.rb  | HomeController  | data_tracer             | 14          |
| ./app/controllers/posts_controller.rb | PostsController | edit                    | 22          |
| ./app/controllers/posts_controller.rb | PostsController | destroy_bad_dangerously | 73          |
---------------------------------------------------------------------------------------------------

Prerequisites

  • Coverband 3.0.X+ requires Ruby 2.3+
  • Coverband currently requires Redis for production usage

Ruby and Rails Version Support

We will match Heroku & Ruby's support lifetime, supporting the last 3 major Ruby releases. For details see supported runtimes.

For Rails, we will follow the policy of the Rails team maintenance policy. We officially support the last two major release versions, while providing minimal support (major bugs / security fixes) for an additional version. This means at the moment we primarily target Rails 6.x, 5.x, and will try to keep current functionality working for Rails 4.x but may release new features that do not work on that target.

JRuby Support

Coverband is compatible with JRuby. If you want to run on JRuby note that I haven't benchmarked and I believe the perf impact on older versions of JRuby could be significant. Improved Coverage support is in JRuby master, and will be in the next release.

  • older versions of JRuby need tracing enabled to work (and this could cause bad performance)
    • run Jruby with the --debug option
    • add into your .jrubyrc the debug.fullTrace=true setting
  • For best performance the oneshot_lines is recommended, and in the latest releases should have very low overhead
  • See JRuby support in a Rails app configured to run via JRuby, in Coverband Demo
  • JRuby is tested via CI against Rails 5 and 6

Contributing To Coverband

If you are working on adding features, PRs, or bugfixes to Coverband this section should help get you going.

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Make sure all tests are passing (run bundle install, make sure Redis is running, and then execute rake test)
  6. Create new Pull Request

Tests & Benchmarks

If you submit a change please make sure the tests and benchmarks are passing.

  • run tests:
    • bundle exec rake
    • BUNDLE_GEMFILE=Gemfile.rails7 bundle exec rake (Same tests using rails 7 instead of 6)
  • view test coverage: open coverage/index.html
  • run the benchmarks before and after your change to see impact
    • rake benchmarks
    • run a single test by line number like rspec: bundle exec m test/coverband/reporters/html_test.rb:29
    • run a single test file: bundle exec ruby test/coverband/collectors/translation_tracker_test.rb

Known Issues

  • total fail on front end code, for line for line coverage, because of the precompiled template step basically coverage doesn't work well for erb, slim, and the like.
    • related it will try to report something, but the line numbers reported for ERB files are often off and aren't considered useful. I recommend filtering out .erb using the config.ignore option. The default configuration excludes these files
    • NOTE: We now have file level coverage for view files, but don't support line level detail
  • Coverage does NOT work when used alongside Scout APM Auto Instrumentation
    • In an environment that uses Scout's AUTO_INSTRUMENT=true (usually production or staging) it stops reporting any coverage, it will show one or two files that have been loaded at the start but everything else will show up as having 0% coverage
    • Bug tracked here: scoutapp/scout_apm_ruby#343
  • Coverband, Elastic APM and resque
    • In an environment that uses the Elastic APM ruby agent, resque jobs will fail with Transactions may not be nested. Already inside #<ElasticAPM::Transaction> if the elastic-apm gem is loaded before the coverband gem
    • Put coverage ahead of elastic-apm in your Gemfile
  • Bootsnap, The methods used by bootsnap do not support having coverage enabled, so you can either have Coverband or bootsnap, but not both.

Debugging Redis Store

What files have been synced to Redis?

Coverband.configuration.store.covered_files

What is the coverage data in Redis?

Coverband.configuration.store.coverage

Diagram

A diagram of the code.

Visualization of this repo

Logo

The Coverband logo was created by Dave Woodall. Thanks Dave!

License

This is a MIT License project... See the file LICENSE for copying permission.

coverband's People

Contributors

alpaca-tc avatar anonoz avatar danmayer avatar dependabot[bot] avatar dondonz avatar drowze avatar fidelisrafael avatar guigs avatar hosamaly avatar inoda avatar jjb avatar kallin avatar kbaum avatar lewiseason avatar maikokuppe avatar mark-davenport-fountain avatar maxp-hover avatar mrbongiolo avatar mtsmfm avatar naoya9922 avatar panozzaj avatar petergoldstein avatar rileyanderson avatar rswaminathan avatar shmokmt avatar thedrow avatar thomasowens avatar vs37559 avatar ydah avatar zwalker avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

coverband's Issues

rake coverband:coverage gives error

rake aborted!
NoMethodError: undefined method track_files' for SimpleCov:Module /Users/ankur/.rvm/gems/ruby-2.2.2/gems/coverband-1.5.0/lib/coverband/reporters/simple_cov_report.rb:22:in report'
/Users/ankur/.rvm/gems/ruby-2.2.2/gems/coverband-1.5.0/lib/coverband/tasks.rb:50:in block (2 levels) in <top (required)>' /Users/ankur/.rvm/gems/ruby-2.2.2/gems/rake-11.2.2/exe/rake:27:in <top (required)>'
/Users/ankur/.rvm/gems/ruby-2.2.2/bin/ruby_executable_hooks:15:in eval' /Users/ankur/.rvm/gems/ruby-2.2.2/bin/ruby_executable_hooks:15:in

'
Tasks: TOP => coverband:coverage
(See full trace by running task with --trace)

Any idea why I am getting this error?

Is there a way to change coverage output directory?

I'm trying to get coverband working on heroku so I can't just write to the coverage directory. Is it possible to change the output directory to something like /tmp/coverage for now so I can test the report?

Coverband does not swallow JSON.parse errors

I do not have a root cause why it cannot parse json (probably uploaded some invalid baseline to our staging server), but it does not swallow JSON.parse errors, throwing:

JSON::ParserError: 784: unexpected token at ':22,"20":22,"24":22}}'

The error is not catched by coverband and makes webserver fail with 500 error.
It should probably be swallowed and reported in coverband log.

Can we change the keys in redis to start from the app root?

It would be great if this was an option. I'd like to submit a PR with this in it, but am having a hard time slicing in the right spot. Can you a)let me know if this is something you'd be willing to incorporate and b)point me in the right direction to implement it?

Running baseline in production

The coverband README warns against running rake coverband:baseline in production. I would like to schedule something like this to run every couple of weeks:

rake coverband:clear 
rake coverband:baseline 

Whats the danger of running coverband:baseline in production?

Web request fails when coverband cannot access Redis

I'm using Coverband::Middleware with Redis. Our app is hosted on Heroku. I used smaller Redis instance and when we had bigger traffic we got Redis::CommandError - ERR max number of clients reached - this exception caused that the web request has failed. It was thrown by calling Coverband::Base.instance.report_coverage at vendor/bundle/ruby/2.3.0/gems/coverband-1.5.1/lib/coverband/middleware.rb:13.

I'm afraid that just upgrading the Redis instance is not the solution because when we have a spike in traffic it's possible that even the biggest instance won't help. Is it possible to rescue all these exceptions to ensure that the web request will never be affected? Maybe I missed something in the documentation so please let me know if there is anything I can do. Our priority is to ensure the request will never fail - It's okay to skip the reporting for that request.

I can provide more detailed logs if needed.

Incorrectly reporting 100% coverage

We're running into an issue where coverband reports data incorrectly. Here's a screenshot that illustrates the problem.

screen shot 2017-06-29 at 10 36 31 am

Coverband says that the class is 100% covered, but I know that I didn't make enough calls to exercise the entire controller, and there are many lines that indicate they've never been run, but that don't count as being "missed" for some reason.

This particular controller lives outside of /app/ in a directory that is treated as an "internal gem", so to speak. That directory is pulled into the Gemfile like this:

gemspec path: 'userpages'

To get any coverage data at all for anything in that directory I added this to my coverband config.

config.include_gems = true
config.root_paths = ['/app/','/userpages/']

Coverband reseting on every request in RAILS_ENV=development

I understand that rails is restarting the application on each request (so changes can be loaded) -- https://github.com/rails/rails/blob/92703a9ea5d8b96f30e0b706b801c9185ef14f0e/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt#L4-L6.

My solution to trying coverband out in a development environment is to set the startup_delay to 0. Otherwise it never decrements (https://github.com/danmayer/coverband/blob/master/lib/coverband/base.rb#L60) since https://github.com/danmayer/coverband/blob/master/lib/coverband/base.rb#L38-L56 is executed on each request.

Should this be mentioned in the README somewhere? Or should I be doing something different to test this in development?

Sharing coverage report to the rest of the team

I've noticed that actually generating and sharing the coverage report to the rest of the team can be a bit awkward. Code coverage data is stored within our production redis. In order to generate the report, i have to copy that data to my local redis instance. Then i run the coverband:coverage task locally. This is a bit confusing as the path to the files on my local instance is different than in production. It seems to work but looking at the code, i think this may not work for others depending on their coverband settings.

Once i have the coverband report locally generated, now i have to figure out how to share it. The actual coverage data is quite sensitive data given that it has the details of our codebase.

I am not sure of the exact solution to the problem, but i think it would be neat if we could generate and expose the report within production. Since we are on Heroku, we cannot generate it to the local file system. We could generate and store on s3 or another remote file system.

Curious if others have noticed this issue.

thx!

Unable to start coverband from outside the Rails project directory

I noticed a few things, all in lib/coverband.rb.

First, CONFIG_FILE is hard coded to a relative path. This is all well and good until you try to invoke a Rails application from outside Rails.root. From what I can tell, Coverband is looking for the CONFIG_FILE relative to the location where you run your script. Although I can set my working directory before starting the application, this doesn't seem like it should be necessary.

I also noticed that the self.configure method takes in a file argument, but it is unused. It appears like it should allow a caller to, instead of passing in a block, pass in a path to a configuration file instead. This would add a great deal of flexibility into users of Coverband by allowing them to choose a location to put the configuration file, instead of requiring it to be in './config/coverband.rb'.

Not working on Puma multithreaded server with startup_delay turned on

I've been trying to use coverband on a Rails application running on the Puma webserver using the example configuration, but coverage was always disabled.

After digging around for a while I noticed that Coverband::Base holds a @delay instance variable which gets decreased on each request until it hits zero, at which point coverage is enabled. Puma, however spawns a new thread for each request, so a new instance of Coverband::Base is created with its @delay variable set to the original number specified in the config. This way, it never reaches zero and coverage is always disabled.

I don't know if there's an easy (and threadsafe) fix for this, but I think it should at least be documented in the README. Everything seems to work fine with startup_delay set to 0.

Please let me know if there's anything I'm missing.

I'd be happy to help with either docs or code!

Having trouble generating a report on production...

We are running rails on AWS, and using redis.
Also have a customized baseline that observes the config.ignore setting.
So I have everything working on my localhost, and can execute clear, custom_baseline, and coverage operations generating the expected results.
Yesterday we pushed this to production, but I can't seem to be able to pull coverage from production. On my localhost I have hard coded the config to match our production values for config.redis including the appropriate url, port, and db#. I have logged in to our redis instance, and it seems to be recording activity from production as it reflects changes as I move through the app in production. But on my localhost if I run rake coverband:coverage with these hard coded production settings generates output reflecting files in production. However, it generates a 0% coverage report. As I said I the redis instance has data in it like this:
screen shot 2017-04-26 at 11 23 19 am
That changes appropriately as I activate the corresponding code from our production app.
Not exactly sure what I am doing wrong here...

Not exactly sure how to make this work

This gem sounds great and I want to use it. I'm just not sure how.

The documentation seems to have most of the parts, but it lacks an overall explanation of work flow.

I've added the gem to my gemfile, made the tmp/coverband_baseline.json file, added the rake tasks and the middleware. Then I wanted to test it locally, but the README kind of stops there.

The coverband_baseline rake task seemed like a good place to start, so I ran it (with --trace). A bunch of json spat out and no errors, so hurray. I did notice, however, that the filenames listed in the json only included my initializers.

With the baseline taken, I assumed the only other thing to do was run the coverband rake task, so I did. When it was finished, my browser popped open to show me a lovely simplecov page. The only problem was that it only listed my initializer files.

Could someone explain to me 1) what the ideal coverband workflow is (add all the stuff from the readme, deploy it, run the baseline, then the coverband tasks?) and 2) any reason why the rest of my production code was missed out?

I'd be happy to submit a PR with more setup instructions in the README once I get my head wrapped around these concepts.

include certain gems in coverage

We have some common code in our own gems. I wish there was a way to include certain gems in the coverage while still ignoring all others in /vendor.

The only way I see to do it is to list all the gem paths in ignored except the ones I want to include. That's a lot regex patterns to check when it's checking whether to ignore the file or not.

Showing untouched files

I noticed that coverband only shows files that have been hit at least once. This leaves out all of the files that were never hit and the number one candidates for deletion. Was thinking it would be better to go through the full file system to produce the report instead of just relying on the set of files within redis. Here is a hacked branch i used to get it done:

https://github.com/danmayer/coverband/compare/master...kbaum:show_uncovered_files?w=0

Obviously hardcoding the Dir.glob inclusion path is not the right way to go, but i wanted to see if others thought this is the general way to go.

thx!

Coverband to measure Rake tasks?

I've got it working for my Rails app, and also for background jobs with a Sidekiq middleware. But for cron jobs that run a Rake task, I'm thinking about how I can wrap those with Coverband.

I have this in my lib/tasks/coverband.rake:

require 'coverband'
Coverband.configure
require 'coverband/tasks'

# Wrap all Rake tasks with Coverband
current_tasks = Rake.application.top_level_tasks
if current_tasks.any? && current_tasks.none? { |t| t.to_s.match?(/^coverband:/) }
  current_tasks.unshift 'coverband:start'
  current_tasks.push 'coverband:stop_and_save'
end

namespace :coverband do
  task :start do
    Coverband::Base.instance.start
  end

  task :stop_and_save do
    Coverband::Base.instance.stop
    Coverband::Base.instance.save
  end
end

This code wraps all Rake tasks with Coverband start/stop/save. Seems to work ok but not sure if that's the best way

move repeated method into gem

baseline = if File.exist?('./tmp/coverband_baseline.json')
JSON.parse(File.read('./tmp/coverband_baseline.json'))
else
{}
end

Make the method take a file path or default to the above... I have been adding this into pretty much all the apps that use cover band.

better rails baseline

so rails2 got a good baseline from

require File.expand_path("./config/environment", Dir.pwd)

rails3 needs something better.

Reporting coverage fails if redis is not configured

It seems that Coverband.report only works if redis is configured and fails with an exception message if it isn't

/Users/omer.katz/.rvm/gems/ruby-2.2.2@mygemset/gems/coverband-1.1/lib/coverband/reporter.rb:104:in `get_current_scov_data_imp': undefined method `smembers' for nil:NilClass (NoMethodError)
    from /Users/omer.katz/.rvm/gems/ruby-2.2.2@mygemset/gems/coverband-1.1/lib/coverband/reporter.rb:123:in `report_scov'
    from /Users/omer.katz/.rvm/gems/ruby-2.2.2@mygemset/gems/coverband-1.1/lib/coverband/reporter.rb:42:in `report'
    from ruby_file.rb:23:in `<main>

At the very least I think that the method should fail with a proper message.
I can make a pull request for that if you want to.
There's another option though. Maybe we should allow reporting coverage from the in-memory store if redis is not configured.

Localization strings coverage

It's just an idea I have a long time ago, but may be you've already seen something like does the same coverage, but for the locale strings or files? Sometimes you can find, that in the project, there are a lots of translations that aren't used already for a while. It will be cool to clean them out of the locale files.

Use SimpleCov profile

Hi! How can i use simplecov profile to generate html reports, groups, filters, and so on? For example:

SimpleCov.profiles.define 'test' do
       add_filter '/gems/'
       add_filter '/coverage/'
       add_filter '/reports/'
       add_group 'Features', 'features'
       add_group 'Applications123', 'app'
       add_group 'Core', 'src/core'
end
    
     SimpleCov.start 'test'

(Maybe) issue in combination with Bootsnap

I was just trying to include Coverband in our app but was wondering why the output shows always empty files and no coverage was detected.

coverband file usage: []

After printing out the files in the TracePoint callback, I saw, that no app files got passed along but some suspicious files with a bootsnap prefix.
I removed bootsnap setup from our config/boot.rb and tried again and all files would be logged. My next idea was to upgrade Bootsnap and everything still works now...

Maybe was related to and old version of bootsnap (Upgraded from 1.1.2 to 1.3.0.) or Bootsnap cache.

I just would like to have this issue for other people that google that problem

Multi-threaded environment

Been getting more familiar with the code and it does not look like Coverband was meant to be used in a multi-threaded environment. For example, Coverband::Base is a singleton keeps state of the current request. In a multi-threaded environment such as Puma or Sidekiq, the worker threads would step on each other.

To be honest, i am not sure how possible it would be to make this library thread safe as each request or worker calls set_trace_func.

Just wanted to start a discussion on this as Sidekiq and Puma have become very popular and more rails installations are relying on a multi-thread model.

thx!
-karl

Extract out the functionality so coverband may be used by any application

It would be great if you could use coverband in a way similar to

my_coverband = Coverband.new( configuration ) # or similar

loop do
  my_coverband.sample {
    # code to sample
  }
end

my_coverband.report( io )

Or if might be an issue with the sampling code being in a block --

my_coverband = Coverband.new( configuration ) # or similar

loop do
  my_coverband.start
  # code to sample
  my_coverband.stop
end

my_coverband.report( io )

That way coveband could be used by any application not just a rack based one. 😄

full path incorrect when running in heroku

I am running the rake coverband:coverage task within heroku and updating to s3. I noticed the file path is cut off for anything within the app directory.

image

I am wondering if coverband/scov is getting confused because heroku deploys the app within a /app directory so the directory ends up being /app/app.

Either way, it's a bit difficult to debug as the issue only happens within heroku. Was wondering if anyone else noticed it.

thx.

Coverband not started when rails app is running

I have tried to set up Coverband in my rails project (as a middleware) following the readme's instructions. I managed to create an HTML report for the baseline (using the rake task), but I'm not able to create a report for the requests done while my app is running.

Running rake middleware shows that the middleware is loaded, because I see use Coverband::Middleware in the middleware listing output.

My hypothesis was that Coverband was never started, because I see coverage disabled in my rails server console output for every request. So I tried to force a start with Coverband::Base.instance.start in the config.ru file, but it didn't change anything.

Also, I noticed that rake coverband:clear coverband:coverage gives me the same report as the report for the baseline. But I would have expected no coverage at all (no lines hit whatsoever).

So then I tried to force it another way. I added an around filter in my application_controller:

around_filter :force_coverage

def force_coverage
  Coverband::Base.instance.start
  yield
  Coverband::Base.instance.stop
  Coverband::Base.instance.save
end

And finally, that gave me some coverage at runtime. But from what I understand, it shouldn't be required to add that kind of code.

Can you help me figure out what am I missing?

rake coverband:baseline never exits

I'm just sitting here wondering what is happening when I run rake coverband:baseline. It never exits. I think there is a problem with my configuration, but I'm not sure what it is. Any ideas? Thanks in advance.

Line Counter Never Goes Above 1

  1. I run rake coverband:clear, coverband:baseline, coverband:coverage
  2. I check that Coverband says my method foobar was not executed
  3. I go to a page that executes foobar
  4. I run coverband:coverage and check that Coverband says foobar has been executed once
  5. I go to that page that executes foobar again (with old/new cookies, in the same/different browser, doesn't matter)
  6. I run coverband:coverage, and Coverband tells me foobar was still only executed once.

In fact, I can't seem to get any part of a model or view to report an execution count > 1. Have you ever experienced this?

`rake coverbands:baseline` dropped production database.

First: Let me be clear - this is not actually the gems fault. The gem has a relatively minor security "flaw" which resulted in an outsize result in our environment because of poor practices / bad luck on our end.

In the baseline task, Coverband loads (and because it's Ruby, executes) every .rb file under both /lib and /app - including potentially destructive scripts that are not loaded by the regular load process.

In our case, we had lib/<submodule/db/schema.rb files from an old submodule that were still present in Capistrano's cached copy. Although we had run Coveralls on staging environments to verify it's operation, since that file was only present on a single production server, when we executed the baseline task on that server, it was loaded and forced schema creation, dropping existing tables.

Although this is definitely one of those "swiss cheese holes align" kind of problems - I think Coverband could benefit from being more intentional about what files it executes while establishing the baseline coverage.

Which of the following approaches would be most compatible with Coverband's overall philosophy?

  1. Do not descend into submodule directories, and do not execute Ruby found there.
  2. Maintain a 'blacklist' of "dangerous" files that should never be executed.
  3. Open the database in "read only" mode while establishing a baseline.
  4. Hook into the existing Rails eager load - which should eager load all files referenced by the code base without loading potentially dangerous 'sidecar' scripts.

add config for coverband_baseline file

coverband should deal with ensuring the file exists or creating it and directories leading up to that file

mkdir -p ./tmp
touch ./tmp/coverband_baseline.json
echo "[]" > ./tmp/coverband_baseline.json

something like that

Broken compatibility with coverband_ext

As I am sure you are aware coverband_ext is a useful addition to this gem which speeds up sampling https://github.com/danmayer/coverband_ext
They use method which was recently deleted from your codebase as no longer used
They use it here:
https://github.com/danmayer/coverband_ext/blob/master/ext/coverband_ext/coverband_ext.c#L49
It was deleted in this commit:
2f80301
I am not sure whether you are the ones who should fix it, but simply adding it back does solve the problem for me (I monkey patch coverband gem)

Coverage report is generated twice with different results

I've followed all of the README instructions for installing and configuring coverband and when I run rake coverband:coverage I'm seeing some weird stuff in the coverage report.

What happens is that a browser window will open that shows some amount of coverage, but if I hit refresh, the coverage drops to 0%.

In the terminal where I run the rake task I can see that two reports are being generated.

$ rake coverband:coverage
...
Coverage report generated for RSpec to .../my-app/coverage. 105 / 1666 LOC (6.3%) covered.
Coverage report generated for RSpec to .../my-app/coverage. 0 / 2025 LOC (0.0%) covered.

I've tried following the same process in two different projects, and I'm seeing the same in both.

Empty lines and certain statements on their own being marked as uncovered

with this code:

# blah.rb
def foo(x)
    if x == 2
        blah = 5 
    else
        blah = 7 
    end 

    return blah
end

# test.rb
...

coverband.start

foo 1
foo 2

coverband.stop
coverband.save
Coverband::Reporter.report

I get
screen shot 2014-09-12 at 03 00 34 p m

It seems to me like this should be 100% covered, and those lines marked as uncovered are incorrect.

Performance issue with Redis Adapter (2.0.1)

At one point previously, we used 1.5.4 with success on our code base, and we wanted to reintroduce coverband with the newer version, 2.01...however we are seeing performance issues when coverband is enabled and collecting/sending data to redis... Here is a gist that demonstrates the problem (runnable in a bundle console session) https://gist.github.com/CharlesMcMillan/6bc239429761adc70ef738d05e82de6b

The goal of the gist here is to simulate the cost associated with a single page load in our app, which might include something like 10 requests where the 'report' being sent will include 2394 files with 24 lines of code each (hopefully this demystifies some of the number choices in the gist!)

The results indicate that we are losing between 1-1.5 seconds per request (I got that number by dividing 11.558872 / 10), given the above assumptions/guesstimations

It appears that saving a 'report' to redis is pretty costly here. Did something major change between 1.5.4 and 2.0.1 that would explain this?

Latest gem code not uploaded: undefined method `s3_bucket' for #<Coverband::Configuration...>

If I have gem 'coverband', '~> 1.1', in my Rails 4.2.7 Gemfile, and I try to set up s3_bucket as follows:

Coverband.configure do |config|
  config.s3_bucket = ENV['AWS_BUCKET']
end

I get undefined method `s3_bucket' for #Coverband::Configuration...

If I use a forked copy of the master branch of this gem, then the s3_bucket method exists. Has the latest copy of master been pushed up to rubygems?

split dependencies so that SimpleCov isn't loaded in production

from Brian E.

Dan Mayer: wondering if it would be appropriate to ensure HOME is set before this line:

require 'simplecov'


My thoughts:

Basically Coverband always loads SimpleCov which assumes a HOME is set and not all deployed environments have that. Really SimpleCov isn't needed in production at all so we if split the gem and drop the SimpleCov dependancies on the gem, but only have it on the Reporter which isn't loaded in production I think we could reduce the production dependancies.

Is coverband useful for evaluating line coverage in the views of a rails project?

The main reason why I decided to give coverband a try is to be able to delete useless code located in the views of a rails project. The report generated after 1 or 2 requests on my app shows various coverage for the touched controllers and models, but shows 100% coverage for all the views touched. However, I know this is not the case (I have if/else blocks in some of those views, and I know for sure that both branches of those if/else blocks could not have been touched). My impression is that as long as 1 line of a view is "touched", the report will say the whole view has been 100% covered.

Also, from the readme – although probably not directly related to my issue – I can read:

the line numbers reported for ERB files are often off and aren't considered useful. I recommend filtering out .erb using the config.ignore option.

My question: is coverband an appropriate tool to evaluate line coverage in my rails views?

Heroku Usage

This seems pretty dependent on the file system which is ephemeral on heroku. Does that mean this is not usable with heroku?

thx!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.