Skip to content

Commit

Permalink
Merge pull request #12 from DataDog/anmarchenko/code_coverage
Browse files Browse the repository at this point in the history
Code coverage tooling
  • Loading branch information
anmarchenko committed Aug 31, 2023
2 parents 038b95c + bd8cb35 commit 1e801e4
Show file tree
Hide file tree
Showing 103 changed files with 924 additions and 57 deletions.
105 changes: 51 additions & 54 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ job_defaults: &job_defaults
# TODO: We should move away from using a directory
# TODO: that requires root permission to be created.
# TODO: Changing this requires rebuilding all docker images.
# TODO: remove this when using our own Dockerfile for datadog-ci-rb
working_directory: /app
shell: /bin/bash --login

Expand All @@ -30,11 +31,6 @@ test_containers:
BUNDLE_GEMFILE: /app/Gemfile
# Faster JVM startup: https://github.com/jruby/jruby/wiki/Improving-startup-time#use-the---dev-flag
JRUBY_OPTS: --dev
# Override number of concurrent compiles in grpc gem, see https://github.com/grpc/grpc/pull/28250 and https://github.com/DataDog/dd-trace-rb/issues/1791
# If you see gem installation failing with "Killed" on CircleCI and `gem install --platform ruby grpc` reproduces the
# issue when you connect to the testing container via ssh, then try lowering this file a notch.
GRPC_RUBY_BUILD_PROCS: 6
DDTRACE_CI: true
- &container_parameters_environment
- *container_base_environment
- TEST_DATADOG_INTEGRATION: 1
Expand All @@ -54,7 +50,7 @@ test_containers:
- DD_APM_ENABLED=true
- DD_BIND_HOST=0.0.0.0
- DD_API_KEY=00000000000000000000000000000000
- DD_HOSTNAME=dd-trace-rb-ci
- DD_HOSTNAME=datadog-ci-rb-ci
- &agent_port 8126

check_exact_bundle_cache_hit: &check_exact_bundle_cache_hit
Expand Down Expand Up @@ -90,9 +86,9 @@ step_bundle_install: &step_bundle_install
else
echo "All required gems were found in cache."
fi
step_rubocop: &step_rubocop
step_lint: &step_lint
run:
name: Delint with Rubocop
name: Lint with standardrb
# There's no straightforward way to get the number of available processors & CPU threads in CircleCI.
# Currently it always return 18 physical processors and 36 threads, regardless of executor size.
# The workaround is to use `cpu.shares / 1024`:
Expand All @@ -108,6 +104,7 @@ step_appraisal_install: &step_appraisal_install
bundle exec appraisal generate # Generate the appraisal files to match the lockfiles in the tree
echo "All required gems were found in cache."
fi
# TODO: this is something to use for edge tests (against newest versions of rspec, minitest, etc.)
# step_appraisal_update: &step_appraisal_update
# run:
# name: Update Appraisal gems
Expand Down Expand Up @@ -154,8 +151,8 @@ filters_only_release_tags: &filters_only_release_tags

orbs:
orb:
# orbs:
# codecov: codecov/codecov@3.2.3
orbs:
codecov: codecov/codecov@3.2.3
jobs:
build:
<<: *test_job_default
Expand Down Expand Up @@ -188,29 +185,29 @@ orbs:
docker:
- <<: *container_base
- *container_agent
parallelism: 10
parallelism: 8
steps:
- restore_cache:
keys:
- '{{ .Environment.CIRCLE_CACHE_VERSION }}-bundled-repo-<<parameters.ruby_version>>-{{ .Environment.CIRCLE_SHA1 }}'
- restore_cache:
keys:
- bundle-{{ .Environment.CIRCLE_CACHE_VERSION }}-{{ checksum ".circleci/images/primary/binary_version" }}-<<parameters.ruby_version>>-{{ checksum "lib/datadog/ci/version.rb" }}-{{ .Branch }}-{{ checksum ".circleci/bundle_checksum" }}
# - run:
# name: Set coverage report directory
# command: |
# # Create a unique coverage directory for this job, to avoid conflicts when merging all results
# echo 'export COVERAGE_DIR="$COVERAGE_BASE_DIR/versions/$CIRCLE_JOB/$CIRCLE_NODE_INDEX"' >> $BASH_ENV
- run:
name: Set coverage report directory
command: |
# Create a unique coverage directory for this job, to avoid conflicts when merging all results
echo 'export COVERAGE_DIR="$COVERAGE_BASE_DIR/versions/$CIRCLE_JOB/$CIRCLE_NODE_INDEX"' >> $BASH_ENV
# Wait for containers to start
- docker-wait:
port: *agent_port
- *step_run_all_tests
- store_test_results:
path: /tmp/rspec
# - persist_to_workspace:
# root: .
# paths:
# - coverage
- persist_to_workspace:
root: .
paths:
- coverage
lint:
<<: *test_job_default
steps:
Expand All @@ -220,29 +217,29 @@ orbs:
- restore_cache:
keys:
- bundle-{{ .Environment.CIRCLE_CACHE_VERSION }}-{{ checksum ".circleci/images/primary/binary_version" }}-<<parameters.ruby_version>>-{{ checksum "lib/datadog/ci/version.rb" }}-{{ .Branch }}-{{ checksum ".circleci/bundle_checksum" }}
- *step_rubocop
# coverage:
# <<: *test_job_default
# steps:
# - restore_cache:
# keys:
# - '{{ .Environment.CIRCLE_CACHE_VERSION }}-bundled-repo-<<parameters.ruby_version>>-{{ .Environment.CIRCLE_SHA1 }}'
# - restore_cache:
# keys:
# - bundle-{{ .Environment.CIRCLE_CACHE_VERSION }}-{{ checksum ".circleci/images/primary/binary_version" }}-<<parameters.ruby_version>>-{{ checksum "lib/datadog/ci/version.rb" }}-{{ .Branch }}-{{ checksum ".circleci/bundle_checksum" }}
# - attach_workspace:
# at: /tmp/workspace
# - run:
# name: Generate combined coverage report for all tests
# command: COVERAGE_DIR=/tmp/workspace/coverage bundle exec rake coverage:report
# - codecov/upload:
# file: /tmp/workspace/coverage/report/coverage.xml
# - run:
# name: Generate individual coverage report for each Ruby version
# command: COVERAGE_DIR=/tmp/workspace/coverage bundle exec rake coverage:report_per_ruby_version
# - store_artifacts:
# path: /tmp/workspace/coverage/report/
# destination: coverage
- *step_lint
coverage:
<<: *test_job_default
steps:
- restore_cache:
keys:
- '{{ .Environment.CIRCLE_CACHE_VERSION }}-bundled-repo-<<parameters.ruby_version>>-{{ .Environment.CIRCLE_SHA1 }}'
- restore_cache:
keys:
- bundle-{{ .Environment.CIRCLE_CACHE_VERSION }}-{{ checksum ".circleci/images/primary/binary_version" }}-<<parameters.ruby_version>>-{{ checksum "lib/datadog/ci/version.rb" }}-{{ .Branch }}-{{ checksum ".circleci/bundle_checksum" }}
- attach_workspace:
at: /tmp/workspace
- run:
name: Generate combined coverage report for all tests
command: COVERAGE_DIR=/tmp/workspace/coverage bundle exec rake coverage:report
- codecov/upload:
file: /tmp/workspace/coverage/report/coverage.xml
- run:
name: Generate individual coverage report for each Ruby version
command: COVERAGE_DIR=/tmp/workspace/coverage bundle exec rake coverage:report_per_ruby_version
- store_artifacts:
path: /tmp/workspace/coverage/report/
destination: coverage
# changelog:
# <<: *test_job_default
# steps:
Expand Down Expand Up @@ -391,17 +388,17 @@ workflows:
name: lint
requires:
- build-3.2
# - orb/coverage:
# <<: *config-3_2-small
# name: coverage
# requires:
# - test-2.7
# - test-3.0
# - test-3.1
# - test-3.2
# - test-3.3
# # ADD NEW RUBIES HERE
# - test-jruby-9.4
- orb/coverage:
<<: *config-3_2-small
name: coverage
requires:
- test-2.7
- test-3.0
- test-3.1
- test-3.2
- test-3.3
# ADD NEW RUBIES HERE
- test-jruby-9.4
# - orb/changelog:
# <<: *config-3_2-small
# name: changelog
Expand Down
25 changes: 25 additions & 0 deletions .simplecov
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
SimpleCov.enable_coverage :branch

SimpleCov.add_filter %r{/vendor/}
SimpleCov.add_filter %r{/spec/support/}

SimpleCov.coverage_dir ENV.fetch("COVERAGE_DIR", "coverage")

# Each test run requires its own unique command_name.
# When running `rake spec:test_name`, the test process doesn"t have access to the
# rake task process, so we have come up with unique values ourselves.
#
# The current approach is to combine the ruby engine (ruby-2.7,jruby-9.2),
# program name (rspec/test), command line arguments (--pattern spec/**/*_spec.rb),
# and the loaded gemset.
#
# This should allow us to distinguish between runs with the same tests, but different gemsets:
# * appraisal ruby-3.2.0-rspec-3 rake spec:rspec
# * appraisal ruby-3.2.0-minitest-5 rake spec:minitest
#
# Subsequent runs of the same exact test suite should have the same command_name.
command_line_arguments = ARGV.join(" ")
gemset_hash = Digest::MD5.hexdigest Gem.loaded_specs.values.map { |x| "#{x.name}#{x.version}" }.sort.join
ruby_engine = "#{RUBY_ENGINE}-#{RUBY_ENGINE_VERSION}"

SimpleCov.command_name "#{ruby_engine}:#{gemset_hash}:#{$PROGRAM_NAME} #{command_line_arguments}"
3 changes: 3 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ gem "standard", "~> 1.31.0"
gem "yard"
gem "webrick"

gem "simplecov"
gem "simplecov-cobertura", "~> 2.1.0"

# type checking
group :check do
if RUBY_PLATFORM != "java"
Expand Down
4 changes: 1 addition & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,7 @@ end

## Development

After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
See [development guide](/docs/DevelopmentGuide.md).

### Static typing

Expand Down
125 changes: 125 additions & 0 deletions docs/DevelopmentGuide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
# Developing

This guide covers some of the common how-tos and technical reference material for developing changes within the CI visibility library.

## Table of Contents

- [Setting up](#setting-up)
- [Testing](#testing)
- [Writing tests](#writing-tests)
- [Running tests](#running-tests)
- [Checking test coverage](#checking-test-coverage)
- [Checking code quality](#checking-code-quality)

## Setting up

*NOTE: To test locally, you must have `Docker` and `Docker Compose` installed. See the [Docker documentation](https://docs.docker.com/compose/install/) for details.*

The CI visibility library uses Docker Compose to create a Ruby environment to develop and test within, as well
as containers for any dependencies that might be necessary for certain kinds of tests.

To start a development environment, choose a target Ruby version then run the following:

```bash
# In the root directory of the project...
cd ~/datadog-ci-rb

# Create and start a Ruby 3.2 test environment with its dependencies
docker-compose run --rm datadog-ci-3.2 /bin/bash

# Then inside the container (e.g. `root@2a73c6d8673e:/app`)...
# Install the library dependencies
bundle install

# Install build targets
bundle exec appraisal install
```

Then within this container you can [run tests](#running-tests), [check code quality](#checking-code-quality), or
[run static typing checks](/docs/StaticTypingGuide.md).

## Testing

The test suite uses [RSpec](https://rspec.info/) tests to verify the correctness of both the core trace library and its integrations.

### Writing tests

New tests should be written as RSpec tests in the `spec/ddtrace` folder. Test files should generally mirror the structure of `lib`.

All changes should be covered by a corresponding RSpec tests. Unit tests are preferred, and integration tests are accepted where appropriate (e.g. acceptance tests, verifying compatibility with datastores, etc) but should be kept to a minimum.

#### Considerations for CI

All tests should run in CI. When adding new `spec.rb` files, you may need to add a test task to ensure your test file is run in CI.

- Ensure that there is a corresponding Rake task defined in `Rakefile` under the the `spec` namespace, whose pattern matches your test file.
- Verify the Rake task is configured to run for the appropriate Ruby runtimes in the `ci` Rake task.

### Running tests

Simplest way to run tests is to run `bundle exec rake ci`, which will run the entire test suite, just as CI does.

#### For the core library

Run the tests for the core library with:

```bash
bundle exec rake spec:main
```

#### For integrations

Integrations which interact with dependencies not listed in the `datadog-ci` gemspec will need to load these dependencies to run their tests.

To do so, load the dependencies using [Appraisal](https://github.com/thoughtbot/appraisal). You can see a list of available appraisals with `bundle exec appraisal list`, or examine the `Appraisals` file.

Then to run tests, prefix the test commain with the appraisal.

`bundle exec appraisal <appraisal_name> rake <test_comand>`

For example:

```bash
# Runs tests for rspec-3
$ bundle exec appraisal ruby-3.2.0-rspec-3 rake spec:rspec
# Runs tests for minitest-5
$ bundle exec appraisal ruby-3.2.0-minitest-5 rake spec:minitest
```

#### Passing arguments to tests

When running tests, you may pass additional args as parameters to the Rake task. For example:

```bash
# Runs minitest integration tests with seed 1234
$ bundle exec appraisal ruby-3.2.0-minitest-5 rake spec:minitest'[--seed,1234]'
```

This can be useful for replicating conditions from CI or isolating certain tests.

#### Checking test coverage

You can check test code coverage by creating a report *after* running a test suite:

```bash
# Run the desired test suite
$ bundle exec appraisal ruby-3.2.0-rspec-3 rake spec:rspec
# Generate report for the suite executed
$ bundle exec rake coverage:report
```

A webpage will be generated at `coverage/report/index.html` with the resulting report.

Because you are likely not running all tests locally, your report will contain partial coverage results.
You *must* check the CI step `coverage` for the complete test coverage report, ensuring coverage is not
decreased.

## Checking code quality

This library uses [standardrb](https://github.com/standardrb/standard) to enforce code style and quality.

To check, run:

```bash
bundle exec standardrb
```
2 changes: 2 additions & 0 deletions gemfiles/jruby_9.4.0.0_cucumber_3.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ gem "appraisal"
gem "standard", "~> 1.31.0"
gem "yard"
gem "webrick"
gem "simplecov"
gem "simplecov-cobertura", "~> 2.1.0"
gem "cucumber", "~> 3"

group :check do
Expand Down
12 changes: 12 additions & 0 deletions gemfiles/jruby_9.4.0.0_cucumber_3.gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ GEM
msgpack
debase-ruby_core_source (3.2.1)
diff-lcs (1.5.0)
docile (1.4.0)
ffi (1.15.5-java)
gherkin (5.1.0)
json (2.6.3-java)
Expand Down Expand Up @@ -101,6 +102,15 @@ GEM
rubocop (>= 1.7.0, < 2.0)
rubocop-ast (>= 0.4.0)
ruby-progressbar (1.13.0)
simplecov (0.22.0)
docile (~> 1.1)
simplecov-html (~> 0.11)
simplecov_json_formatter (~> 0.1)
simplecov-cobertura (2.1.0)
rexml
simplecov (~> 0.19)
simplecov-html (0.12.3)
simplecov_json_formatter (0.1.4)
spoon (0.0.6)
ffi
standard (1.31.0)
Expand Down Expand Up @@ -134,6 +144,8 @@ DEPENDENCIES
rspec
rspec-collection_matchers
rspec_junit_formatter
simplecov
simplecov-cobertura (~> 2.1.0)
standard (~> 1.31.0)
webrick
yard
Expand Down
Loading

0 comments on commit 1e801e4

Please sign in to comment.