aboutsummaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorMartin Pärtel <martin.partel@gmail.com>2011-07-08 19:09:07 +0300
committerMartin Pärtel <martin.partel@gmail.com>2011-07-08 19:09:07 +0300
commit2135ad723dce9654f1844ec4f76f06c9e240be98 (patch)
tree31b94696aa7e3da71b823e8d856febb7ee245e5c /tests
downloadbindfs-2135ad723dce9654f1844ec4f76f06c9e240be98.tar.gz
Initial commit to git.
Diffstat (limited to 'tests')
-rw-r--r--tests/Makefile.am2
-rwxr-xr-xtests/common.rb154
-rwxr-xr-xtests/test_bindfs.rb206
-rwxr-xr-xtests/test_concurrent.rb45
4 files changed, 407 insertions, 0 deletions
diff --git a/tests/Makefile.am b/tests/Makefile.am
new file mode 100644
index 0000000..79b8dee
--- /dev/null
+++ b/tests/Makefile.am
@@ -0,0 +1,2 @@
+
+TESTS = test_bindfs.rb
diff --git a/tests/common.rb b/tests/common.rb
new file mode 100755
index 0000000..f15110c
--- /dev/null
+++ b/tests/common.rb
@@ -0,0 +1,154 @@
+#!/usr/bin/env ruby
+#
+# Copyright 2006,2007,2008,2009,2010 Martin Pärtel <martin.partel@gmail.com>
+#
+# This file is part of bindfs.
+#
+# bindfs 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.
+#
+# bindfs 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 bindfs. If not, see <http://www.gnu.org/licenses/>.
+#
+
+require 'fileutils.rb'
+include FileUtils
+
+# Set the default umask for all tests
+File.umask 0022
+
+EXECUTABLE_PATH = '../src/bindfs'
+TESTDIR_NAME = 'tmp_test_bindfs'
+
+# If set to an array of test names, only those will be run
+$only_these_tests = nil
+
+# Prepares a test environment with a mounted directory
+def testenv(bindfs_args, &block)
+
+ testcase_title = bindfs_args
+
+ return unless $only_these_tests == nil or $only_these_tests.member? testcase_title
+
+ puts "--- #{testcase_title} ---"
+ puts "[ #{bindfs_args} ]"
+
+ begin
+ Dir.mkdir TESTDIR_NAME
+ rescue Exception => ex
+ $stderr.puts "ERROR creating testdir at #{TESTDIR_NAME}"
+ $stderr.puts ex
+ exit! 1
+ end
+
+ begin
+ Dir.chdir TESTDIR_NAME
+ Dir.mkdir 'src'
+ Dir.mkdir 'mnt'
+ rescue Exception => ex
+ $stderr.puts "ERROR preparing testdir at #{TESTDIR_NAME}"
+ $stderr.puts ex
+ exit! 1
+ end
+
+ bindfs_pid = nil
+ begin
+ cmd = "../#{EXECUTABLE_PATH} #{bindfs_args} src mnt"
+ bindfs_pid = Process.fork do
+ exec cmd
+ exit! 127
+ end
+ rescue Exception => ex
+ $stderr.puts "ERROR running bindfs"
+ $stderr.puts ex
+ Dir.chdir '..'
+ system("rm -Rf #{TESTDIR_NAME}")
+ exit! 1
+ end
+
+ # Wait for bindfs to daemonize itself
+ Process.wait bindfs_pid
+
+ # TODO: check that mounting was successful
+
+ testcase_ok = true
+ begin
+ yield
+ rescue Exception => ex
+ $stderr.puts "ERROR: testcase `#{testcase_title}' failed"
+ $stderr.puts ex
+ $stderr.puts ex.backtrace
+ testcase_ok = false
+ end
+
+ begin
+ unless system(umount_cmd + ' mnt')
+ raise Exception.new(umount_cmd + " failed with status #{$?}")
+ end
+ rescue Exception => ex
+ $stderr.puts "ERROR: failed to umount"
+ $stderr.puts ex
+ $stderr.puts ex.backtrace
+ testcase_ok = false
+ end
+
+ begin
+ Dir.chdir '..'
+ rescue Exception => ex
+ $stderr.puts "ERROR: failed to exit test env"
+ $stderr.puts ex
+ $stderr.puts ex.backtrace
+ exit! 1
+ end
+
+ unless system "rm -Rf #{TESTDIR_NAME}"
+ $stderr.puts "ERROR: failed to clear test directory"
+ exit! 1
+ end
+
+ if testcase_ok
+ puts "OK"
+ else
+ exit! 1
+ end
+end
+
+# Like testenv but skips the test if not running as root
+def root_testenv(bindfs_args, &block)
+ if Process.uid == 0
+ testenv(bindfs_args, &block)
+ else
+ puts "--- #{bindfs_args} ---"
+ puts "[ #{bindfs_args} ]"
+ puts "SKIP (requires root)"
+ end
+end
+
+def umount_cmd
+ if `which fusermount`.strip.empty?
+ then 'umount'
+ else 'fusermount -uz'
+ end
+end
+
+def assert
+ raise Exception.new('test failed') unless yield
+end
+
+def assert_exception(ex)
+ begin
+ yield
+ rescue ex
+ return
+ end
+ raise Exception.new('expected exception ' + ex.to_s)
+end
+
+
diff --git a/tests/test_bindfs.rb b/tests/test_bindfs.rb
new file mode 100755
index 0000000..8d5b18f
--- /dev/null
+++ b/tests/test_bindfs.rb
@@ -0,0 +1,206 @@
+#!/usr/bin/env ruby
+#
+# Copyright 2006,2007,2008,2009,2010 Martin Pärtel <martin.partel@gmail.com>
+#
+# This file is part of bindfs.
+#
+# bindfs 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.
+#
+# bindfs 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 bindfs. If not, see <http://www.gnu.org/licenses/>.
+#
+
+require 'common.rb'
+
+include Errno
+
+# FileUtils.chown turned out to be quite buggy in Ruby 1.8.7,
+# so we'll use File.chown instead.
+def chown(user, group, list)
+ user = Etc.getpwnam(user).uid if user.is_a? String
+ group = Etc.getgrnam(group).gid if group.is_a? String
+
+ list = [list] unless list.is_a? Array
+ for file in list
+ File.chown(user, group, file)
+ end
+end
+
+# Treat parameters as test names and run only those
+$only_these_tests = ARGV unless ARGV.empty?
+
+# Some useful shorthands
+$nobody_uid = nobody_uid = Etc.getpwnam('nobody').uid
+$nogroup_gid = nogroup_gid = Etc.getgrnam('nogroup').gid
+
+
+testenv("") do
+ assert { File.basename(pwd) == TESTDIR_NAME }
+end
+
+testenv("-u nobody -g nogroup") do
+ touch('src/file')
+
+ assert { File.stat('mnt/file').uid == nobody_uid }
+ assert { File.stat('mnt/file').gid == nogroup_gid }
+end
+
+testenv("-p 0600:u+D") do
+ touch('src/file')
+ chmod(0777, 'src/file')
+
+ assert { File.stat('mnt/file').mode & 0777 == 0600 }
+end
+
+testenv("--chmod-deny") do
+ touch('src/file')
+
+ assert_exception(EPERM) { chmod(0777, 'mnt/file') }
+end
+
+testenv("-u nobody -m #{Process.uid} -p 0600,u+D") do
+ touch('src/file')
+
+ assert { File.stat('mnt/file').uid == Process.uid }
+end
+
+testenv("--create-with-perms=og=r:ogd+x") do
+ touch('src/file')
+ mkdir('src/dir')
+
+ assert { File.stat('mnt/file').mode & 0077 == 0044 }
+ assert { File.stat('mnt/dir').mode & 0077 == 0055 }
+end
+
+testenv("--ctime-from-mtime") do
+ sf = 'src/file'
+ mf = 'mnt/file'
+
+ touch(sf)
+ sleep(1.1)
+ chmod(0777, mf)
+
+ assert { File.stat(mf).ctime == File.stat(mf).mtime }
+ assert { File.stat(sf).ctime > File.stat(sf).mtime }
+
+end
+
+# Define expectation for changing [uid, gid, both]
+# for each combination of chown/chgrp flags.
+chown_chgrp_test_cases = {
+ :chown_normal => {
+ :chgrp_normal => [:uid, :gid, :both],
+ :chgrp_ignore => [:uid, nil, :uid],
+ :chgrp_deny => [:uid, EPERM, EPERM]
+ },
+ :chown_ignore => {
+ :chgrp_normal => [nil, :gid, :gid],
+ :chgrp_ignore => [nil, nil, nil],
+ :chgrp_deny => [nil, EPERM, EPERM]
+ },
+ :chown_deny => {
+ :chgrp_normal => [EPERM, :gid, EPERM],
+ :chgrp_ignore => [EPERM, nil, EPERM],
+ :chgrp_deny => [EPERM, EPERM, EPERM]
+ }
+}
+
+def run_chown_chgrp_test_case(chown_flag, chgrp_flag, expectations)
+ flags = [chown_flag, chgrp_flag].map do |flag|
+ '--' + flag.to_s.sub('_', '-')
+ end.join ' '
+
+ srcfile = 'src/file'
+ mntfile = 'mnt/file'
+ tests = [
+ lambda { chown('nobody', nil, mntfile) },
+ lambda { chown(nil, 'nogroup', mntfile) },
+ lambda { chown('nobody', 'nogroup', mntfile) }
+ ]
+
+ for testcase, expect in tests.zip expectations
+ root_testenv(flags) do
+ touch(srcfile)
+ if expect.respond_to? :exception
+ assert_exception(expect) { testcase.call }
+ else
+ testcase.call
+ uid = File.stat(srcfile).uid
+ gid = File.stat(srcfile).gid
+
+ case expect
+ when :uid
+ assert { uid == $nobody_uid }
+ assert { gid != $nogroup_gid }
+ when :gid
+ assert { uid != $nobody_uid }
+ assert { gid == $nogroup_gid }
+ when :both
+ assert { uid == $nobody_uid }
+ assert { gid == $nogroup_gid }
+ when nil
+ assert { uid != $nobody_uid }
+ assert { gid != $nogroup_gid }
+ end
+ end
+ end
+ end
+end
+
+chown_chgrp_test_cases.each do |chown_flag, more|
+ more.each do |chgrp_flag, expectations|
+ run_chown_chgrp_test_case(chown_flag, chgrp_flag, expectations)
+ end
+end
+
+root_testenv("--chown-deny") do
+ touch('src/file')
+
+ assert_exception(EPERM) { chown('nobody', nil, 'mnt/file') }
+ assert_exception(EPERM) { chown('nobody', 'nogroup', 'mnt/file') }
+ chown(nil, 'nogroup', 'mnt/file')
+end
+
+testenv("--chmod-allow-x --chmod-ignore") do
+ touch('src/file')
+
+ chmod(01700, 'src/file') # sticky bit set
+
+ chmod(00077, 'mnt/file') # should change x bits; should not unset sticky bit
+ assert { File.stat('src/file').mode & 07777 == 01611 }
+
+
+ mkdir('src/dir')
+ chmod(0700, 'src/dir')
+ chmod(0077, 'mnt/dir') # bits on dir should not change
+ assert { File.stat('src/dir').mode & 0777 == 0700 }
+end
+
+testenv("--chmod-deny --chmod-allow-x") do
+ touch('src/file')
+
+ chmod(0700, 'src/file')
+
+ chmod(0700, 'mnt/file') # no-op chmod should work
+
+ assert_exception(EPERM) { chmod(0777, 'mnt/file') }
+ assert_exception(EPERM) { chmod(0000, 'mnt/file') }
+ assert_exception(EPERM) { chmod(01700, 'mnt/file') } # sticky bit
+
+ chmod(0611, 'mnt/file') # chmod that only changes x-bits should work
+ assert { File.stat('src/file').mode & 07777 == 00611 }
+
+
+ mkdir('src/dir')
+ chmod(0700, 'src/dir')
+ assert_exception(EPERM) { chmod(0700, 'mnt/dir') } # chmod on dir should not work
+end
+
diff --git a/tests/test_concurrent.rb b/tests/test_concurrent.rb
new file mode 100755
index 0000000..f07c031
--- /dev/null
+++ b/tests/test_concurrent.rb
@@ -0,0 +1,45 @@
+#!/usr/bin/env ruby
+#
+# Copyright 2006,2007,2008,2009,2010 Martin Pärtel <martin.partel@gmail.com>
+#
+# This file is part of bindfs.
+#
+# bindfs 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.
+#
+# bindfs 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 bindfs. If not, see <http://www.gnu.org/licenses/>.
+#
+require 'common.rb'
+
+raise "Please run this as root" unless Process.uid == 0
+
+raise "Give two users as parameters" unless ARGV.length == 2
+
+user1 = ARGV[0]
+user2 = ARGV[1]
+
+raise "Give two _different_ users as parameters" unless user1 != user2
+
+testenv "--mirror=#{user1},#{user2}" do
+ touch('src/file')
+
+ count = 0
+ 10.times do |i|
+ out1 = `su #{user1} -c "stat --format=%U mnt/file"`
+ out2 = `su #{user2} -c "stat --format=%U mnt/file"`
+
+ out1.strip!
+ out2.strip!
+ puts "#{i+1}: #{out1} #{out2}"
+ raise "FAIL: #{user1} saw #{out1} on iter #{i}" unless out1 == user1
+ raise "FAIL: #{user2} saw #{out2} on iter #{i}" unless out2 == user2
+ end
+end