Skip to content

Conversation

benjirewis
Copy link
Member

RSDK-10762

Adding some quick test coverage for optional dependency cycles, since we have had issues in the past with weak dependency cycles.

@benjirewis benjirewis requested a review from a team June 6, 2025 15:30
@viambot viambot added the safe to test This pull request is marked safe to test from a trusted zone label Jun 6, 2025
return nil
}

func TestModularOptionalDependenciesCycles(t *testing.T) {
Copy link
Member

Choose a reason for hiding this comment

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

I think this test is about having two resources, "A" and "B". Where "A" optionally depends on "B" and "B" optionally depends on "A"?

It's a good test name, but I still think it's worth just writing that out in plain english as a test comment. And stating what the behavior we're asserting on. I assume this is a legal config and we expect A and B both (eventually) get reconfigured with handles to the other one.

But because of the cycle I expect this has to work something like:

  1. Create A (without B)
  2. Create B (with A)
  3. Reconfigure A with B

Or vice versa where B gets constructed first. Or, if A and B are constructed concurrently:

  1. Create A (without B) while also Creating B (without A)
  2. Reconfigure A with B
  3. Reconfigure B with A

I think at least describing the expected/allowable interleavings would be useful for understanding a future test failure.

Copy link
Member Author

Choose a reason for hiding this comment

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

Your description of the test is pretty much accurate, yep. Good idea to have an explanatory comment; added. I also renamed the test to TestOptionalDependenciesCycle since there's nothing modular going on here.

expected interleavings

I believe the ordering of reconfigures and constructions are deterministic with this test. That is because I am not adding both A (moc) and B (moc2) at the same time. I can add A and B simultaneously if we want to test that. With what I'm doing now, the order should always be the first interleaving you described:

  1. Construct A (without B)
  2. Construct B (with A)
  3. Reconfigure A with B

Plus two extra steps:

  1. Remove A
  2. Reconfigure B without A

return nil
}

func TestModularOptionalDependenciesCycles(t *testing.T) {
Copy link
Member Author

Choose a reason for hiding this comment

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

Your description of the test is pretty much accurate, yep. Good idea to have an explanatory comment; added. I also renamed the test to TestOptionalDependenciesCycle since there's nothing modular going on here.

expected interleavings

I believe the ordering of reconfigures and constructions are deterministic with this test. That is because I am not adding both A (moc) and B (moc2) at the same time. I can add A and B simultaneously if we want to test that. With what I'm doing now, the order should always be the first interleaving you described:

  1. Construct A (without B)
  2. Construct B (with A)
  3. Reconfigure A with B

Plus two extra steps:

  1. Remove A
  2. Reconfigure B without A

return moc, nil
}

func (moc *mutualOptionalChild) Reconfigure(ctx context.Context, deps resource.Dependencies,
Copy link
Member Author

Choose a reason for hiding this comment

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

Discussed offline: you must have a reconfigurable resource to leverage optional dependencies at all. This is not something that was captured in documentation, so I'll have to chat with NetCode/docs about this.

@viambot viambot added safe to test This pull request is marked safe to test from a trusted zone and removed safe to test This pull request is marked safe to test from a trusted zone labels Jun 6, 2025
@benjirewis benjirewis requested a review from dgottlieb June 10, 2025 15:31
//
// A resource 'moc' will optionally depend upon 'moc2', and 'moc2' will optionally
// depend upon 'moc'. We will start with only 'moc' in the config, and assert that 'moc'
// builds successfully without 'moc2'. We will then add 'moc2' to the config, assert
Copy link
Member

Choose a reason for hiding this comment

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

We will then add 'moc2' to the config, assert that 'moc' reconfigures successfully, 'moc2' builds successfully, and both resources have handles to each other.

Does moc2 only have its constructor invoked and be successful? Or is the construction immediately followed by a Reconfigure?

I assume only the constructor is called. So moc2 will get its optional dependency via that (thanks to moc[1] already existing). But if moc2 did not have a Reconfigure, it would never be able to learn of changes to moc[1].

edit
Ok, now I see the bit further in the test about weak resources.

Copy link
Member Author

Choose a reason for hiding this comment

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

I assume only the constructor is called. So moc2 will get its optional dependency via that (thanks to moc[1] already existing). But if moc2 did not have a Reconfigure, it would never be able to learn of changes to moc[1].

That sounds correct to me!

@benjirewis benjirewis merged commit d255154 into viamrobotics:main Jun 11, 2025
18 checks passed
@benjirewis benjirewis deleted the optional-deps-cycle-test branch June 11, 2025 14:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
safe to test This pull request is marked safe to test from a trusted zone
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants