|
| 1 | +# Combining srcref Data |
| 2 | + |
| 3 | +``` r |
| 4 | +library(covtracer) |
| 5 | +``` |
| 6 | + |
| 7 | +There are two key relationships that we will explore. The first is a |
| 8 | +relationship of `srcref` objects, and the second is the relationship |
| 9 | +between namespace object definitions and their associated documentation. |
| 10 | + |
| 11 | +## Setup |
| 12 | + |
| 13 | +Before we begin, we’ll set up a demo coverage object and package |
| 14 | +namespace that we can use to showcase these relationships: |
| 15 | + |
| 16 | +``` r |
| 17 | +library(withr) |
| 18 | +library(covr) |
| 19 | + |
| 20 | +withr::with_temp_libpaths({ |
| 21 | + options(keep.source = TRUE, keep.source.pkg = TRUE, covr.record_tests = TRUE) |
| 22 | + examplepkg_source_path <- system.file("examplepkg", package = "covtracer") |
| 23 | + install.packages( |
| 24 | + examplepkg_source_path, |
| 25 | + type = "source", |
| 26 | + repos = NULL, |
| 27 | + INSTALL_opts = c("--with-keep.source", "--install-tests") |
| 28 | + ) |
| 29 | + examplepkg_cov <- covr::package_coverage(examplepkg_source_path) |
| 30 | + examplepkg_ns <- getNamespace("examplepkg") |
| 31 | +}) |
| 32 | +``` |
| 33 | + |
| 34 | +## Relational `srcref` data |
| 35 | + |
| 36 | +First and foremost, we want to be able to associate `srcref` objects. |
| 37 | +These relationships are define the location of code. A `srecref` |
| 38 | +describes a region of code where the expression was pulled from, and we |
| 39 | +can compare these to determine whether a `srcref` is within, containing |
| 40 | +or independent of another. |
| 41 | + |
| 42 | +This vignette will gloss over each of these tables. For more details see |
| 43 | +the *Working with `srcref`s* vignette. |
| 44 | + |
| 45 | +### Linking `covr` traces to package object `srcref`s |
| 46 | + |
| 47 | +It’s important to note that coverage traces always sit within a package |
| 48 | +namespace object. Where a namespace object might have a `srcref` to the |
| 49 | +full code for a function, coverage traces trace individual expressions |
| 50 | +within that function. |
| 51 | + |
| 52 | +To associate `srcref`s by this relation, we provide a special joining |
| 53 | +function to combine `data.frames` by `srcref` columns. |
| 54 | + |
| 55 | +``` r |
| 56 | +traces_df <- trace_srcrefs_df(examplepkg_cov) |
| 57 | +pkg_ns_df <- pkg_srcrefs_df(examplepkg_ns) |
| 58 | +``` |
| 59 | + |
| 60 | +Just looking at these two `data.frames`, we can use the first trace and |
| 61 | +package object to illustrate the relationship: |
| 62 | + |
| 63 | +``` r |
| 64 | +cat("pkg : ", format(pkg_ns_df$srcref["s3_example_func.list"]), "\n") |
| 65 | +#> pkg : s3_example.R:20:25:22:1 |
| 66 | +cat("trace: ", format(traces_df$srcref[1L]), "\n") |
| 67 | +#> trace: r6_example.R:63:7:63:24 |
| 68 | +``` |
| 69 | + |
| 70 | +Although still a little arcane, you can see that the package object code |
| 71 | +contains the coverage trace. The package code spans lines 19-21, whereas |
| 72 | +the coverage trace lies in line 20. With this information, we can couple |
| 73 | +each package object with the coverage traces contained within each. |
| 74 | + |
| 75 | +``` r |
| 76 | +head(join_on_containing_srcrefs(traces_df, pkg_ns_df)) |
| 77 | +#> name.x srcref.x name.y srcref.y |
| 78 | +#> 1 r6_example.R:63:7:63:24:7:24:100:100 r6_example.R:63:7:63:24 Person r6_example.R:60:18:64:5 |
| 79 | +#> 2 r6_example.R:74:7:74:23:7:23:111:111 r6_example.R:74:7:74:23 Person r6_example.R:72:13:77:5 |
| 80 | +#> 3 r6_example.R:4:3:8:3:3:3:41:45 r6_example.R:4:3:8:3 adder r6_example.R:3:10:9:1 |
| 81 | +#> 4 r6_example.R:97:9:97:22:9:22:97:97 r6_example.R:97:9:97:22 Rando r6_example.R:95:12:102:3 |
| 82 | +#> 5 s3_example.R:16:3:16:11:3:11:259:259 s3_example.R:16:3:16:11 s3_example_func.default s3_example.R:15:28:17:1 |
| 83 | +#> 6 hypotenuse.R:8:3:8:25:3:25:35:35 hypotenuse.R:8:3:8:25 hypotenuse hypotenuse.R:7:15:9:1 |
| 84 | +#> namespace.y |
| 85 | +#> 1 examplepkg |
| 86 | +#> 2 examplepkg |
| 87 | +#> 3 examplepkg |
| 88 | +#> 4 examplepkg |
| 89 | +#> 5 examplepkg |
| 90 | +#> 6 examplepkg |
| 91 | +``` |
| 92 | + |
| 93 | +As expected, we can see that this test trace (now with the `".x"` |
| 94 | +suffix) is mapped to the expected corresponding package namespace |
| 95 | +object. |
| 96 | + |
| 97 | +### Linking unit tests to evaluated `covr` traces |
| 98 | + |
| 99 | +Although this relationship doesn’t require any fancy `srcref` joining, |
| 100 | +we can associate tests and traces by a simple mapping of indices. FOr |
| 101 | +this, the [`test_trace_mapping()`](../reference/test_trace_mapping.md) |
| 102 | +function is provided which will reshape a `covr` object (produced using |
| 103 | +`options(covr.record_tests = TRUE)`) to create a unified table across |
| 104 | +all `covr` traces: |
| 105 | + |
| 106 | +``` r |
| 107 | +head(test_trace_mapping(examplepkg_cov)) |
| 108 | +#> test call depth i trace |
| 109 | +#> [1,] 1 1 1 1 9 |
| 110 | +#> [2,] 2 1 37 1 18 |
| 111 | +#> [3,] 2 1 38 2 14 |
| 112 | +#> [4,] 2 1 39 3 24 |
| 113 | +#> [5,] 2 1 40 4 21 |
| 114 | +#> [6,] 2 1 40 5 27 |
| 115 | +``` |
| 116 | + |
| 117 | +The `test` and `trace` columns contain the row indices in the respective |
| 118 | +[`test_srcrefs_df()`](../reference/test_srcrefs_df.md) and |
| 119 | +[`trace_srcrefs_df()`](../reference/trace_srcrefs_df.md) `data.frame`s, |
| 120 | +allowing for this data to be joined. However, since it is easy for a |
| 121 | +testing suite to cause the evaluation of an enormous number of traces, |
| 122 | +this matrix can become extremely long. It is recommended to do some |
| 123 | +aggregation or subsetting of this matrix before trying to use it to join |
| 124 | +more data-rich data. |
| 125 | + |
| 126 | +You can also see that the evaluation order is stored (`i`), as well as |
| 127 | +the stack depth when it was evaluated (`depth`). With this added info, |
| 128 | +you might consider first filtering for only the first trace evaluated by |
| 129 | +each test, or to count all the times that a line of code was evaluated |
| 130 | +by each test by aggregating rows. |
| 131 | + |
| 132 | +## Relational documentation data |
| 133 | + |
| 134 | +On the other side of the process, we also need to associate package |
| 135 | +objects with documentation. In many cases, this is trivial, and the name |
| 136 | +of the exported object can be used directly to find documentation as you |
| 137 | +have come to expect using `?<object>`. This holds for simple functions. |
| 138 | +However, some objects are aliased to different documentation files or |
| 139 | +are built at package build time into internal representations, as is |
| 140 | +with `S4` classes, and `R6` classes. |
| 141 | + |
| 142 | +To handle these cases, we can use the [`Rd_df()`](../reference/Rd_df.md) |
| 143 | +function to associate any available source code with a documentation |
| 144 | +file. |
| 145 | + |
| 146 | +``` r |
| 147 | +# filter for interesting columns for display |
| 148 | +cols <- c("file", "alias", "doctype") |
| 149 | +Rd_df(examplepkg_source_path)[, cols] |
| 150 | +#> file alias doctype |
| 151 | +#> 1 Accumulator.Rd Accumulator <NA> |
| 152 | +#> 2 adder.Rd adder <NA> |
| 153 | +#> 3 complex_call_stack.Rd complex_call_stack <NA> |
| 154 | +#> 4 deeper_nested_function.Rd deeper_nested_function <NA> |
| 155 | +#> 5 hypotenuse.Rd hypotenuse <NA> |
| 156 | +#> 6 increment.Rd increment <NA> |
| 157 | +#> 7 names-S4Example-method.Rd names,S4Example-method <NA> |
| 158 | +#> 8 names-S4Example2-method.Rd names,S4Example2-method <NA> |
| 159 | +#> 9 nested_function.Rd nested_function <NA> |
| 160 | +#> 10 Person.Rd Person <NA> |
| 161 | +#> 11 PersonPrime.Rd PersonPrime data |
| 162 | +#> 12 Rando.Rd Rando <NA> |
| 163 | +#> 13 rd_data_sampler.Rd rd_data_sampler data |
| 164 | +#> 14 rd_sampler.Rd rd_sampler <NA> |
| 165 | +#> 15 recursive_function.Rd recursive_function <NA> |
| 166 | +#> 16 reexport_example.Rd reexport_example <NA> |
| 167 | +#> 17 reexports.Rd reexports import |
| 168 | +#> 18 reexports.Rd help import |
| 169 | +#> 19 s3_example_func.Rd s3_example_func <NA> |
| 170 | +#> 20 s3_example_func.Rd s3_example_func.default <NA> |
| 171 | +#> 21 s3_example_func.Rd s3_example_func.list <NA> |
| 172 | +#> 22 S4Example-class.Rd S4Example-class class |
| 173 | +#> 23 S4Example-class.Rd S4Example class |
| 174 | +#> 24 S4Example2-class.Rd S4Example2-class class |
| 175 | +#> 25 S4Example2-class.Rd S4Example2 class |
| 176 | +#> 26 show-S4Example-method.Rd show,S4Example-method <NA> |
| 177 | +``` |
| 178 | + |
| 179 | +These aliases are also used when we use |
| 180 | +[`pkg_srcrefs_df()`](../reference/pkg_srcrefs_df.md) and can be used to |
| 181 | +associate `srcrefs` with `.Rd` files. |
| 182 | + |
| 183 | +``` r |
| 184 | +pkg_srcrefs_df(examplepkg_ns) |
| 185 | +#> name srcref namespace |
| 186 | +#> 1 nested_function complex_call_stack.R:9:20:11:1 examplepkg |
| 187 | +#> 2 adder r6_example.R:3:10:9:1 examplepkg |
| 188 | +#> 3 recursive_function complex_call_stack.R:21:23:24:1 examplepkg |
| 189 | +#> 4 Accumulator r6_example.R:29:16:32:3 examplepkg |
| 190 | +#> 6 s3_example_func.list s3_example.R:20:25:22:1 examplepkg |
| 191 | +#> 7 s3_example_func s3_example.R:10:20:12:1 examplepkg |
| 192 | +#> 8 Person r6_example.R:60:18:64:5 examplepkg |
| 193 | +#> 9 Person r6_example.R:72:13:77:5 examplepkg |
| 194 | +#> 10 increment s4_example.R:58:35:60:1 examplepkg |
| 195 | +#> 11 rd_sampler rd_sampler.R:55:15:57:1 examplepkg |
| 196 | +#> 12 deeper_nested_function complex_call_stack.R:15:27:17:1 examplepkg |
| 197 | +#> 13 hypotenuse hypotenuse.R:7:15:9:1 examplepkg |
| 198 | +#> 14 Rando r6_example.R:95:12:102:3 examplepkg |
| 199 | +#> 15 increment s4_example.R:53:25:55:1 examplepkg |
| 200 | +#> 16 s3_example_func.default s3_example.R:15:28:17:1 examplepkg |
| 201 | +#> 17 names,S4Example-method s4_example.R:17:44:19:1 examplepkg |
| 202 | +#> 18 names,S4Example2-method s4_example.R:43:45:45:1 examplepkg |
| 203 | +#> 19 show,S4Example-method s4_example.R:25:43:27:1 examplepkg |
| 204 | +#> 20 complex_call_stack complex_call_stack.R:3:23:5:1 examplepkg |
| 205 | +#> 21 PersonPrime <NA> <NA> |
| 206 | +#> 22 help <NA> utils |
| 207 | +#> 23 reexport_example <NA> utils |
| 208 | +#> 24 S4Example2 <NA> examplepkg |
| 209 | +#> 25 S4Example <NA> examplepkg |
| 210 | +#> 26 person <NA> utils |
| 211 | +#> 27 rd_data_sampler <NA> <NA> |
| 212 | +``` |
| 213 | + |
| 214 | +You’ll see that we don’t have any `srcref`s associated with the `"data"` |
| 215 | +and `"class"` doctype documentation because these objects do not |
| 216 | +themselves have source code, even if there is source code in that was |
| 217 | +used to create them at bulid time. |
| 218 | + |
| 219 | +## Summary |
| 220 | + |
| 221 | +With these relationships, we can build some really deep understandings |
| 222 | +of exactly what code a test evaluates and tie that test together with |
| 223 | +the documented behaviors. |
0 commit comments