Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Code coverage tooling #12

Merged
merged 5 commits into from
Aug 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great! Do need to use our fork for simplecov. 👍

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of using our fork I disabled jruby coverage reporting as jruby support is questionable for this gem

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