Skip to content

Commit dde2152

Browse files
authored
chore: Add build / test skills; rework several Claude docs (#3662)
1 parent 059c23b commit dde2152

7 files changed

Lines changed: 270 additions & 328 deletions

File tree

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
---
2+
name: build-dotnet-agent
3+
description: Build the New Relic .NET agent locally and validate that code changes compile. Use this whenever you are about to build the agent, need to confirm your edits compile, need to refresh the agent home directories (newrelichome_*) before running tests, or find yourself reaching for `dotnet build` / `msbuild` on any project in this repo. Always build the full solution from the CLI in Debug -- never a single project. Covers that command and the traps that waste time (building individual projects, Release config, building the profiler by hand, dragging preview SDKs into build tools).
4+
---
5+
6+
# Building the .NET agent
7+
8+
To validate edits or refresh agent home dirs, build the full solution yourself from the repo root:
9+
10+
```bash
11+
dotnet build FullAgent.sln
12+
```
13+
14+
Debug is default -- never pass `-c Release`. It's slow; run in background or tee to a file and read the tail. The user builds in VS; that's their path, not yours -- you use the CLI.
15+
16+
## Rules
17+
18+
- **Never build a single `.csproj`.** `Core.csproj` and its dependents have a post-build step (ILRepack + `AssemblyModifier.exe`) whose ordering only the solution provides. Building one project alone yields `AssemblyModifier.exe not found`, exit code 3, or `$(SolutionDir)`/`*Undefined*` errors.
19+
- **Debug, never Release** (Release is for CI/releases).
20+
- **`Core.UnitTest` from the CLI needs `SolutionDir`** (VS sets it, `dotnet` doesn't), trailing backslash required. Both forms below pass the same value (`<repo-root>\`):
21+
```bash
22+
# bash (Git Bash)
23+
SLN="$(cygpath -w "$PWD")\\"
24+
dotnet build tests/Agent/UnitTests/Core.UnitTest/Core.UnitTest.csproj -f net10.0 -p:SolutionDir="$SLN"
25+
```
26+
```powershell
27+
# PowerShell
28+
$sln = "$((Resolve-Path .).Path)\"
29+
dotnet build tests/Agent/UnitTests/Core.UnitTest/Core.UnitTest.csproj -f net10.0 -p:SolutionDir="$sln"
30+
```
31+
- **`NewRelic.Agent.Extensions.Tests`:** `dotnet test` on the csproj silently no-ops via VSTestTask -- test the built DLL instead (path in root CLAUDE.md).
32+
- **Profiler** (`Profiler.sln`, `src/Agent/NewRelic/Profiler/`, only when changing the profiler): build with `build.ps1`, never raw MSBuild; Debug; Linux build needs Docker Desktop.
33+
- **Build tools stay on latest LTS** (`net10.0`): `build/*` tools (ArtifactBuilder, Dotty, etc.) and `AssemblyModifier`. When evaluating a preview .NET, retarget agent core + tests, not the build tools.
34+
35+
## Agent home directories
36+
37+
`FullAgent.sln` writes these under `src/Agent/`; all integration/unbounded/container tests read from them, so a stale home dir = "my change isn't taking effect." Rebuild after changing agent/wrapper code. If new instrumentation isn't applied, confirm files exist under `<agent-home>/extensions/` (and `extensions/netcore/` for .NET Core wrappers).
38+
39+
| Framework | OS | Arch | Directory |
40+
|-----------|-----|-------|-----------|
41+
| .NET FW | Win | x64 | `newrelichome_x64` |
42+
| .NET FW | Win | x86 | `newrelichome_x86` |
43+
| .NET Core | Win | x64 | `newrelichome_x64_coreclr` |
44+
| .NET Core | Win | x86 | `newrelichome_x86_coreclr` |
45+
| .NET Core | Linux | x64 | `newrelichome_x64_coreclr_linux` |
46+
| .NET Core | Linux | arm64 | `newrelichome_arm64_coreclr_linux` |
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
---
2+
name: run-integration-tests
3+
description: Run the New Relic .NET agent integration tests locally. Use this whenever you need to run, debug, or reproduce an integration test (host-run IntegrationTests, UnboundedIntegrationTests, or ContainerIntegrationTests), are verifying that an agent/wrapper change behaves correctly end to end, or find yourself reaching for `dotnet test` against a test project in this repo. You run these yourself from the CLI every time -- no human needed; secrets and infra are already in place. Covers the build-first prerequisite, picking the correct test layer (host-run, unbounded, container), the CLI command, and the environment gotchas that cause confusing failures.
4+
---
5+
6+
# Running integration tests locally
7+
8+
Tests start a real app with the agent attached, exercise it, wait for a harvest, and assert on the parsed agent log. Run them yourself from the CLI every time.
9+
10+
## 1. Build first (two builds, in order)
11+
12+
**a. Build the agent.** Tests read the built home dirs (`src/Agent/newrelichome_*`), not your source. If agent/wrapper code changed, build `FullAgent.sln` first (see **build-dotnet-agent**) or the run uses stale agent code -- the #1 false failure. Don't debug a "failure" until you've confirmed a fresh build.
13+
14+
**b. Build only the app(s) your test launches.** The test launches its app as a separate process; the test `.csproj` does NOT reference the launched apps, so `dotnet test` never builds them (it only rebuilds the shared `MultiFunctionApplicationHelpers` library, a project reference). You must build the app yourself first. CI's **All Solutions Build** does a whole-solution `-p:DeployOnBuild=true` build because it runs the entire suite -- but locally you only need the one app the test(s) you're running use, so build that single project, not the solution.
15+
16+
**Find which app from the test's fixture** (the `NewRelicIntegrationTest<TFixture>` / `IClassFixture<TFixture>` generic arg, or the test's base class) -- deterministic, two cases:
17+
18+
- **Console fixture** (`ConsoleDynamicMethodFixture*` -- almost all unbounded + dynamic-method tests): the variant suffix gives the app + target framework. `FW*` -> `ConsoleMultiFunctionApplicationFW`, `Core*` -> `ConsoleMultiFunctionApplicationCore`. Current variant -> tfm (authoritative source is `ConsoleDynamicMethodFixture.cs`; it drifts as .NET versions roll, so grep it rather than trusting this snapshot): `FW462`->net462, `FW471`->net471, `FW48`->net48, `FW481`/`FWLatest`->net481, `Core80`/`CoreOldest`->net8.0, `Core100`/`CoreLatest`->net10.0.
19+
- **Host-run web fixture** (lives in `IntegrationTests/RemoteServiceFixtures/`): the fixture's ctor names the app -- `grep -n "RemoteWebApplication(\|: base(\"" <fixture>.cs` yields e.g. `new RemoteWebApplication("BasicMvcApplication", ...)`. The project is `tests/Agent/IntegrationTests/Applications/<that name>/<that name>.csproj`.
20+
21+
Then build that single project. The command depends on the app type (this is the only reason full MSBuild ever matters):
22+
23+
- **Console MF app** (most unbounded / `ConsoleDynamicMethodFixture` tests) -- a plain SDK project; `dotnet build` is fine. Build it for the target framework your fixture variant uses:
24+
```bash
25+
dotnet build tests/Agent/IntegrationTests/SharedApplications/ConsoleMultiFunctionApplicationFW/ConsoleMultiFunctionApplicationFW.csproj -c Debug -f net471
26+
# or ...ConsoleMultiFunctionApplicationCore/...Core.csproj -c Debug -f net10.0
27+
```
28+
- **Web app** (many host-run tests, e.g. `BasicMvcApplication`) -- a legacy web project that `dotnet build` cannot build/deploy; use full `MSBuild.exe` on just that project with its publish profile, which deploys it to its `Deploy` folder:
29+
```bash
30+
"C:/Program Files/Microsoft Visual Studio/18/Enterprise/MSBuild/Current/Bin/MSBuild.exe" \
31+
-restore -p:Configuration=Debug -p:DeployOnBuild=true -p:PublishProfile=LocalDeploy \
32+
tests/Agent/IntegrationTests/Applications/BasicMvcApplication/BasicMvcApplication.csproj
33+
```
34+
35+
How each launched app is then sourced at test time (`RemoteService.CopyToRemote`):
36+
37+
- **Web apps** -- copied from the `Deploy` folder produced by the publish profile.
38+
- **.NET Framework console apps** -- plain file copy from `bin/<Config>/<tfm>` (NOT rebuilt at test time; your build above is what makes them current).
39+
- **.NET (Core) console apps** -- `dotnet publish`ed from source at test time, so they pick up your latest source even without a prior build. (Exception: `NR_DOTNET_TEST_PREBUILT_APPS=1`, which CI sets, makes `TryCopyPrebuiltPublishOutput` copy a pre-published `bin/Release/<tfm>/<rid>/publish` output instead -- a stale one then masks changes; delete it if in doubt.)
40+
41+
**Config note:** the fixture reads `Utilities.Configuration` (`Debug` for a Debug-built test assembly, `Release` for Release). Match your app build config to how you run `dotnet test` -- Debug locally, hence `-p:Configuration=Debug` / `-c Debug` above.
42+
43+
## 2. Pick the layer
44+
45+
| Layer | Project | Notes |
46+
|-------|---------|-------|
47+
| **Host-run** (default) | `IntegrationTests/IntegrationTests.csproj` | Windows + built home dirs. Connects to real NR staging collectors with a shared test license key; asserts on the agent log. .NET Framework web app tests (those using `RemoteWebApplication` / HostedWebCore) **require an elevated (Administrator) terminal** -- HostedWebCore needs admin to bind ports via the IIS in-process API. Console app tests (`ConsoleDynamicMethodFixture`) do not. |
48+
| **Unbounded** | `UnboundedIntegrationTests/UnboundedIntegrationTests.csproj` | Adds real DBs/brokers (MySQL, Postgres, SQL Server, Mongo, Redis, RabbitMQ, Kafka, Elasticsearch, Couchbase...). |
49+
| **Container** | `ContainerIntegrationTests/ContainerIntegrationTests.csproj` | Docker Desktop. Linux-agent / container coverage. |
50+
| Performance | `PerformanceTests.sln` | Not `dotnet test` (`run-perf-test.py`). Out of scope. |
51+
52+
Host-run answers almost everything; use unbounded/container only when the test needs that infra.
53+
54+
- **Secrets are automatic:** all secrets (incl. test license key) come from .NET user secrets on `tests/Agent/IntegrationTests/Shared/Shared.csproj`, already configured on this machine.
55+
- **Unbounded infra is already up** (`restart: unless_stopped`) -- assume the DB/broker is present, no `docker compose up`. Only if a connection fails, check the `UnboundedServices` stack.
56+
- **Container tests, two purposes, same execution:** (1) basic Linux agent functionality, (2) special cases (e.g. AWS SDK) that provision their own one-off infra. Both run identically with no extra setup.
57+
58+
## 3. Run it in a sub-agent
59+
60+
Don't run tests inline -- the runner output plus per-test agent log (hundreds of KB, long lines) would re-bill on every later turn. Dispatch a sub-agent (**Sonnet**; **Haiku** for a simple single-test pass/fail) and have it return only a short summary: pass/fail, failing assertion messages, any `NR-ERROR`/`NR-FATAL` lines.
61+
62+
Run `dotnet test` against the specific test `.csproj` (filtered). This rebuilds the test project and the shared library, then launches the app you built in section 1b (it does NOT rebuild that app). Run it in the same config you built the app (Debug):
63+
64+
```bash
65+
dotnet test tests/Agent/IntegrationTests/IntegrationTests/IntegrationTests.csproj \
66+
--filter "FullyQualifiedName~BasicMvcTests" # or --filter "Category=AspNetCore"
67+
```
68+
69+
Secrets are present, so a failure is a real signal -- investigate (section 5), don't route around it.
70+
71+
**Large-log handling** (sub-agent, or you if reading directly): never raw `Read`/`tail`/wide-`grep` a big log -- it re-bills every turn. Counts before content (`grep -c`, `grep -o`, `sort|uniq -c`); cap width (`| cut -c1-200`); distill to a slim `/tmp` file and read that.
72+
73+
## 4. Don't touch the logging env
74+
75+
`NEW_RELIC_LOG_DIRECTORY` / `NEW_RELIC_LOG_LEVEL` are set in the user's environment and inherited -- do not set or override them. To change agent behavior in a test, use `NewRelicConfigModifier` / `WebConfigModifier` / `fixture.SetEnvironmentVariable(...)`, never ad-hoc XML edits.
76+
77+
## 5. On failure, check in order
78+
79+
1. **Elevated terminal?** If the test uses a .NET Framework web app (RemoteWebApplication/HostedWebCore), the terminal must be running as Administrator. HostedWebCore.exe exits with code 2 and the fixture fails with a process error if not elevated.
80+
2. Did `FullAgent.sln` build and are `newrelichome_*` current? Did you build the app your test launches (section 1b) in the same config you're running `dotnet test`? (FW console + web apps are copied, not rebuilt by the test; a stale prebuilt Core publish output can also mask changes.) (most common)
81+
2. Expected env vars set on the fixture?
82+
3. Ports free? (apps bind localhost)
83+
4. Agent log (temp path printed in test output): grep `NR-ERROR`/`NR-FATAL` first, distilled.
84+
5. Test app stdout/stderr.
85+
86+
Container tests: Docker Desktop running with enough resources, image built, container started and stayed up, host<->container network reachable.

claude.md renamed to CLAUDE.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ A native C++ profiler injects IL into JIT-compiled methods; the managed agent
55
core collects telemetry and ships it to the collector.
66

77
Sub-docs (read when working in that area):
8-
- [src/claude-source.md](src/claude-source.md) — agent/profiler/extensions internals
9-
- [build/claude-build.md](build/claude-build.md) — build, packaging, release
10-
- [tests/claude-tests.md](tests/claude-tests.md) — unit + integration test layout
8+
- [src/CLAUDE.md](src/CLAUDE.md) — agent/profiler/extensions internals
9+
- [build/CLAUDE.md](build/CLAUDE.md) — build, packaging, release
10+
- [tests/CLAUDE.md](tests/CLAUDE.md) — unit + integration test layout
1111

1212
## Solutions
1313

@@ -99,7 +99,7 @@ Precedence: env vars > `newrelic.config` > server-side config > defaults.
9999
**Editing `Configuration.xsd`** requires regenerating `Configuration.cs`
100100
via `xsd2code` and restoring the license header — never hand-edit the
101101
generated file. Exact command and caveats in
102-
[src/claude-source.md](src/claude-source.md) under Configuration.
102+
[src/CLAUDE.md](src/CLAUDE.md) under Configuration.
103103

104104
## Building and testing from the CLI
105105

@@ -180,7 +180,7 @@ later turn. Instead:
180180
wrapper. That keeps the interesting logic unit-testable while the
181181
wrapper itself stays thin (match, create segment, delegate, finish).
182182
- Integration tests: `tests/Agent/IntegrationTests/` — see
183-
[tests/claude-tests.md](tests/claude-tests.md).
183+
[tests/CLAUDE.md](tests/CLAUDE.md).
184184
- **Never use `InternalsVisibleTo`** in any production or test assembly.
185185
If a test needs to reach non-public code, refactor the production type to
186186
expose what's needed through a proper surface (interface, public helper,
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
Tools that turn the solutions into shippable artifacts (NuGet, MSI, Linux
44
packages, Azure Site Extension, download-site bundle). See the
5-
[root claude.md](../claude.md) for local-development build basics.
5+
[root claude.md](../CLAUDE.md) for local-development build basics.
66

77
## Layout
88

@@ -71,7 +71,7 @@ Invoke from repo root, e.g.:
7171

7272
- `xsd2code/` — regenerates `Configuration.cs` from `Configuration.xsd`
7373
(command + license-header restore in
74-
[src/claude-source.md](../src/claude-source.md))
74+
[src/CLAUDE.md](../src/CLAUDE.md))
7575
- `NUnit-Console/`, `XUnit-Console/` — CLI test runners
7676
- `NuGet/`, `nuget.exe` — NuGet CLI
7777
- `vswhere.exe` — Visual Studio discovery
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Source Code Architecture
22

3-
Orientation map for `src/`. See the [root claude.md](../claude.md) for how
3+
Orientation map for `src/`. See the [root claude.md](../CLAUDE.md) for how
44
instrumentation works end-to-end and for coding standards.
55

66
## Layout
@@ -37,7 +37,7 @@ build.ps1 -Platform x64 -Configuration Debug # Windows x64
3737
build.ps1 -Platform linux # Linux (requires Docker)
3838
```
3939

40-
Profiler GUIDs are documented in the [root claude.md](../claude.md).
40+
Profiler GUIDs are documented in the [root claude.md](../CLAUDE.md).
4141

4242
## Agent Core (`Agent/NewRelic/Agent/Core/`)
4343

@@ -152,14 +152,14 @@ Wrappers that *start* transactions must return `false`.
152152
`maxVersion` in instrumentation XML is **exclusive**. Prefer
153153
`VisibilityBypasser` over reflection / `dynamic` when reaching into
154154
instrumented types; cache generated delegates per type. Both conventions
155-
are detailed in the [root claude.md](../claude.md).
155+
are detailed in the [root claude.md](../CLAUDE.md).
156156

157157
Wrapper projects have **no unit tests** — they're covered by integration
158158
and unbounded test solutions. Only `NewRelic.Agent.Extensions` (shared
159159
helpers like `SqsHelper`) is unit tested. When adding non-trivial logic
160160
to a wrapper, lift it into a helper in `NewRelic.Agent.Extensions` so it
161161
can be unit tested — keep the wrapper itself thin. The same rule is
162-
covered in the [root claude.md](../claude.md) testing conventions.
162+
covered in the [root claude.md](../CLAUDE.md) testing conventions.
163163

164164
## Public API (`NewRelic.Api.Agent/`)
165165

@@ -174,7 +174,7 @@ an explicit baseline update.
174174

175175
## Configuration
176176

177-
Runtime config precedence is in the [root claude.md](../claude.md).
177+
Runtime config precedence is in the [root claude.md](../CLAUDE.md).
178178

179179
The canonical schema is `src/Agent/NewRelic/Agent/Core/Config/Configuration.xsd`
180180
and it generates `Configuration.cs` in the same directory.

0 commit comments

Comments
 (0)