diff --git a/docs/DevelopmentGuide.md b/docs/DevelopmentGuide.md index 284ef73cb1f..1be40748ca0 100644 --- a/docs/DevelopmentGuide.md +++ b/docs/DevelopmentGuide.md @@ -19,7 +19,7 @@ This guide covers some of the common how-tos and technical reference material fo The trace 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: +To start a development environment, choose a target Ruby or JRuby version. Then run the following: ```bash # In the root directory of the project... @@ -28,12 +28,15 @@ cd ~/dd-trace-rb # Create and start a Ruby 3.3 test environment with its dependencies docker compose run --rm tracer-3.3 /bin/bash +# or a JRuby test environment with its dependencies +docker compose run --rm tracer-jruby-9.4 /bin/bash + # Then inside the container (e.g. `root@2a73c6d8673e:/app`)... # Install the library dependencies bundle install ``` -Then within this container you can [run tests](#running-tests), or [run code quality checks](#checking-code-quality). +Within this container, you can [run tests](#running-tests) or [run code quality checks](#checking-code-quality). ## Testing @@ -43,7 +46,7 @@ The test suite uses [RSpec](https://rspec.info/) tests to verify the correctness New tests should be written as RSpec tests in the `spec/datadog` 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. +All changes should be covered by 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. ### Running tests @@ -54,31 +57,31 @@ Any file that is **not** inside a `contrib` folder is part of the core library. You can run all the tests for the core library with: ``` -$ bundle exec rake test:main +bundle exec rake test:main ``` ##### Specific core tests You can also run only a subset of the core library tests. -For example, for the test files [utils_spec.rb](https://github.com/DataDog/dd-trace-rb/blob/d66a6688f6cddab6a8c3cb1d6e0a6bfe86928e44/spec/datadog/core/utils_spec.rb) and +For example, for the test files [utils_spec.rb](https://github.com/DataDog/dd-trace-rb/blob/d66a6688f6cddab6a8c3cb1d6e0a6bfe86928e44/spec/datadog/core/utils_spec.rb) and [error_spec.rb](https://github.com/DataDog/dd-trace-rb/blob/d66a6688f6cddab6a8c3cb1d6e0a6bfe86928e44/spec/datadog/core/error_spec.rb), you can run: ```bash # Runs all tests in utils_spec.rb -$ bundle exec rspec spec/datadog/core/utils_spec.rb +bundle exec rspec spec/datadog/core/utils_spec.rb -# Runs only the test in line 24 -$ bundle exec rspec spec/datadog/core/utils_spec.rb:24 +# Runs only the test on line 24 +bundle exec rspec spec/datadog/core/utils_spec.rb:24 -# Runs the test in line 24 of utils_spec.rb and all the tests in error_spec.rb -$ bundle exec rspec spec/datadog/core/utils_spec.rb:24 spec/datadog/core/error_spec.rb +# Runs the test on line 24 of utils_spec.rb and all the tests in error_spec.rb +bundle exec rspec spec/datadog/core/utils_spec.rb:24 spec/datadog/core/error_spec.rb ``` #### All tests `bundle exec rake ci` will run the entire test suite with any given Ruby runtime, just as CI does. -However, this is not recommended because it is going take a long time. +However, this is not recommended because it will take a long time. Instead, run [specific core tests](#specific-core-tests), [integation tests](#for-integrations), or [the core library tests](#for-the-core-library). @@ -90,9 +93,9 @@ Any file that **is** inside a `contrib` folder is part of an integration. To get a list of the test tasks, run `bundle exec rake -T test:` -To run test, run `bundle exec rake test:` +To run a test, run `bundle exec rake test:` -Take `bundle exec rake test:redis` as example, multiple versions of `redis` from different dependency definitions are being tested (from `Matrixfile`). +Take `bundle exec rake test:redis` as example: multiple versions of `redis` from different dependency definitions are being tested (from `Matrixfile`). ```ruby @@ -105,7 +108,7 @@ Take `bundle exec rake test:redis` as example, multiple versions of `redis` from } ``` -If the dependency groups are prepared (with up-to-date gemfile and lockfile), the test task would install them before running the test. +If the dependency groups are prepared (with up-to-date gemfile and lockfile), the test task will install them before running the test. **Working with different dependencies** @@ -117,39 +120,39 @@ You can find them by running the following command: bundle exec rake -T dependency: ``` -Dependency group definitions are located under `appraisal/` directory using the same DSL provided by [Appraisal](https://github.com/thoughtbot/appraisal). These definitions are used to generate `gemfiles/*.gemfile` and then `gemfiles/*.lock`. All the files are underscored and prefixed with Ruby runtime. +Dependency group definitions are located under the `appraisal/` directory using the same DSL provided by [Appraisal](https://github.com/thoughtbot/appraisal). These definitions are used to generate `gemfiles/*.gemfile` and then `gemfiles/*.lock`. All the files are underscored and prefixed with the Ruby or JRuby runtime version. > [!IMPORTANT] -> Do NOT manually edit `gemfiles/*.gemfile` or `gemfiles/*.lock`. Instead, make changes to `appraisal/*.rb` and propagates your changes programmatically +> Do NOT manually edit `gemfiles/*.gemfile` or `gemfiles/*.lock`. Instead, make changes to `appraisal/*.rb` and propagate your changes programmatically. -To find out existing gemfiles in your environment, run +To find the existing gemfiles in your environment, run ```bash bundle exec rake dependency:list ``` -`dependency:list` is convenient to look for a specific gemfile path before assigning it to the environment variable `BUNDLE_GEMFILE` for doing all kinds of stuff. +`dependency:list` is convenient to look for a specific gemfile path before assigning it to the environment variable `BUNDLE_GEMFILE`. `BUNDLE_GEMFILE` is useful for doing all kinds of stuff, such as: ```bash env BUNDLE_GEMFILE=/app/gemfiles/ruby_3.3_stripe_latest.gemfile bundle update stripe ``` -After introducing a new dependency group or changing existing one, run `bundle exec rake dependency:generate` to propagate the changes to the gemfile. `dependency:generate` is idempotent and only changes `gemfiles/*.gemfile` but not `gemfiles/*.lock`. +After introducing a new dependency group or changing an existing one, run `bundle exec rake dependency:generate` to propagate the changes to the gemfile. `dependency:generate` is idempotent and only changes `gemfiles/*.gemfile` but not `gemfiles/*.lock`. -To keep lockfile up-to-date with the gemfile, run `bundle exec rake dependency:lock`. +To keep lockfiles up-to-date with the gemfile, run `bundle exec rake dependency:lock`. -To install, run `bundle exec rake dependency:install`. +To install the dependencies, run `bundle exec rake dependency:install`. Both `dependency:lock` and `dependency:install` can be provided with a specific gemfile path (from `dependency:list`) or pattern to target specific groups. For example: ```bash -# Generates lockfiles for all the stripe groups with `stripe_*` pattern +# Generate lockfiles for all the stripe groups with `stripe_*` pattern bundle exec rake dependency:lock['/app/gemfiles/ruby_3.3_stripe_*.gemfile'] -# or only generate lockfile for `stripe_latest` group +# or only generate lockfile for the `stripe_latest` group bundle exec rake dependency:lock['/app/gemfiles/ruby_3.3_stripe_latest.gemfile'] ``` -**How to add new dependency group** +**How to add a new dependency group** > [!IMPORTANT] > Add a new group only if the existing groups do not meet your requirements, or if adding a new dependency to an existing group is impractical. @@ -171,7 +174,7 @@ For example, if you want tests to run only on Ruby 3.3 for tracing, you can defi } ``` -2. Define the required gems in the corresponding Appraisal file. For this example, we are going to use [`Appraisal/ruby-3.3.rb`](../Appraisal/ruby-3.3.rb). Let's define what `rails-edge` group needs. +2. Define the required gems in the corresponding Appraisal file. For this example, we are going to use [`Appraisal/ruby-3.3.rb`](../Appraisal/ruby-3.3.rb). Let's define what the `rails-edge` group needs. ```ruby appraise 'rails-edge' do @@ -179,10 +182,10 @@ appraise 'rails-edge' do end ``` -3. Now let's generate that dependency Gemfile with `rake`, simply run +3. Now let's generate that dependency Gemfile with `rake`. Simply run > [!IMPORTANT] -> Ensure you are using Ruby 3.3 as the current Ruby version (`ruby -v`) or run commands within a Docker container. +> Ensure you are either using Ruby 3.3 as the current Ruby version (`ruby -v`) or running commands within a Docker container. ```console $ bundle exec rake dependency:generate @@ -242,7 +245,7 @@ At this point, the new tests will be automatically included in the CI that runs When running tests, you may pass additional args as parameters to the Rake task. For example: ``` -# Runs Redis tests with seed 1234 +# Run Redis tests with seed 1234 $ bundle exec rake test:redis'[--seed 1234]' ``` @@ -261,13 +264,13 @@ $ 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. +You *must* check the CI step `coverage` for the complete test coverage report, ensuring coverage does not +decrease. **Ensuring tests don't leak resources** -Tests execution can create resources that are hard to track: threads, sockets, files, etc. Because these resources can come -from the both the test setup as well as the code under test, making sure all resources are properly disposed is important +Test execution can create resources that are hard to track: threads, sockets, files, etc. Because these resources can come +from both the test setup as well as the test code itself, making sure all resources are properly disposed of is important to prevent the application from inadvertently creating cumulative resources during its execution. When running tests that utilize threads, you might see an error message similar to this one: @@ -288,21 +291,23 @@ Thread Backtrace: This means that this test did not finish all threads by the time the test had finished. In this case, the thread creation can be traced to `workers_integration_spec.rb:245:in 'new'`. The thread itself is sleeping at `workers_integration_spec.rb:262:in 'sleep'`. -The actionable in this case would be to ensure that the thread created in `workers_integration_spec.rb:245` is properly terminated by invoking `Thread#join` during the test tear down, which will wait for the thread to finish before returning. +The actionable in this case would be to ensure that the thread created in `workers_integration_spec.rb:245` is properly terminated by invoking `Thread#join` during the test teardown, which will wait for the thread to finish before returning. Depending on the situation, the thread in question might need to be forced to terminate. It's recommended to have a mechanism in place to terminate it (a shared variable that changes value when the thread should exit), but as a last resort, `Thread#terminate` forces the thread to finish. Keep in mind that regardless of the termination method, `Thread#join` must be called to ensure that the thread has completely finished its shutdown process. **The APM Test Agent** The APM test agent emulates the APM endpoints of the Datadog Agent. The Test Agent container -runs alongside the Ruby tracer locally and in CI, handles all traces during test runs and performs a number +runs alongside the Ruby tracer locally and in CI, handles all traces during test runs, and performs a number of 'Trace Checks'. For more information on these checks, see: https://github.com/DataDog/dd-apm-test-agent#trace-invariant-checks -The APM Test Agent also emits helpful logging, which can be viewed in local testing or in CircleCI as a job step for tracer and contrib -tests. Locally, to get Test Agent logs: +The APM Test Agent also emits helpful logging, which can be viewed in local testing or in CI as a job step for tracer and contrib +tests. Locally, to get Test Agent logs run: - $ docker-compose logs -f testagent +``` +docker-compose logs -f testagent +``` Read more about the APM Test Agent: https://github.com/datadog/dd-apm-test-agent#readme @@ -314,29 +319,29 @@ https://github.com/datadog/dd-apm-test-agent#readme Most of the library uses Rubocop to enforce [code style](https://github.com/bbatsov/ruby-style-guide) and quality. To check, run: ``` -$ bundle exec rake rubocop +bundle exec rake rubocop ``` To change your code to the version that rubocop wants, run: ``` -$ bundle exec rake rubocop -A +bundle exec rake rubocop -A ``` Profiling and Dynamic Instrumentation use [standard](https://github.com/standardrb/standard) instead of Rubocop. To check files with standard, run: ``` -$ bundle exec rake standard +bundle exec rake standard ``` To change your code to the version that standard wants, run: ``` -$ bundle exec rake standard:fix +bundle exec rake standard:fix ``` -For non-Ruby code, follow the instructions below to debug locally, if CI failed with respective linter. +For non-Ruby code, follow the instructions below to debug locally, if CI failed with the respective linter. - For `yamllint`, run: ```bash @@ -383,12 +388,12 @@ If you modify any of the `.proto` files under `./spec/datadog/tracing/contrib/gr testing the `grpc` integration, you'll need to regenerate the Ruby code by running: ``` -$ docker run \ - --platform linux/amd64 \ - -v ${PWD}:/app \ - -w /app \ - ruby:latest \ - ./spec/datadog/tracing/contrib/grpc/support/gen_proto.sh +docker run \ + --platform linux/amd64 \ + -v ${PWD}:/app \ + -w /app \ + ruby:latest \ + ./spec/datadog/tracing/contrib/grpc/support/gen_proto.sh ``` ### Community pull requests