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

Refactor image vault utils #3846

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open

Conversation

Sploder12
Copy link
Contributor

This PR refactors several functions related to image vault utilities. All util functions now have unit test coverage and can be mocked to ease testing for components that use the utilities. Some functions have been removed or moved to file_ops since they already exist or fit better there.

MULTI-1653

Copy link

codecov bot commented Dec 16, 2024

Codecov Report

Attention: Patch coverage is 85.93750% with 9 lines in your changes missing coverage. Please review.

Project coverage is 89.13%. Comparing base (cbcb5e7) to head (a57672d).
Report is 27 commits behind head on main.

Files with missing lines Patch % Lines
src/platform/backends/lxd/lxd_vm_image_vault.cpp 42.85% 4 Missing ⚠️
src/xz_decoder/xz_image_decoder.cpp 0.00% 3 Missing ⚠️
src/daemon/default_vm_image_vault.cpp 80.00% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3846      +/-   ##
==========================================
+ Coverage   89.02%   89.13%   +0.11%     
==========================================
  Files         255      255              
  Lines       14583    14604      +21     
==========================================
+ Hits        12982    13018      +36     
+ Misses       1601     1586      -15     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Contributor

@sharder996 sharder996 left a comment

Choose a reason for hiding this comment

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

Looks good @Sploder12

Thanks for tackling this. This has needed to be cleaned up for a while.

src/utils/file_ops.cpp Outdated Show resolved Hide resolved
tests/test_file_ops.cpp Show resolved Hide resolved
include/multipass/vm_image_vault.h Outdated Show resolved Hide resolved
@Sploder12 Sploder12 force-pushed the refactor-image-vault-utils branch from f128abb to 4ec3486 Compare January 17, 2025 14:17
sharder996
sharder996 previously approved these changes Jan 17, 2025
Copy link
Contributor

@georgeliao georgeliao left a comment

Choose a reason for hiding this comment

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

@Sploder12
Well done, I only have a few minor comments and questions.

using DefaultDecoderT = XzImageDecoder;

virtual QString copy_to_dir(const QString& file, const QDir& output_dir) const;
[[nodiscard]] virtual QString compute_hash(QIODevice& device) const;
Copy link
Contributor

Choose a reason for hiding this comment

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

comput_hash function is used internally (except the unit test), do we have to expose that? Can it be a private function?

Copy link
Contributor Author

@Sploder12 Sploder12 Jan 21, 2025

Choose a reason for hiding this comment

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

It could be made protected nevermind

Copy link
Contributor

@georgeliao georgeliao Jan 21, 2025

Choose a reason for hiding this comment

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

Is it the only role of it is being called as sub-function of the other compute_file_hash function? If so, I do not even see the point of unit testing that alone, because the unit test of compute_file_hash should cover that.

Copy link
Contributor

@georgeliao georgeliao Jan 21, 2025

Choose a reason for hiding this comment

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

Anyway, I do not have a strong opinion on this. Feel free to go either way.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My reasoning for making it was to make compute_file_hash easier to unit test, since otherwise mocking would require a wrapper around the Qt hashing functions. Or it could use a real file for testing, but that tends to cause issues in CI so I wanted to avoid it.

Copy link
Contributor

Choose a reason for hiding this comment

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

ok I see now, basically QIODevice& device acted as a dependency injection point so you can pass down buffer as opposed to a file. I think we can compromise on this in this particular case. But In general, exposing private function for unit testing should not be the norm. Because of breaking encapsulation and increasing the test brittleness.


virtual void verify_file_hash(const QString& file, const QString& hash) const;

virtual QString extract_file(const QString& file, const Decoder& decoder, bool delete_original = false) const;
Copy link
Contributor

Choose a reason for hiding this comment

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

Same question about the exposure of this non-template extract_file function.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Same response, in this case const Decoder& decoder (a function object) is used as an injection point.

{

class MockImageDecoder
{
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice standalone mock class only for template parameter use.

new_image_path.remove(".xz");
QString mp::ImageVaultUtils::extract_file(const QString& file, const Decoder& decoder, bool delete_original) const
{
const auto fs_new_path = MP_FILEOPS.remove_extension(file.toStdU16String());
Copy link
Contributor

Choose a reason for hiding this comment

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

Why exactly do we need the toStdU16String function as opposed to toStdString? I thought the toStdString checks the value type and return the corresponding string so it should be safe on windows, maybe I have missed something. Same question goes to fs_new_path.u8string() called later.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It is not safe to use a UTF-8 std::string on windows here. For example, see here. u8path solves this issue but is deprecated since c++20. Using the UTF-16 constructor of path, which is what this does, will always convert to the native encoding.

Copy link
Contributor

Choose a reason for hiding this comment

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

Another wondering is the QString::toStdU16String's behavior on linux, std::u16string is the input string to construct fs::path, will that be safe considering linux use UTF-8?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The path constructor overload for std::u16string says "conversion from UTF-16 to native filesystem encoding is used" so it should be safe. While the std::string constructor says "is assumed to be the native narrow encoding". This whole issue is probably why they added the std::u8string overload in C++20 which has the same spec as std::u16string, but with UTF-8

const auto image_filename = mp::vault::filename_for(image_url.path());

QFileInfo file_info{image_url.path()};
const auto image_filename = file_info.fileName();
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we do one liner const auto image_filename = QFileInfo{image_url.path()}.fileName();? Which is cleaner. Same goes to other occurrences of this pattern in the PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I did so for where I could. The places with remove need an lvalue reference so a one liner won't work unless we change the API for the fileop.

src/utils/vm_image_vault_utils.cpp Show resolved Hide resolved
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.

4 participants