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

Add functional tests for grub lib #104

Merged
merged 9 commits into from
Aug 31, 2023

Conversation

rgildein
Copy link
Contributor

@rgildein rgildein commented Aug 4, 2023

  • add functional tests for grub lib
    • the feature tested for grub is required to run on a VM instead of a container, so I changed the command in tox to use a VM with LXD
  • add _update_config function to update charm config file instead of overwriting it
  • fix writing lines in _save_config function
  • raise ApplyError if grub-mkconfig failed, since it is failing if wrong configuration was provided

Switch to use update_config instead of save_config in Config.update
function to ensure charm configs are updated and not overwritten.
Fix wiring lines in _save_config function.
Raise ApplyError if grub-mkconfig command failed, since it is failing
also if there is wrong validation.
Copy link
Collaborator

@benhoyt benhoyt left a comment

Choose a reason for hiding this comment

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

Change itself looks reasonable. One small request (avoiding os.linesep) and one larger request (that we avoid mocking the filesystem in unit tests). However, it might be too much of a change in this PR to change how we're testing filesystem operations, so I'd be happy to keep that in mind for future work.

lib/charms/operator_libs_linux/v0/grub.py Outdated Show resolved Hide resolved
lib/charms/operator_libs_linux/v0/grub.py Outdated Show resolved Hide resolved
tests/unit/test_grub.py Outdated Show resolved Hide resolved
)
def test_update_validation_failure(self, _, mock_save, mock_apply):
@mock.patch.object(grub.Config, "_update")
@mock.patch.object(grub, "_update_config")
Copy link
Collaborator

Choose a reason for hiding this comment

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

I still don't like all this mocking. It makes the tests more fragile as we're testing a lot more test internals. Ideally we could change these to use the real filesystem (in a temp directory) where possible, and only use mocking where necessary (for example, the subprocess update-grub calls). Local filesystem operations are fast and reliable, so don't need to be mocked.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hi @benhoyt, I agree, but at the same time the definition of unit tests is testing functionality of single function, because it's not good if I change one thing and it breaks half of unit tests. So for me it's really necessary to tests each function deeply with mocked everything else.
However at the beginning I mention that I agree with you and that's why I provide TestSmokeConfig where I mocked everything and TestFullConfig where I mocked only subprocess check_call. I think that it is good solution, because like this I test all scenarios for each function with mocking other functions and the general usage for .remove and .update function.
What do you think?

Copy link
Collaborator

@benhoyt benhoyt Aug 9, 2023

Choose a reason for hiding this comment

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

I agree, but at the same time the definition of unit tests is testing functionality of single function, because it's not good if I change one thing and it breaks half of unit tests. So for me it's really necessary to tests each function deeply with mocked everything else.

"Unit testing" is a somewhat vague term, but just given the name, I don't think it necessarily means testing functions, but testing units (which could be modules, classes, methods). That said, I don't believe that debating the term is particularly helpful. I agree with you that we want to avoid "if I change one thing it breaks half of the tests".

But, as I see it, mocking internal helper functions is exactly how you'd get that problem. This test is mocking the internal _save_grub_configuration, _update, and _update_config functions. But those are implementation details, so if we change the name or implementation of any of those methods, this test will break. And that's fragile. That's why I advocate mocking as little as possible, and -- as much as possible -- only testing the external contract, not the implementation details. On the other hand, we probably do need to mock subprocess.check_call because it's an external service/program.

In any case, those are my thoughts. I'm not going to block this PR because the testing is not to my taste. We'll see if the tests end up being fragile and we can always change the testing approach later. And we can keep discussing what "unit testing" means over a beer in Riga. :-)

@rgildein
Copy link
Contributor Author

rgildein commented Aug 8, 2023

@benhoyt Can me moved CI to runner, which will be able to run VMs with lxd? It's mandatory to run integration tests for grub on non container machine, or potentially we can mocked that function.

@benhoyt
Copy link
Collaborator

benhoyt commented Aug 9, 2023

@rgildein Approved now. I'm not quite sure what you mean by "Can me moved CI to runner" -- can you clarify and/or propose that in the GitHub workflow YAML?

@rgildein
Copy link
Contributor Author

rgildein commented Aug 9, 2023

@rgildein Approved now. I'm not quite sure what you mean by "Can me moved CI to runner" -- can you clarify and/or propose that in the GitHub workflow YAML?

If you check this line from logs form Integrations tests, it is saying that on that runner we could not create VM machine. I though that we are using self hosted runner on which is not possible to do this, but apparently this is an issue of GitHub runner where nested virtualization is disabled. I'll check how we can enable it.

@benhoyt
Copy link
Collaborator

benhoyt commented Aug 15, 2023

@rgildein Per your Mattermost message, you say:

The issue is that GitHub runners do not allow nested virtualization and the gtub lib require to be run on VM and not container.
Options:

  • We can run only Ubuntu integration tests directly on the runner VM with run: tox -e integration-ubuntu. There is no option to use CentOS. e.g. using runs-on: centos-latest
  • We can run CI on self-hosted machine, which can use lxc launch --vm ....
  • We can run CI on larger GitHub runner 1, where it's possible to enable KVM. However it's billed.
  • We do skip for those functional tests if they are run on container machine for now and test it only manually.

I think we should do option 1 for now -- run Ubuntu integration tests directly on a VM runner. Fairly simple, and gets us at least some automated testing for this.

@rgildein rgildein force-pushed the grub/functional-tests branch 3 times, most recently from 7a792eb to 9f35388 Compare August 15, 2023 14:18
Since the grub lib required to run integration tests on machine or VM,
we are switch to running integration tests directly on GitHub runner.
It was not possible to run it on VMs, since the GitHub runner do not
supports nested virtualization.
@rgildein
Copy link
Contributor Author

The linting error was fixed in #105.

@rgildein rgildein requested a review from benhoyt August 15, 2023 15:00
Copy link
Collaborator

@benhoyt benhoyt left a comment

Choose a reason for hiding this comment

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

I haven't gone through all the tests with a fine-toothed comb, but this looks reasonable. A couple of very minor comments, but approving.

tests/integration/test_grub.py Show resolved Hide resolved
with pytest.raises(grub.ValidationError):
grub_conf_2.update(config_2)

assert grub_conf_2.path.exists() is False
Copy link
Collaborator

Choose a reason for hiding this comment

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

assert not grub_conf_2.path.exists() would be the Pythonic way to spell this. (Similar elsewhere.)

@benhoyt
Copy link
Collaborator

benhoyt commented Aug 30, 2023

The code looks good. Looks like the Ubuntu integration tests are failing / timing out, though.

@rgildein
Copy link
Contributor Author

The code looks good. Looks like the Ubuntu integration tests are failing / timing out, though.

The tests are not failing, but the "Setup operator environment" is failing. I think it's due some change in system config. We can check all tests and properly clean any configuration or simply run juju-systemd-notices first or even better move that to separate job. What do you think? To me the best option is to move it to separate job.

@rgildein rgildein requested a review from benhoyt August 31, 2023 10:09
@benhoyt
Copy link
Collaborator

benhoyt commented Aug 31, 2023

For the record, @NucciTheBoss said:

I wouldn't be surprised if the grub functional tests modified something that prevented lxd from restarting the containers correctly. For example, if you don't clean up your NFS kernel server running inside an LXD container, lxc/lxd commands will hang for most of eternity unless. That seems to have been what happened with this job https://github.com/canonical/operator-libs-linux/actions/runs/6025011553/job/16344855206

So we should probably get to the bottom of that at some point. In the meantime, I'm going to merge this. We can always improve / isolate CI later.

@benhoyt benhoyt merged commit 20b8afd into canonical:main Aug 31, 2023
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants