Module: FFI::Libfuse::TestHelper

Defined in:
lib/ffi/libfuse/test_helper.rb

Overview

Can be included test classes to assist with running/debugging filesystems

Instance Method Summary collapse

Instance Method Details

#mac_fuse?Boolean

Returns:

  • (Boolean)


103
104
105
# File 'lib/ffi/libfuse/test_helper.rb', line 103

def mac_fuse?
  FFI::Platform::IS_MAC
end

#mounted?(mnt, _filesystem = '.*') ⇒ Boolean

Returns:

  • (Boolean)


80
81
82
83
84
# File 'lib/ffi/libfuse/test_helper.rb', line 80

def mounted?(mnt, _filesystem = '.*')
  type, prefix = mac_fuse? ? %w[macfuse /private] : %w[fuse]
  mounts = Sys::Filesystem.mounts.select { |m| m.mount_type == type }
  mounts.detect { |m| m.mount_point == "#{prefix}#{mnt}" }
end

#run_filesystem(filesystem, *args, env: {}) {|mnt| ... } ⇒ Array

Note:

if the filesystem is configured to daemonize then no output will be captured

Runs a filesystem in a separate process

Parameters:

  • filesystem (String)

    path to filesystem executable

  • args (Array<String>)

    to pass the filesystem

  • env (Hash<String,String>) (defaults to: {})

    environment to run the filesystem under

Yields:

  • (mnt)

    caller can execute and test file operations using mnt and ruby File/Dir etc

Yield Parameters:

  • mnt (String)

    the temporary direct used as the mount point

Returns:

  • (Array)

    stdout, stderr, exit code as captured by Open3.capture3

Raises:

  • (Error)

    if unexpected state is found during operations



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/ffi/libfuse/test_helper.rb', line 55

def run_filesystem(filesystem, *args, env: {})
  fsname = File.basename(filesystem)

  t, err = safe_fuse do |mnt|
    t = Thread.new { open3_filesystem(args, env, filesystem, fsname, mnt) }
    sleep 1

    begin
      raise Error, "#{fsname} not mounted at #{mnt}" if block_given? && !mounted?(mnt, fsname)

      yield mnt if block_given?
      [t, nil]
    rescue Minitest::Assertion, StandardError => e
      [t, e]
    end
  end

  o, e, s = t.value
  return [o, e, s.exitstatus] unless err

  warn "Errors\n#{e}" unless e.empty?
  warn "Output\n#{o}" unless o.empty?
  raise err
end

#safe_fuseObject



94
95
96
97
98
99
100
101
# File 'lib/ffi/libfuse/test_helper.rb', line 94

def safe_fuse
  Dir.mktmpdir('ffi-libfuse-spec') do |mountpoint|
    yield mountpoint
  ensure
    # Attempt to force unmount.
    unmount(mountpoint) if mounted?(mountpoint)
  end
end

#unmount(mnt) ⇒ Object



86
87
88
89
90
91
92
# File 'lib/ffi/libfuse/test_helper.rb', line 86

def unmount(mnt)
  if mac_fuse?
    system("diskutil unmount force #{mnt} >/dev/null 2>&1")
  else
    system("fusermount#{FUSE_MAJOR_VERSION == 3 ? '3' : ''} -zu #{mnt} >/dev/null 2>&1")
  end
end

#with_fuse(operations, *args, **options) {|mnt| ... } ⇒ void

This method returns an undefined value.

Runs the fuse loop on a pre configured fuse filesystem

Parameters:

Yields:

  • (mnt)

    caller can execute and test file operations using mnt and ruby File/Dir etc the block is run in a forked process and is successful unless an exception is raised

Yield Parameters:

  • mnt (String)

    the temporary directory used as the mount point

Raises:

  • (Error)

    if unexpected state is found during operations



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/ffi/libfuse/test_helper.rb', line 22

def with_fuse(operations, *args, **options)
  raise ArgumentError, 'Needs block' unless block_given?

  # ignore MacOS special files
  args << '-onoappledouble,noapplexattr' if mac_fuse?
  safe_fuse do |mnt|
    # Start the fork before loading fuse (for MacOS)
    fpid = Process.fork do
      sleep 2.5 # Give fuse a chance to start
      yield mnt
    end

    run_fuse(mnt, *args, operations: operations, **options) do
      # TODO: Work out why waitpid2 hangs on mac unless the process has already finished
      sleep 10 if mac_fuse?

      _pid, block_status = Process.waitpid2(fpid)
      block_exit = block_status.exitstatus
      raise FFI::Libfuse::Error, "forked file operations failed with #{block_exit}" unless block_exit.zero?
    end
  end
end