From 644d6f85223d486a412af9aaf4b40ef3d5d6705b Mon Sep 17 00:00:00 2001 From: Harald Sitter Date: Tue, 26 Feb 2019 13:48:08 +0100 Subject: [PATCH] redo service menu ruby helpers from ground up more or less Summary: - apply ruby community style guidelines - full rewrite fixing, among other things: - inefficient/unreadable String#end_with reimplementation - inefficient use of Kernel.system (forked shell to fork a shell) - inefficient/unreadable Dir.glob reimplementation - inefficient File initialization for single chmod - invocation conditions are now actually readable - invocation conditions now also force +x on argless scripts, not just scripts that need arguments - repetitive conditions are now expressed as loops on argument arrays - mime detection now uses xdg-mime instead of file (xdg-mime internally may fall back to mime but will prefer higher level tools such as kmimetypefinder5; giving better results overall) - return values of "backtick forks" are now checked and will produce suitable errors on stderr - fail now takes a log_msg argument which is printed to stderr. this is in addition to the error raised as notification for the user, as that is unfortunately not so useful for diagnostics - overall error handling and logging of problem causes is much improved - add license headers. the original code was actually fairly exhaustive, so this really should have had a header to begin with. the code was originally introduced in svn r1045663 on Nov 6 14:56:35 2009 UTC - add blackbox tests. in the interest of keeping the scripts actually simple scripts (as opposed to a bunch of classes used by even simpler scirpts) they are now also covered by test rigging which runs them as scripts (again, as opposed to individual unit testing of distinct units) - the tests optionally can use simplecov to gather coverage metrics - also wired up to ctest so it actually gets run structurally there is actually a fair amount of overlap between the two scripts, but again, in the interest of keeping things simple I think it's better to live with that instead of refactoring a shared library out of it and then use heavy-duty meta-programming Reviewers: #dolphin, elvisangelaccio Reviewed By: #dolphin, elvisangelaccio Subscribers: elvisangelaccio, kfm-devel Tags: #dolphin Differential Revision: https://phabricator.kde.org/D19334 --- .../services/servicemenudeinstallation | 97 ++++++--- src/settings/services/servicemenuinstallation | 206 +++++++++++------- .../test/service_menu_deinstallation_test.rb | 121 ++++++++++ .../test/service_menu_installation_test.rb | 122 +++++++++++ src/settings/services/test/test_helper.rb | 78 +++++++ src/settings/services/test/test_run.rb | 24 ++ src/tests/CMakeLists.txt | 2 + 7 files changed, 540 insertions(+), 110 deletions(-) create mode 100644 src/settings/services/test/service_menu_deinstallation_test.rb create mode 100644 src/settings/services/test/service_menu_installation_test.rb create mode 100644 src/settings/services/test/test_helper.rb create mode 100755 src/settings/services/test/test_run.rb diff --git a/src/settings/services/servicemenudeinstallation b/src/settings/services/servicemenudeinstallation index 5e4234262c..9e090e2cda 100755 --- a/src/settings/services/servicemenudeinstallation +++ b/src/settings/services/servicemenudeinstallation @@ -1,37 +1,72 @@ #!/usr/bin/env ruby + +# Copyright (C) 2009 Jonathan Schmidt-Dominé +# Copyright (C) 2019 Harald Sitter +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the +# Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + require 'fileutils' -archive = ARGV[0] -if archive[(archive.length - 8)..(archive.length)] == ".desktop" - FileUtils.rm(`qtpaths --writable-path GenericDataLocation`.strip! + "/kservices5/ServiceMenus/" + File.basename(archive)) - exit(0) + +ARCHIVE = ARGV[0] + +# @param log_msg [String] error that gets logged to CLI +def fail(log_msg: nil) + # FIXME: this is not translated... + msg = 'Dolphin service menu installation failed' + warn log_msg if log_msg + system('kdialog', '--passivepopup', msg, '15') + abort end -dir = archive + "-dir" -# try: deinstall.sh -# try: deinstall -# try: installKDE4.sh -# try: installKDE4 -# try: install.sh -# try: install -while true - dd = Dir.new(dir) - break if dd.count != 3 - odir = dir - for entry in dd - dir += "/" + entry if entry != "." && entry != ".." - end - if !File.directory? dir - dir = odir - break + +if ARCHIVE.end_with?('.desktop') + data_location = `qtpaths --writable-path GenericDataLocation`.strip + unless $?.success? + fail(log_msg: "Could not get GenericDataLocation #{data_location}") + end + FileUtils.rm("#{data_location}/kservices5/ServiceMenus/#{File.basename(ARCHIVE)}") + exit(0) +end +dir = "#{ARCHIVE}-dir" + +deinstaller = nil +%w[deinstall.sh deinstall].find do |script| + deinstaller = Dir.glob("#{dir}/**/#{script}")[0] +end + +installer = nil +%w[install-it.sh install-it installKDE4.sh installKDE4 install.sh install].find do |script| + installer = Dir.glob("#{dir}/**/#{script}")[0] +end + +Dir.chdir(dir) do + deinstalled = false + + [deinstaller, installer].uniq.compact.each { |f| File.chmod(0o700, f) } + + if deinstaller + puts "[servicemenudeinstallation]: Trying to run deinstaller #{deinstaller}" + deinstalled = system(deinstaller) + elsif installer + puts "[servicemenudeinstallation]: Trying to run installer #{installer}" + %w[--remove --delete --uninstall --deinstall].any? do |arg| + deinstalled = system(installer, arg) end + end + + fail unless deinstalled end -Dir.chdir(dir) -def fail() - system("kdialog --passivepopup \"Deinstallation failed\" 15") - exit(-1) -end -if !((File.exist?(file = "./deinstall.sh") || File.exist?(file = "./deinstall")) && system(file)) - fail() if !File.exist?(file = "./installKDE4.sh") && !File.exist?(file = "./installKDE4") && !File.exist?(file = "./install.sh") && !File.exist?(file = "./install") -File.new(file).chmod(0700) - fail() if !system(file + " --remove") && !system(file + " --delete") && !system(file + " --uninstall") && !system(file + " --deinstall") -end + FileUtils.rm_r(dir) diff --git a/src/settings/services/servicemenuinstallation b/src/settings/services/servicemenuinstallation index 60b699bb60..e2d42bfbf4 100755 --- a/src/settings/services/servicemenuinstallation +++ b/src/settings/services/servicemenuinstallation @@ -1,88 +1,136 @@ #!/usr/bin/env ruby -require 'pathname' + +# Copyright (C) 2009 Jonathan Schmidt-Dominé +# Copyright (C) 2019 Harald Sitter +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the +# Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + require 'fileutils' -archive = ARGV[0] -$servicedir = `qtpaths --writable-path GenericDataLocation`.strip! + "/kservices5/ServiceMenus/" -FileUtils.mkdir_p($servicedir) if !File.exist?($servicedir) -if archive[(archive.length - 8)..(archive.length - 1)] == ".desktop" - puts "Single-File Service-Menu" - puts archive - puts $servicedir - FileUtils.cp(archive, $servicedir); - exit(0) -end -def mimeType(filename) - IO.popen("file --mime-type -b " + filename).gets().strip!() -end -$archivetypes = { "application/x-tar" => :"tar -xf %s -C %s", - "application/tar" => :"tar -xf %s -C %s", - "application/x-gzip" => :"tar -zxf %s -C %s", - "application/gzip" => :"tar -zxf %s -C %s", - "application/x-gzip-compressed-tar" => :"tar -zxf %s -C %s", - "application/gzip-compressed-tar" => :"tar -zxf %s -C %s", - "application/x-gzip-compressed" => :"tar -zxf %s -C %s", - "application/gzip-compressed" => :"tar -zxf %s -C %s", - "application/bzip" => :"tar -jxf %s -C %s", - "application/bzip2" => :"tar -jxf %s -C %s", - "application/x-bzip" => :"tar -jxf %s -C %s", - "application/x-bzip2" => :"tar -jxf %s -C %s", - "application/bzip-compressed" => :"tar -jxf %s -C %s", - "application/bzip2-compressed" => :"tar -jxf %s -C %s", - "application/x-bzip-compressed" => :"tar -jxf %s -C %s", - "application/x-bzip2-compressed" => :"tar -jxf %s -C %s", - "application/bzip-compressed-tar" => :"tar -jxf %s -C %s", - "application/bzip2-compressed-tar" => :"tar -jxf %s -C %s", - "application/x-bzip-compressed-tar" => :"tar -jxf %s -C %s", - "application/x-bzip2-compressed-tar" => :"tar -jxf %s -C %s", - "application/zip" => :"unzip %s -d %s", - "application/x-zip" => :"unzip %s -d %s", - "application/x-zip-compressed" => :"unzip %s -d %s", - "multipart/x-zip" => :"unzip %s -d %s", - "application/tgz" => :"tar -zxf %s -C %s", - "application/x-compressed-gtar" => :"tar -zxf %s -C %s", - "file/tgz" => :"tar -zxf %s -C %s", - "multipart/x-tar-gz" => :"tar -zxf %s -C %s", - "application/x-gunzip" => :"tar -zxf %s -C %s", - "application/gzipped" => :"tar -zxf %s -C %s", - "gzip/document" => :"tar -zxf %s -C %s", - "application/x-bz2 " => :"tar -jxf %s -C %s", - "application/x-gtar" => :"tar -xf %s -C %s", - "multipart/x-tar" => :"tar -xf %s -C %s" + +ARCHIVE_UNCOMPRESSORS = { + 'application/x-tar' => :"tar -xf %s -C %s", + 'application/tar' => :"tar -xf %s -C %s", + 'application/x-gzip' => :"tar -zxf %s -C %s", + 'application/gzip' => :"tar -zxf %s -C %s", + 'application/x-gzip-compressed-tar' => :"tar -zxf %s -C %s", + 'application/gzip-compressed-tar' => :"tar -zxf %s -C %s", + 'application/x-gzip-compressed' => :"tar -zxf %s -C %s", + 'application/gzip-compressed' => :"tar -zxf %s -C %s", + 'application/bzip' => :"tar -jxf %s -C %s", + 'application/bzip2' => :"tar -jxf %s -C %s", + 'application/x-bzip' => :"tar -jxf %s -C %s", + 'application/x-bzip2' => :"tar -jxf %s -C %s", + 'application/bzip-compressed' => :"tar -jxf %s -C %s", + 'application/bzip2-compressed' => :"tar -jxf %s -C %s", + 'application/x-bzip-compressed' => :"tar -jxf %s -C %s", + 'application/x-bzip2-compressed' => :"tar -jxf %s -C %s", + 'application/bzip-compressed-tar' => :"tar -jxf %s -C %s", + 'application/bzip2-compressed-tar' => :"tar -jxf %s -C %s", + 'application/x-bzip-compressed-tar' => :"tar -jxf %s -C %s", + 'application/x-bzip2-compressed-tar' => :"tar -jxf %s -C %s", + 'application/zip' => :"unzip %s -d %s", + 'application/x-zip' => :"unzip %s -d %s", + 'application/x-zip-compressed' => :"unzip %s -d %s", + 'multipart/x-zip' => :"unzip %s -d %s", + 'application/tgz' => :"tar -zxf %s -C %s", + 'application/x-compressed-gtar' => :"tar -zxf %s -C %s", + 'file/tgz' => :"tar -zxf %s -C %s", + 'multipart/x-tar-gz' => :"tar -zxf %s -C %s", + 'application/x-gunzip' => :"tar -zxf %s -C %s", + 'application/gzipped' => :"tar -zxf %s -C %s", + 'gzip/document' => :"tar -zxf %s -C %s", + 'application/x-bz2 ' => :"tar -jxf %s -C %s", + 'application/x-gtar' => :"tar -xf %s -C %s", + 'multipart/x-tar' => :"tar -xf %s -C %s" } + +ARCHIVE = ARGV[0] + +# @param log_msg [String] error that gets logged to CLI +def fail(log_msg: nil) + # FIXME: this is not translated... + msg = 'Dolphin service menu installation failed' + warn log_msg if log_msg + system('kdialog', '--passivepopup', msg, '15') + abort +end + +def mime_type(filename) + ret = `xdg-mime query filetype #{filename}`.strip + return ret if $?.success? + + warn 'Failed to xdg-mime' + fail(log_msg: "Failed to xdg-mime #{filename}: #{ret}") +end + def uncompress(filename, output) - system(sprintf($archivetypes[mimeType(filename)].to_s, filename, output)) + uncompressor = ARCHIVE_UNCOMPRESSORS.fetch(mime_type(filename)).to_s + system(format(uncompressor, filename, output)) +rescue KeyError => e + # If a mimetype doesn't have an uncompressor mapped we'll get a keyerror. + # we'll log the error but visually report the failure. + fail(log_msg: "Unmapped compression format #{filename}; #{e.message}") end -dir = archive + "-dir" -if File.exist?(dir) - FileUtils.rm_r(dir) + +data_location = `qtpaths --writable-path GenericDataLocation`.strip +unless $?.success? + fail(log_msg: "Could not get GenericDataLocation #{data_location}") end +servicedir = "#{data_location}/kservices5/ServiceMenus/" + +FileUtils.mkdir_p(servicedir) unless File.exist?(servicedir) +if ARCHIVE.end_with?('.desktop') + puts 'Single-File Service-Menu' + puts ARCHIVE + puts servicedir + FileUtils.cp(ARCHIVE, servicedir) + exit +end + +dir = "#{ARCHIVE}-dir" + +FileUtils.rm_r(dir) if File.exist?(dir) FileUtils.mkdir(dir) -exit(-1) if !uncompress(archive, dir) -# try: install-it.sh -# try: install-it -# try: installKDE4.sh -# try: installKDE4 -# try: install.sh -# try: install -while true - dd = Dir.new(dir) - break if dd.count != 3 - odir = dir - for entry in dd - dir += "/" + entry if entry != "." && entry != ".." - end - if !File.directory? dir - dir = odir - break - end + +fail(log_msg: 'uncompress failed') unless uncompress(ARCHIVE, dir) + +install_it = nil +%w[install-it.sh install-it].find do |script| + install_it = Dir.glob("#{dir}/**/#{script}")[0] end -Dir.chdir(dir) -def fail() - system("kdialog --passivepopup \"Installation failed\" 15") - exit(-1) + +installer = nil +%w[installKDE4.sh installKDE4 install.sh install].find do |script| + installer = Dir.glob("#{dir}/**/#{script}")[0] +end + +Dir.chdir(dir) do + installed = false + + [install_it, installer].uniq.compact.each { |f| File.chmod(0o700, f) } + + if install_it + puts "[servicemenuinstallation]: Trying to run install_it #{install_it}" + installed = system(install_it) + elsif installer + puts "[servicemenuinstallation]: Trying to run installer #{installer}" + %w[--local --local-install --install].any? do |arg| + installed = system(installer, arg) + end + end + + fail unless installed end -if !((File.exist?(file = "./install-it.sh") || File.exist?(file = "./install-it")) && system(file)) - fail() if !File.exist?(file = "./installKDE4.sh") && !File.exist?(file = "./installKDE4") && !File.exist?(file = "./install.sh") && !File.exist?(file = "./install") - File.new(file).chmod(0700) - fail() if !system(file + " --local") && !system(file + "--local-install") && !system(file + " --install") -end diff --git a/src/settings/services/test/service_menu_deinstallation_test.rb b/src/settings/services/test/service_menu_deinstallation_test.rb new file mode 100644 index 0000000000..2b3514a651 --- /dev/null +++ b/src/settings/services/test/service_menu_deinstallation_test.rb @@ -0,0 +1,121 @@ +#!/usr/bin/env ruby + +# Copyright (C) 2019 Harald Sitter +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the +# Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +require_relative 'test_helper' + +require 'tmpdir' + +class ServiceMenuDeinstallationTest < Test::Unit::TestCase + def setup + @tmpdir = Dir.mktmpdir("dolphintest-#{self.class.to_s.tr(':', '_')}") + @pwdir = Dir.pwd + Dir.chdir(@tmpdir) + + ENV['XDG_DATA_HOME'] = File.join(@tmpdir, 'data') + end + + def teardown + Dir.chdir(@pwdir) + FileUtils.rm_rf(@tmpdir) + + ENV.delete('XDG_DATA_HOME') + end + + def test_run_deinstall + service_dir = File.join(Dir.pwd, 'share/servicemenu-download') + archive_base = "#{service_dir}/foo.zip" + archive_dir = "#{archive_base}-dir/foo-1.1/" + FileUtils.mkpath(archive_dir) + File.write("#{archive_dir}/deinstall.sh", <<-DEINSTALL_SH) +#!/bin/sh +touch #{@tmpdir}/deinstall.sh-run + DEINSTALL_SH + File.write("#{archive_dir}/install.sh", <<-INSTALL_SH) +#!/bin/sh +touch #{@tmpdir}/install.sh-run + INSTALL_SH + + assert(covered_system('servicemenudeinstallation', archive_base)) + + # deinstaller should be run + # installer should not be run + # archive_dir should have been correctly removed + + assert_path_exist('deinstall.sh-run') + assert_path_not_exist('install.sh-run') + assert_path_not_exist(archive_dir) + end + + def test_run_install_with_arg + service_dir = File.join(Dir.pwd, 'share/servicemenu-download') + archive_base = "#{service_dir}/foo.zip" + archive_dir = "#{archive_base}-dir/foo-1.1/" + FileUtils.mkpath(archive_dir) + + File.write("#{archive_dir}/install.sh", <<-INSTALL_SH) +#!/bin/sh +if [ "$@" = "--uninstall" ]; then + touch #{@tmpdir}/install.sh-run + exit 0 +fi +exit 1 + INSTALL_SH + + assert(covered_system('servicemenudeinstallation', archive_base)) + + assert_path_not_exist('deinstall.sh-run') + assert_path_exist('install.sh-run') + assert_path_not_exist(archive_dir) + end + + # no scripts in sight + def test_run_fail + service_dir = File.join(Dir.pwd, 'share/servicemenu-download') + archive_base = "#{service_dir}/foo.zip" + archive_dir = "#{archive_base}-dir/foo-1.1/" + FileUtils.mkpath(archive_dir) + + refute(covered_system('servicemenudeinstallation', archive_base)) + + # I am unsure if deinstallation really should keep the files around. But + # that's how it behaved originally so it's supposedly intentional + # - sitter, 2019 + assert_path_exist(archive_dir) + end + + # For desktop files things are a bit special. There is one in .local/share/servicemenu-download + # and another in the actual ServiceMenus dir. The latter gets removed by the + # script, the former by KNS. + def test_run_desktop + service_dir = File.join(Dir.pwd, 'share/servicemenu-download') + downloaded_file = "#{service_dir}/foo.desktop" + FileUtils.mkpath(service_dir) + FileUtils.touch(downloaded_file) + + menu_dir = "#{ENV['XDG_DATA_HOME']}/kservices5/ServiceMenus/" + installed_file = "#{menu_dir}/foo.desktop" + FileUtils.mkpath(menu_dir) + FileUtils.touch(installed_file) + + assert(covered_system('servicemenudeinstallation', downloaded_file)) + + assert_path_exist(downloaded_file) + assert_path_not_exist(installed_file) + end +end diff --git a/src/settings/services/test/service_menu_installation_test.rb b/src/settings/services/test/service_menu_installation_test.rb new file mode 100644 index 0000000000..3c9fc90ef0 --- /dev/null +++ b/src/settings/services/test/service_menu_installation_test.rb @@ -0,0 +1,122 @@ +#!/usr/bin/env ruby + +# Copyright (C) 2019 Harald Sitter +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the +# Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +require_relative 'test_helper' + +require 'tmpdir' + +class ServiceMenuInstallationTest < Test::Unit::TestCase + def setup + @tmpdir = Dir.mktmpdir("dolphintest-#{self.class.to_s.tr(':', '_')}") + @pwdir = Dir.pwd + Dir.chdir(@tmpdir) + + ENV['XDG_DATA_HOME'] = File.join(@tmpdir, 'data') + end + + def teardown + Dir.chdir(@pwdir) + FileUtils.rm_rf(@tmpdir) + + ENV.delete('XDG_DATA_HOME') + end + + def test_run_install + service_dir = File.join(Dir.pwd, 'share/servicemenu-download') + FileUtils.mkpath(service_dir) + archive = "#{service_dir}/foo.tar" + + archive_dir = 'foo' # relative so tar cf is relative without fuzz + FileUtils.mkpath(archive_dir) + File.write("#{archive_dir}/install-it.sh", <<-INSTALL_IT_SH) +#!/bin/sh +touch #{@tmpdir}/install-it.sh-run +INSTALL_IT_SH + File.write("#{archive_dir}/install.sh", <<-INSTALL_SH) +#!/bin/sh +touch #{@tmpdir}/install.sh-run + INSTALL_SH + assert(system('tar', '-cf', archive, archive_dir)) + + assert(covered_system('servicemenuinstallation', archive)) + + tar_dir = "#{service_dir}/foo.tar-dir" + tar_extract_dir = "#{service_dir}/foo.tar-dir/foo" + assert_path_exist(tar_dir) + assert_path_exist(tar_extract_dir) + assert_path_exist("#{tar_extract_dir}/install-it.sh") + assert_path_exist("#{tar_extract_dir}/install.sh") + end + + def test_run_install_with_arg + service_dir = File.join(Dir.pwd, 'share/servicemenu-download') + FileUtils.mkpath(service_dir) + archive = "#{service_dir}/foo.tar" + + archive_dir = 'foo' # relative so tar cf is relative without fuzz + FileUtils.mkpath(archive_dir) + File.write("#{archive_dir}/install.sh", <<-INSTALL_SH) +#!/bin/sh +if [ "$@" = "--install" ]; then + touch #{@tmpdir}/install.sh-run + exit 0 +fi +exit 1 + INSTALL_SH + assert(system('tar', '-cf', archive, archive_dir)) + + assert(covered_system('servicemenuinstallation', archive)) + + tar_dir = "#{service_dir}/foo.tar-dir" + tar_extract_dir = "#{service_dir}/foo.tar-dir/foo" + assert_path_exist(tar_dir) + assert_path_exist(tar_extract_dir) + assert_path_not_exist("#{tar_extract_dir}/install-it.sh") + assert_path_exist("#{tar_extract_dir}/install.sh") + end + + def test_run_fail + service_dir = File.join(Dir.pwd, 'share/servicemenu-download') + FileUtils.mkpath(service_dir) + archive = "#{service_dir}/foo.tar" + + archive_dir = 'foo' # relative so tar cf is relative without fuzz + FileUtils.mkpath(archive_dir) + assert(system('tar', '-cf', archive, archive_dir)) + + refute(covered_system('servicemenuinstallation', archive)) + end + + def test_run_desktop + service_dir = File.join(Dir.pwd, 'share/servicemenu-download') + downloaded_file = "#{service_dir}/foo.desktop" + FileUtils.mkpath(service_dir) + FileUtils.touch(downloaded_file) + + menu_dir = "#{ENV['XDG_DATA_HOME']}/kservices5/ServiceMenus/" + installed_file = "#{menu_dir}/foo.desktop" + FileUtils.mkpath(menu_dir) + FileUtils.touch(installed_file) + + assert(covered_system('servicemenuinstallation', downloaded_file)) + + assert_path_exist(downloaded_file) + assert_path_exist(installed_file) + end +end diff --git a/src/settings/services/test/test_helper.rb b/src/settings/services/test/test_helper.rb new file mode 100644 index 0000000000..98c29c1391 --- /dev/null +++ b/src/settings/services/test/test_helper.rb @@ -0,0 +1,78 @@ +# Copyright (C) 2019 Harald Sitter +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the +# Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +begin + require 'simplecov' + SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new( + [ + SimpleCov::Formatter::HTMLFormatter + ] + ) + SimpleCov.start +rescue LoadError + warn 'SimpleCov not loaded' +end + +# FIXME: add coverage report for jenkins? + +$LOAD_PATH.unshift(File.absolute_path('../', __dir__)) # ../ + +def __test_method_name__ + return @method_name if defined?(:@method_name) + index = 0 + caller = '' + until caller.start_with?('test_') + caller = caller_locations(index, 1)[0].label + index += 1 + end + caller +end + +# system() variant which sets up merge-coverage. simplecov supports merging +# of multiple coverage sets. we use this to get coverage metrics on the +# binaries without having to refactor the script into runnable classes. +def covered_system(cmd, *argv) + pid = fork do + Kernel.module_exec do + alias_method(:real_system, :system) + define_method(:system) do |*args| + return true if args.include?('kdialog') # disable kdialog call + real_system(*args) + end + end + + begin + require 'simplecov' + SimpleCov.start do + command_name "#{cmd}_#{__test_method_name__}" + merge_timeout 16 + end + rescue LoadError + warn 'SimpleCov not loaded' + end + + ARGV.replace(argv) + load "#{__dir__}/../#{cmd}" + puts 'all good, fork ending!' + exit 0 + end + waitedpid, status = Process.waitpid2(pid) + assert_equal(pid, waitedpid) + status.success? # behave like system and return the success only +end + +require 'test/unit' diff --git a/src/settings/services/test/test_run.rb b/src/settings/services/test/test_run.rb new file mode 100755 index 0000000000..b2149bbe0a --- /dev/null +++ b/src/settings/services/test/test_run.rb @@ -0,0 +1,24 @@ +#!/usr/bin/env ruby + +# Copyright (C) 2019 Harald Sitter +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the +# Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# This is a fancy wrapper around test_helper to prevent the collector from +# loading the helper twice as it would occur if we ran the helper directly. + +require_relative 'test_helper' + +Test::Unit::AutoRunner.run(true, File.absolute_path(__dir__)) diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 07e4257a0b..d788479bd5 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -69,3 +69,5 @@ ecm_add_test(placesitemmodeltest.cpp TEST_NAME placesitemmodeltest LINK_LIBRARIES dolphinprivate dolphinstatic Qt5::Test) +add_test(NAME servicemenutest + COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/../settings/services/test/test_run.rb)