1414#include " linglong/cli/printer.h"
1515#include " ocppi/cli/crun/Crun.hpp"
1616
17+ #include < filesystem>
18+ #include < fstream>
19+
1720using namespace linglong ;
1821using ::testing::_;
1922using ::testing::ElementsAre;
@@ -32,10 +35,27 @@ class MockRepo : public repo::OSTreeRepo
3235 {
3336 }
3437
38+ QDir defaultSharedDir () const noexcept { return this ->getDefaultSharedDir (); }
39+
40+ QDir overlaySharedDir () const noexcept { return this ->getOverlayShareDir (); }
41+
3542 MOCK_METHOD (utils::error::Result<std::vector<api::types::v1::PackageInfoV2>>,
3643 listLocal,
3744 (),
3845 (override , const , noexcept ));
46+ MOCK_METHOD (utils::error::Result<package::Reference>,
47+ clearReference,
48+ (const package::FuzzyReference &fuzzyRef,
49+ const repo::clearReferenceOption &opts,
50+ const std::string &module ,
51+ const std::optional<std::string> &repo),
52+ (override , const , noexcept ));
53+ MOCK_METHOD (utils::error::Result<api::types::v1::RepositoryCacheLayersItem>,
54+ getLayerItem,
55+ (const package::Reference &ref,
56+ std::string module ,
57+ const std::optional<std::string> &subRef),
58+ (override , const , noexcept ));
3959 MOCK_METHOD (utils::error::Result<package::ReferenceWithRepo>,
4060 latestRemoteReference,
4161 (const package::FuzzyReference &fuzzyRef),
@@ -49,6 +69,7 @@ class MockPrinter : public cli::CLIPrinter
4969 printUpgradeList,
5070 (std::vector<api::types::v1::UpgradeListResult> &),
5171 (override ));
72+ MOCK_METHOD (void , printContent, (const QStringList &filePaths), (override ));
5273};
5374
5475class CliTest : public ::testing::Test
@@ -237,4 +258,145 @@ TEST_F(CliTest, listUpgradableNoUpgrade)
237258 cli->list (cli::ListOptions{ .showUpgradeList = true });
238259}
239260
261+ TEST_F (CliTest, contentPreferDesktopFromDefaultSharedDir)
262+ {
263+ auto ref = package::Reference::parse (" main:org.example.app/1.0.0/x86_64" );
264+ ASSERT_TRUE (ref.has_value ());
265+
266+ const std::string commit = " test-commit-default" ;
267+ const auto layerEntriesDir =
268+ tempDir->path () / " layers" / commit / " entries" / " share" / " applications" ;
269+ const auto defaultDesktopPath =
270+ std::filesystem::path (repo->defaultSharedDir ().absolutePath ().toStdString ()) / " applications"
271+ / " org.example.app.desktop" ;
272+
273+ std::filesystem::create_directories (layerEntriesDir);
274+ std::filesystem::create_directories (defaultDesktopPath.parent_path ());
275+ std::ofstream (layerEntriesDir / " org.example.app.desktop" ) << " Test desktop content" ;
276+ std::ofstream (defaultDesktopPath) << " Test desktop content in default" ;
277+
278+ api::types::v1::RepositoryCacheLayersItem layerItem;
279+ layerItem.commit = commit;
280+ layerItem.info .kind = " app" ;
281+
282+ EXPECT_CALL (*repo, clearReference (_, _, _, _)).WillOnce (Return (*ref));
283+ EXPECT_CALL (*repo, getLayerItem (_, _, _))
284+ .WillRepeatedly (
285+ [layerItem](const package::Reference &, std::string, const std::optional<std::string> &)
286+ -> utils::error::Result<api::types::v1::RepositoryCacheLayersItem> {
287+ return layerItem;
288+ });
289+ EXPECT_CALL (*printer,
290+ printContent (ElementsAre (QString::fromStdString (defaultDesktopPath.string ()))))
291+ .WillOnce (Return ());
292+
293+ EXPECT_EQ (cli->content (cli::ContentOptions{ .appid = " org.example.app" }), 0 );
294+ }
295+
296+ TEST_F (CliTest, contentFallbackDesktopToOverlaySharedDir)
297+ {
298+ auto ref = package::Reference::parse (" main:org.example.app/1.0.0/x86_64" );
299+ ASSERT_TRUE (ref.has_value ());
300+
301+ const std::string commit = " test-commit-overlay" ;
302+ const auto layerEntriesDir =
303+ tempDir->path () / " layers" / commit / " entries" / " share" / " applications" ;
304+ const auto overlayDesktopPath =
305+ std::filesystem::path (repo->overlaySharedDir ().absolutePath ().toStdString ()) / " applications"
306+ / " org.example.app.desktop" ;
307+
308+ std::filesystem::create_directories (layerEntriesDir);
309+ std::filesystem::create_directories (overlayDesktopPath.parent_path ());
310+ std::ofstream (layerEntriesDir / " org.example.app.desktop" ) << " Test desktop content" ;
311+ std::ofstream (overlayDesktopPath) << " Test desktop content in overlay" ;
312+
313+ api::types::v1::RepositoryCacheLayersItem layerItem;
314+ layerItem.commit = commit;
315+ layerItem.info .kind = " app" ;
316+
317+ EXPECT_CALL (*repo, clearReference (_, _, _, _)).WillOnce (Return (*ref));
318+ EXPECT_CALL (*repo, getLayerItem (_, _, _))
319+ .WillRepeatedly (
320+ [layerItem](const package::Reference &, std::string, const std::optional<std::string> &)
321+ -> utils::error::Result<api::types::v1::RepositoryCacheLayersItem> {
322+ return layerItem;
323+ });
324+ EXPECT_CALL (*printer,
325+ printContent (ElementsAre (QString::fromStdString (overlayDesktopPath.string ()))))
326+ .WillOnce (Return ());
327+
328+ EXPECT_EQ (cli->content (cli::ContentOptions{ .appid = " org.example.app" }), 0 );
329+ }
330+
331+ TEST_F (CliTest, contentMapsLegacySystemdUserPathToExportedLibPath)
332+ {
333+ auto ref = package::Reference::parse (" main:org.example.app/1.0.0/x86_64" );
334+ ASSERT_TRUE (ref.has_value ());
335+
336+ const std::string commit = " test-commit-systemd-legacy" ;
337+ const auto layerEntriesDir =
338+ tempDir->path () / " layers" / commit / " entries" / " share" / " systemd" / " user" ;
339+ const auto exportedDir = tempDir->path () / " entries" / " lib" / " systemd" / " user" ;
340+ const auto exportedFile = exportedDir / " org.example.app.service" ;
341+
342+ std::filesystem::create_directories (layerEntriesDir);
343+ std::filesystem::create_directories (exportedDir);
344+ std::ofstream (layerEntriesDir / " org.example.app.service" )
345+ << " [Service]\n ExecStart=/bin/true\n " ;
346+ std::ofstream (exportedFile) << " [Service]\n ExecStart=/bin/true\n " ;
347+
348+ api::types::v1::RepositoryCacheLayersItem layerItem;
349+ layerItem.commit = commit;
350+ layerItem.info .kind = " app" ;
351+
352+ EXPECT_CALL (*repo, clearReference (_, _, _, _)).WillOnce (Return (*ref));
353+ EXPECT_CALL (*repo, getLayerItem (_, _, _))
354+ .WillRepeatedly (
355+ [layerItem](const package::Reference &, std::string, const std::optional<std::string> &)
356+ -> utils::error::Result<api::types::v1::RepositoryCacheLayersItem> {
357+ return layerItem;
358+ });
359+ EXPECT_CALL (*printer, printContent (ElementsAre (QString::fromStdString (exportedFile.string ()))))
360+ .WillOnce (Return ());
361+
362+ EXPECT_EQ (cli->content (cli::ContentOptions{ .appid = " org.example.app" }), 0 );
363+ }
364+
365+ TEST_F (CliTest, contentPrefersLibSystemdUserOverLegacySharePath)
366+ {
367+ auto ref = package::Reference::parse (" main:org.example.app/1.0.0/x86_64" );
368+ ASSERT_TRUE (ref.has_value ());
369+
370+ const std::string commit = " test-commit-systemd-lib" ;
371+ const auto layerLibDir =
372+ tempDir->path () / " layers" / commit / " entries" / " lib" / " systemd" / " user" ;
373+ const auto layerLegacyDir =
374+ tempDir->path () / " layers" / commit / " entries" / " share" / " systemd" / " user" ;
375+ const auto exportedDir = tempDir->path () / " entries" / " lib" / " systemd" / " user" ;
376+ const auto exportedFile = exportedDir / " org.example.app.service" ;
377+
378+ std::filesystem::create_directories (layerLibDir);
379+ std::filesystem::create_directories (layerLegacyDir);
380+ std::filesystem::create_directories (exportedDir);
381+ std::ofstream (layerLibDir / " org.example.app.service" ) << " [Service]\n ExecStart=/bin/true\n " ;
382+ std::ofstream (layerLegacyDir / " org.example.app.service" ) << " [Service]\n ExecStart=/bin/true\n " ;
383+ std::ofstream (exportedFile) << " [Service]\n ExecStart=/bin/true\n " ;
384+
385+ api::types::v1::RepositoryCacheLayersItem layerItem;
386+ layerItem.commit = commit;
387+ layerItem.info .kind = " app" ;
388+
389+ EXPECT_CALL (*repo, clearReference (_, _, _, _)).WillOnce (Return (*ref));
390+ EXPECT_CALL (*repo, getLayerItem (_, _, _))
391+ .WillRepeatedly (
392+ [layerItem](const package::Reference &, std::string, const std::optional<std::string> &)
393+ -> utils::error::Result<api::types::v1::RepositoryCacheLayersItem> {
394+ return layerItem;
395+ });
396+ EXPECT_CALL (*printer, printContent (ElementsAre (QString::fromStdString (exportedFile.string ()))))
397+ .WillOnce (Return ());
398+
399+ EXPECT_EQ (cli->content (cli::ContentOptions{ .appid = " org.example.app" }), 0 );
400+ }
401+
240402} // namespace
0 commit comments