Skip to content

libct: trim and de-mock container state unit tests - #5382

Open
kolyshkin wants to merge 2 commits into
opencontainers:mainfrom
kolyshkin:libct-test-demock
Open

libct: trim and de-mock container state unit tests#5382
kolyshkin wants to merge 2 commits into
opencontainers:mainfrom
kolyshkin:libct-test-demock

Conversation

@kolyshkin

@kolyshkin kolyshkin commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Related to opencontainers/cgroups#61.

The removal of mockCgroupManager is needed so oc/cgroups can add more methods without breaking runc.

Remove the useless tests:

  • TestGetContainerPids merely asserted that Container.Processes returns what the mock cgroup manager was configured to return;
  • TestGetContainerState reimplemented the namespace type to /proc file name mapping it was meant to verify, so it could only detect drift between the two copies.

Keep the useful one:

  • TestGetContainerStateAfterUpdate (initially added by commit e6e1c34)

The test round-trips the container state through a real state directory and is the only unit level coverage of Set, saveState, updateState, and currentState. Rewrite it to use real fs2 cgroup manager instead of a mock.

Note that in its current form the test is not particularly sound; the second commit explains and fixes it.

Package coverage goes from 7.4% to 7.3%.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR simplifies libcontainer’s container state unit tests by removing mock-based and redundant coverage, and rewriting the remaining meaningful test to use a real cgroup v2 (fs2) manager operating against a temporary directory (via cgroups.TestMode).

Changes:

  • Removed mockCgroupManager and two low-value tests (TestGetContainerPids, TestGetContainerState).
  • Added newFakeCgroupManager to create a real fs2 cgroup manager backed by a temp directory.
  • Updated TestGetContainerStateAfterUpdate to validate persisted state and confirm the updated memory limit is written to memory.max.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread libcontainer/container_linux_test.go Outdated
Comment on lines +59 to +61
t.Helper()
cgroups.TestMode = true
t.Cleanup(func() { cgroups.TestMode = false })

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

fixed

@kolyshkin
kolyshkin force-pushed the libct-test-demock branch 2 times, most recently from 932697f to 765b657 Compare August 1, 2026 04:12

@rata rata left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Sorry for the quick review. I think the remaining test is not really doing much. But not 100% sure, I'm in a rush now :D

Comment thread libcontainer/container_linux_test.go Outdated
func newFakeCgroupManager(t *testing.T, config *cgroups.Cgroup) (cgroups.Manager, string) {
t.Helper()
cgroups.TestMode = true
t.Cleanup(func() { cgroups.TestMode = false })

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should we store what it was set to, and restore that on defer? To be good neighbors.

This cgroup global thing makes me think that this can never run in parallel. Let's add a comment to the function doc, just in case?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Should we store what it was set to, and restore that on defer? To be good neighbors.

Fixed (was already reported by copilot, #5382 (comment))

This cgroup global thing makes me think that this can never run in parallel. Let's add a comment to the function doc, just in case?

Ideally there should be a way to mark a test case as being unable to run in parallel. The only way I know of lives inside the testing package (for example when you use t.Setenv() it internally marks the test case and then t.Parallel panics). I wish there was a way to do so but currently it's just a proposal (golang/go#78614).

As a kludge we could use t.Setenv("USES_cgroups.TestMode", "") or somesuch, but this is quite ugly.

Added a note to newFakeCgroupManager docstring. @rata let me know if you prefer using the kludge.

Comment thread libcontainer/container_linux_test.go Outdated
Comment on lines 129 to 130

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The remaining test I'm not sure how much is really testing, I guess we need to improve it here.

I don't have a lot of time to see this now. But it seems this is sharing pointers and then not testing much?

This makes the test not really testing much, IIUC. Because we do a shallow copy in newConfig := ..., then newConfig.Cgroups is a pointer that is shared with the same pointer the internal libct is using. Then we are just modifying the internal struct of libct.

Then all reads will pass just fine. I think we should assign a new struct here for the test to be more meaningful.

But not sure if I'm missing something

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

To add some context, the test was added by commit e6e1c34. And I guess you're right -- in its current form it's useless, too :(

Fixed this (in a separate commit on top so you don't have to re-review).

Alternatively, we can just drop all this useless-in-its-current-form-nonsense tests altogether.

kolyshkin and others added 2 commits August 11, 2026 20:49
Removal of mockCgroupManager is needed so that oc/cgroups can add more
methods without breaking runc compatibility.

Remove the useless tests:
- TestGetContainerPids merely asserted that Container.Processes returns
  what the mock cgroup manager was configured to return;
- TestGetContainerState reimplemented the namespace type to /proc file
  name mapping it was meant to verify, so it could only detect drift
  between the two copies.

Keep the useful one:
- TestGetContainerStateAfterUpdate (added by commit e6e1c34).

The test round-trips the container state through a real state directory
and is the only unit level coverage of Set, saveState, updateState, and
currentState. Rewrite it to use real fs2 cgroup manager instead of a
mock.

Note that in its current form the test is not particularly sound; the
next commit explains and fixes it.

Package coverage goes from 7.4% to 7.3%.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
Most of what the test asserts after calling Set could not fail, because
the config it passes to Set is aliased to the one the container already
holds, in two independent ways:

 1. Container.Config returns a shallow copy, so newConfig.Cgroups is the
    very *cgroups.Cgroup the container config points to, and setting
    Memory on it modified the container config before Set was even
    called. This is as old as the test itself (commit e6e1c34).

 2. fs2.Manager.Set ends with "m.config.Resources = r", and the manager
    was handed the same *cgroups.Cgroup as the container config, so the
    manager installed the new Resources into the container config as a
    side effect. This one appeared once the test switched to a real
    cgroup manager in the previous commit.

Fix both: give newConfig its own Cgroups/Resources, and construct the
manager from a separate (but equal) instance. In real life these are the
same object, so the split is only there to let the test tell whether Set
did its job.

Also, check that the new value made it to state.json, rather than merely
asserting that saveState did not return an error.

With this, removing "c.config = &config" from Set, or skipping the
saveState call in updateState, each make the test fail; before, both
mutations passed.

Reported-by: Rodrigo Campos <rodrigo@amutable.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
@kolyshkin

Copy link
Copy Markdown
Contributor Author

@rata sorry for the late reply; your comments are valuable and were addressed; I still think maybe we should just drop all this entirely as we have a bunch of integration tests that tests the same thing anyway.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants