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

[R-package] [docs] fix calculation of R test coverage (fixes #4919) #4922

Merged
merged 9 commits into from
Jan 5, 2022

Conversation

jameslamb
Copy link
Collaborator

Fixes #4919.

See the discussion in #4919, especially #4919 (comment), for an explanation of the changes in this PR.

Running the instructions in the R package README as of this branch, I found that the test results from {covr} match my expectations and seem correct.

lightgbm coverage - 55.65% (click me)
lightgbm Coverage: 55.65%
R/lgb.drop_serialized.R: 0.00%
R/lgb.unloader.R: 0.00%
src/boosting/goss.hpp: 0.00%
src/boosting/rf.hpp: 0.00%
src/include/Eigen/src/Core/Map.h: 0.00%
src/include/Eigen/src/Core/products/GeneralMatrixVector.h: 0.00%
src/include/Eigen/src/Core/products/Parallelizer.h: 0.00%
src/include/Eigen/src/Core/Stride.h: 0.00%
src/include/Eigen/src/Core/Transpose.h: 0.00%
src/include/Eigen/src/plugins/MatrixCwiseBinaryOps.h: 0.00%
src/metric/map_metric.hpp: 0.00%
src/metric/xentropy_metric.hpp: 0.00%
src/network/linkers_socket.cpp: 0.00%
src/network/linkers.h: 0.00%
src/network/socket_wrapper.hpp: 0.00%
src/objective/xentropy_objective.hpp: 0.00%
src/treelearner/cuda_tree_learner.h: 0.00%
src/treelearner/data_parallel_tree_learner.cpp: 0.00%
src/treelearner/feature_parallel_tree_learner.cpp: 0.00%
src/treelearner/gpu_tree_learner.h: 0.00%
src/treelearner/parallel_tree_learner.h: 0.00%
src/treelearner/voting_parallel_tree_learner.cpp: 0.00%
src/network/network.cpp: 3.17%
src/include/LightGBM/network.h: 3.39%
src/io/json11.cpp: 4.33%
src/treelearner/cost_effective_gradient_boosting.hpp: 4.81%
src/network/linker_topo.cpp: 4.96%
src/include/Eigen/src/Core/GeneralProduct.h: 6.25%
src/include/Eigen/src/Core/products/GeneralBlockPanelKernel.h: 10.50%
src/boosting/prediction_early_stop.cpp: 14.29%
src/include/LightGBM/utils/array_args.h: 16.53%
src/treelearner/tree_learner.cpp: 18.75%
src/include/Eigen/src/Core/products/GeneralMatrixMatrix.h: 18.90%
src/treelearner/split_info.hpp: 18.99%
src/objective/regression_objective.hpp: 24.93%
src/metric/multiclass_metric.hpp: 25.00%
src/io/parser.hpp: 30.14%
src/include/LightGBM/fmt/format.h: 36.22%
src/include/LightGBM/utils/text_reader.h: 36.65%
src/io/metadata.cpp: 36.73%
src/boosting/gbdt_prediction.cpp: 37.50%
src/objective/multiclass_objective.hpp: 39.29%
src/include/LightGBM/utils/json11.h: 40.00%
src/io/dataset_loader.cpp: 40.87%
src/include/Eigen/src/Core/util/Memory.h: 40.93%
src/application/predictor.hpp: 40.98%
src/io/parser.cpp: 41.32%
src/metric/regression_metric.hpp: 42.03%
src/metric/binary_metric.hpp: 43.72%
src/c_api.cpp: 44.37%
src/boosting/dart.hpp: 45.54%
src/treelearner/col_sampler.hpp: 45.86%
src/objective/objective_function.cpp: 47.37%
src/include/Eigen/src/Core/arch/SSE/PacketMath.h: 47.83%
src/include/LightGBM/fmt/format-inl.h: 48.49%
src/io/multi_val_dense_bin.hpp: 51.30%
src/io/tree.cpp: 51.98%
src/include/Eigen/src/Core/Inverse.h: 53.33%
src/include/LightGBM/utils/random.h: 54.35%
src/include/Eigen/src/Core/util/BlasUtil.h: 54.55%
src/boosting/gbdt.h: 55.10%
src/io/config.cpp: 56.68%
src/include/LightGBM/objective_function.h: 57.14%
src/boosting/gbdt_model_text.cpp: 57.42%
src/io/multi_val_sparse_bin.hpp: 57.73%
src/io/sparse_bin.hpp: 58.24%
src/treelearner/feature_histogram.hpp: 58.81%
src/include/Eigen/src/Core/GenericPacketMath.h: 60.00%
src/include/Eigen/src/Core/MathFunctions.h: 60.00%
src/io/train_share_states.cpp: 60.42%
src/boosting/gbdt.cpp: 60.48%
src/include/LightGBM/fmt/core.h: 61.80%
src/treelearner/serial_tree_learner.cpp: 63.27%
src/include/LightGBM/feature_group.h: 63.72%
src/include/Eigen/src/Core/Redux.h: 64.58%
src/include/Eigen/src/Core/MapBase.h: 65.38%
src/include/Eigen/src/Core/ProductEvaluators.h: 65.85%
src/include/LightGBM/tree.h: 66.42%
src/include/Eigen/src/Core/util/Meta.h: 66.67%
src/include/LightGBM/tree_learner.h: 66.67%
src/objective/rank_objective.hpp: 67.68%
src/include/LightGBM/utils/openmp_wrapper.h: 68.00%
src/objective/binary_objective.hpp: 69.23%
src/io/bin.cpp: 69.40%
src/boosting/boosting.cpp: 69.77%
src/include/Eigen/src/plugins/BlockMethods.h: 70.00%
src/include/LightGBM/dataset.h: 70.30%
src/metric/rank_metric.hpp: 70.65%
src/include/Eigen/src/Core/CwiseNullaryOp.h: 71.43%
R/lgb.Predictor.R: 73.77%
R/lgb.make_serializable.R: 75.00%
R/lgb.restore_handle.R: 75.00%
src/include/Eigen/src/Core/util/IntegralConstant.h: 75.00%
src/include/LightGBM/boosting.h: 75.00%
src/metric/metric.cpp: 75.00%
src/io/dataset.cpp: 76.47%
src/include/LightGBM/bin.h: 76.62%
src/include/Eigen/src/Core/DenseBase.h: 76.92%
src/include/Eigen/src/Core/CoreEvaluators.h: 77.68%
src/io/dense_bin.hpp: 77.73%
src/include/Eigen/src/Core/functors/BinaryFunctors.h: 77.78%
src/include/Eigen/src/Core/functors/UnaryFunctors.h: 77.78%
src/include/LightGBM/utils/common.h: 78.09%
src/treelearner/serial_tree_learner.h: 78.95%
src/include/Eigen/src/Core/AssignEvaluator.h: 79.25%
src/lightgbm_R.cpp: 79.32%
src/treelearner/data_partition.hpp: 80.30%
R/lgb.convert_with_rules.R: 80.65%
src/include/Eigen/src/Core/products/TriangularSolverMatrix.h: 80.70%
src/include/Eigen/src/Core/DenseCoeffsBase.h: 83.33%
src/include/Eigen/src/Core/functors/AssignmentFunctors.h: 83.33%
R/lgb.cv.R: 83.39%
src/boosting/score_updater.hpp: 83.67%
src/include/LightGBM/fast_double_parser.h: 83.83%
src/include/LightGBM/train_share_states.h: 84.27%
src/metric/dcg_calculator.cpp: 86.36%
src/treelearner/linear_tree_learner.cpp: 86.43%
src/include/LightGBM/config.h: 86.83%
R/lgb.Dataset.R: 87.59%
src/include/Eigen/src/LU/FullPivLU.h: 90.10%
R/lgb.Booster.R: 90.32%
R/lgb.train.R: 91.33%
src/include/Eigen/src/Core/DenseStorage.h: 91.53%
src/include/LightGBM/utils/yamc/alternate_shared_mutex.hpp: 91.67%
src/io/file_io.cpp: 91.67%
src/include/Eigen/src/Core/CwiseBinaryOp.h: 92.31%
src/include/LightGBM/utils/threading.h: 92.63%
R/saveRDS.lgb.Booster.R: 92.86%
src/include/Eigen/src/Core/SolveTriangular.h: 92.86%
src/include/LightGBM/utils/log.h: 93.33%
R/callback.R: 93.49%
src/treelearner/leaf_splits.hpp: 93.65%
src/include/LightGBM/utils/file_io.h: 93.75%
src/include/Eigen/src/Core/Block.h: 93.88%
src/include/Eigen/src/Core/PlainObjectBase.h: 94.34%
R/utils.R: 94.74%
src/include/Eigen/src/Core/Visitor.h: 97.06%
src/treelearner/monotone_constraints.hpp: 97.96%
src/io/config_auto.cpp: 98.41%
R/aliases.R: 100.00%
R/lgb.importance.R: 100.00%
R/lgb.interprete.R: 100.00%
R/lgb.model.dt.tree.R: 100.00%
R/lgb.plot.importance.R: 100.00%
R/lgb.plot.interpretation.R: 100.00%
R/lightgbm.R: 100.00%
R/metrics.R: 100.00%
R/readRDS.lgb.Booster.R: 100.00%
src/include/Eigen/src/Core/Assign.h: 100.00%
src/include/Eigen/src/Core/CwiseUnaryOp.h: 100.00%
src/include/Eigen/src/Core/EigenBase.h: 100.00%
src/include/Eigen/src/Core/functors/NullaryFunctors.h: 100.00%
src/include/Eigen/src/Core/Matrix.h: 100.00%
src/include/Eigen/src/Core/MatrixBase.h: 100.00%
src/include/Eigen/src/Core/NoAlias.h: 100.00%
src/include/Eigen/src/Core/NumTraits.h: 100.00%
src/include/Eigen/src/Core/PartialReduxEvaluator.h: 100.00%
src/include/Eigen/src/Core/PermutationMatrix.h: 100.00%
src/include/Eigen/src/Core/Product.h: 100.00%
src/include/Eigen/src/Core/SelfCwiseBinaryOp.h: 100.00%
src/include/Eigen/src/Core/Solve.h: 100.00%
src/include/Eigen/src/Core/SolverBase.h: 100.00%
src/include/Eigen/src/Core/Swap.h: 100.00%
src/include/Eigen/src/Core/TriangularMatrix.h: 100.00%
src/include/Eigen/src/Core/util/ForwardDeclarations.h: 100.00%
src/include/Eigen/src/Core/util/IndexedViewHelper.h: 100.00%
src/include/Eigen/src/Core/util/Macros.h: 100.00%
src/include/Eigen/src/Core/util/SymbolicIndex.h: 100.00%
src/include/Eigen/src/Core/util/XprHelper.h: 100.00%
src/include/Eigen/src/Core/VectorBlock.h: 100.00%
src/include/Eigen/src/Core/VectorwiseOp.h: 100.00%
src/include/Eigen/src/plugins/CommonCwiseBinaryOps.h: 100.00%
src/include/Eigen/src/plugins/CommonCwiseUnaryOps.h: 100.00%
src/include/Eigen/src/plugins/MatrixCwiseUnaryOps.h: 100.00%
src/include/LightGBM/c_api.h: 100.00%
src/include/LightGBM/dataset_loader.h: 100.00%
src/include/LightGBM/metric.h: 100.00%
src/include/LightGBM/prediction_early_stop.h: 100.00%
src/include/LightGBM/utils/pipeline_reader.h: 100.00%
src/include/LightGBM/utils/yamc/yamc_rwlock_sched.hpp: 100.00%
src/include/LightGBM/utils/yamc/yamc_shared_lock.hpp: 100.00%
src/treelearner/linear_tree_learner.h: 100.00%

@@ -265,10 +265,13 @@ The example below shows how to generate code coverage for the R package on a mac

```shell
# Install
sh build-cran-package.sh
sh build-cran-package.sh \
--no-build-vignettes
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This change isn't necessary to make calculating test coverage work, but it does speed up the process a lot.

Since these instructions are only about computing the coverage from tests, it isn't necessary to build the vignettes.

coverage <- covr::package_coverage('./lightgbm_r', type = 'tests', quiet = FALSE);

I found that skipping vignette building saved on the order of 60-90 seconds in the process of computing coverage.

Copy link
Collaborator

@StrikerRUS StrikerRUS left a comment

Choose a reason for hiding this comment

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

Thanks a lot for your investigation!

R-package/README.md Outdated Show resolved Hide resolved
@@ -2,7 +2,13 @@ VERBOSITY <- as.integer(
Sys.getenv("LIGHTGBM_TEST_VERBOSITY", "-1")
)

CALCULATING_TEST_COVERAGE <- Sys.getenv("LIGHTGBM_TEST_COVERAGE") == "true"
Copy link
Collaborator

@StrikerRUS StrikerRUS Jan 4, 2022

Choose a reason for hiding this comment

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

Can we use built-in R_COVR env variable instead?
https://www.rdocumentation.org/packages/covr/versions/3.5.1/topics/in_covr

UPD: ... or even better in_covr() function directly like

if (require(covr)) {
  testthat::skip_if(covr::in_covr())
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

oh nice. I like covr::in_covr() a lot better than this solution with environment variables, didn't know about that! Thank you!

updated in 1e1e491

Copy link
Collaborator

Choose a reason for hiding this comment

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

Great! Happy to help with some simplification.
I see you omitted if (require(covr)) part. What will happen if user doesn't have covr package installed but decided to run all tests with testthat?

Copy link
Collaborator

@StrikerRUS StrikerRUS Jan 4, 2022

Choose a reason for hiding this comment

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

I guess failing CI tests illustrate that there will be an error in that case:

  Error in `loadNamespace(x)`: there is no package called 'covr'
* checking for unstated dependencies in ‘tests’ ... � WARNING
'::' or ':::' import not declared from: ‘covr’
* checking tests ...
  Running ‘testthat.R’ [26s/14s]
 � � � ERROR
Running the tests in ‘tests/testthat.R’ failed.
Last 13 lines of output:
   6. base:::doWithOneRestart(return(expr), restart)
  
  ── 2. Error (test_lgb.unloader.R:31:5): lgb.unloader finds all boosters and remo
  <packageNotFoundError/error/condition>
  Error in `loadNamespace(x)`: there is no package called 'covr'
  Backtrace:
   1. testthat::skip_if(condition = covr::in_covr(), message = "lgb.unloader() tests are skipped when calculating test coverage") test_lgb.unloader.R:31:4
   3. base::loadNamespace(x)
   4. base::withRestarts(stop(cond), retry_loadNamespace = function() NULL)
   5. base:::withOneRestart(expr, restarts[[1L]])
   6. base:::doWithOneRestart(return(expr), restart)
  
  ══ DONE ════════════════════════════════════════════════════════════════════════
  Error: Test failures
  Execution halted

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

haha yeah I tried to update that too fast, you're right.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

fixed in 41e7f07

had to include # nolint comments on those lines since we use {lintr} to discourage other uses of require()

, "require" = paste0(
"library() is preferred to require() because it will raise an error immediately "
, "if a package is missing."
)

Copy link
Collaborator Author

@jameslamb jameslamb Jan 4, 2022

Choose a reason for hiding this comment

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

just pushed one more commit adding {covr} to the Suggests dependencies (23fd47c), since R CMD check failed with the following:

* checking for unstated dependencies in ‘tests’ ... WARNING
'::' or ':::' import not declared from: ‘covr’
'library' or 'require' call not declared from: ‘covr’

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

gah I forgot that adding a new Suggests dependency would mean it has to be installed in CI

* checking package namespace information ... OK
* checking package dependencies ... ERROR
Package suggested but not available: ‘covr’

https://github.com/microsoft/LightGBM/runs/4697717553?check_suite_focus=true

I don't think this change to support computing code coverage during development is worth adding a new dependency to CI jobs (which will make them take longer and increase the risk of failures).

I just pushed b97d37a, which proposes using the R_COVR environment variable.

jameslamb and others added 2 commits January 3, 2022 20:18
Co-authored-by: Nikita Titov <nekit94-08@mail.ru>
@jameslamb jameslamb requested a review from StrikerRUS January 4, 2022 02:21
Co-authored-by: Nikita Titov <nekit94-08@mail.ru>
Copy link
Collaborator

@StrikerRUS StrikerRUS left a comment

Choose a reason for hiding this comment

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

Thank you very much for this fix!

@jameslamb jameslamb merged commit 2e25719 into master Jan 5, 2022
@jameslamb jameslamb deleted the fix-r-coverage branch January 5, 2022 01:59
@StrikerRUS StrikerRUS mentioned this pull request Jan 6, 2022
13 tasks
@jameslamb jameslamb mentioned this pull request Oct 7, 2022
40 tasks
@github-actions
Copy link

This pull request has been automatically locked since there has not been any recent activity since it was closed. To start a new related discussion, open a new issue at https://github.com/microsoft/LightGBM/issues including a reference to this.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Aug 23, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[R-package] [docs] instructions for generating code coverage produce incorrect results
2 participants