github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/test/fuse/README.md (about)

     1  # gVisor FUSE Test Suite
     2  
     3  This is an integration test suite for fuse(4) filesystem. It runs under gVisor
     4  sandbox container with VFS2 and FUSE function enabled.
     5  
     6  This document describes the framework of FUSE integration test, how to use it,
     7  and the guidelines that should be followed when adding new testing features.
     8  
     9  ## Integration Test Framework
    10  
    11  By inheriting the `FuseTest` class defined in `linux/fuse_base.h`, every test
    12  fixture can run in an environment with `mount_point_` mounted by a fake FUSE
    13  server. It creates a `socketpair(2)` to send and receive control commands and
    14  data between the client and the server. Because the FUSE server runs in the
    15  background thread, gTest cannot catch its assertion failure immediately. Thus,
    16  `TearDown()` function sends command to the FUSE server to check if all gTest
    17  assertion in the server are successful and all requests and preset responses are
    18  consumed.
    19  
    20  ## Communication Diagram
    21  
    22  Diagram below describes how a testing thread communicates with the FUSE server
    23  to achieve integration test.
    24  
    25  For the following diagram, `>` means entering the function, `<` is leaving the
    26  function, and `=` indicates sequentially entering and leaving. Not necessarily
    27  follow exactly the below diagram due to the nature of a multi-threaded system,
    28  however, it is still helpful to know when the client waits for the server to
    29  complete a command and when the server awaits the next instruction.
    30  
    31  ```
    32  |  Client (Testing Thread)            |  Server (FUSE Server Thread)
    33  |                                     |
    34  |  >TEST_F()                          |
    35  |    >SetUp()                         |
    36  |      =MountFuse()                   |
    37  |      >SetUpFuseServer()             |
    38  |        [create communication socket]|
    39  |        =fork()                      |      =fork()
    40  |        [wait server complete]       |
    41  |                                     |      =ServerConsumeFuseInit()
    42  |                                     |      =ServerCompleteWith()
    43  |      <SetUpFuseServer()             |
    44  |    <SetUp()                         |
    45  |    [testing main]                   |
    46  |                                     |      >ServerFuseLoop()
    47  |                                     |        [poll on socket and fd]
    48  |    >SetServerResponse()             |
    49  |      [write data to socket]         |
    50  |      [wait server complete]         |
    51  |                                     |        [socket event occurs]
    52  |                                     |        >ServerHandleCommand()
    53  |                                     |          >ServerReceiveResponse()
    54  |                                     |            [read data from socket]
    55  |                                     |            [save data to memory]
    56  |                                     |          <ServerReceiveResponse()
    57  |                                     |          =ServerCompleteWith()
    58  |    <SetServerResponse()             |
    59  |                                     |        <ServerHandleCommand()
    60  |    >[Do fs operation]               |
    61  |      [wait for fs response]         |
    62  |                                     |        [fd event occurs]
    63  |                                     |        >ServerProcessFuseRequest()
    64  |                                     |          =[read fs request]
    65  |                                     |          =[save fs request to memory]
    66  |                                     |          =[write fs response]
    67  |    <[Do fs operation]               |
    68  |                                     |        <ServerProcessFuseRequest()
    69  |                                     |
    70  |    =[Test fs operation result]      |
    71  |                                     |
    72  |    >GetServerActualRequest()        |
    73  |      [write data to socket]         |
    74  |      [wait data from server]        |
    75  |                                     |        [socket event occurs]
    76  |                                     |        >ServerHandleCommand()
    77  |                                     |          >ServerSendReceivedRequest()
    78  |                                     |            [write data to socket]
    79  |      [read data from socket]        |
    80  |      [wait server complete]         |
    81  |                                     |          <ServerSendReceivedRequest()
    82  |                                     |          =ServerCompleteWith()
    83  |    <GetServerActualRequest()        |
    84  |                                     |        <ServerHandleCommand()
    85  |                                     |
    86  |    =[Test actual request]           |
    87  |                                     |
    88  |    >TearDown()                      |
    89  |      ...                            |
    90  |      >GetServerNumUnsentResponses() |
    91  |        [write data to socket]       |
    92  |        [wait server complete]       |
    93  |                                     |        [socket event arrive]
    94  |                                     |        >ServerHandleCommand()
    95  |                                     |          >ServerSendData()
    96  |                                     |            [write data to socket]
    97  |                                     |          <ServerSendData()
    98  |                                     |          =ServerCompleteWith()
    99  |        [read data from socket]      |
   100  |        [test if all succeeded]      |
   101  |      <GetServerNumUnsentResponses() |
   102  |                                     |        <ServerHandleCommand()
   103  |      =UnmountFuse()                 |
   104  |    <TearDown()                      |
   105  |  <TEST_F()                          |
   106  ```
   107  
   108  ## Running the tests
   109  
   110  Based on syscall tests, FUSE tests generate targets only with vfs2 and fuse
   111  enabled. The corresponding targets end in `_fuse`.
   112  
   113  For example, to run fuse test in `stat_test.cc`:
   114  
   115  ```bash
   116  $ bazel test //test/fuse:stat_test_runsc_ptrace_vfs2_fuse
   117  ```
   118  
   119  Test all targets tagged with fuse:
   120  
   121  ```bash
   122  $ bazel test --test_tag_filters=fuse //test/fuse/...
   123  ```
   124  
   125  ## Writing a new FUSE test
   126  
   127  1.  Add test targets in `BUILD` and `linux/BUILD`.
   128  2.  Inherit your test from `FuseTest` base class. It allows you to:
   129      -   Fork a fake FUSE server in background during each test setup.
   130      -   Create a pair of sockets for communication and provide utility
   131          functions.
   132      -   Stop FUSE server and check if error occurs in it after test completes.
   133  3.  Build the expected opcode-response pairs of your FUSE operation.
   134  4.  Call `SetServerResponse()` to preset the next expected opcode and response.
   135  5.  Do real filesystem operations (FUSE is mounted at `mount_point_`).
   136  6.  Check FUSE response and/or errors.
   137  7.  Retrieve FUSE request by `GetServerActualRequest()`.
   138  8.  Check if the request is as expected.
   139  
   140  A few customized matchers used in syscalls test are encouraged to test the
   141  outcome of filesystem operations. Such as:
   142  
   143  ```cc
   144  SyscallSucceeds()
   145  SyscallSucceedsWithValue(...)
   146  SyscallFails()
   147  SyscallFailsWithErrno(...)
   148  ```
   149  
   150  Please refer to [test/syscalls/README.md](../syscalls/README.md) for further
   151  details.
   152  
   153  ## Writing a new FuseTestCmd
   154  
   155  A `FuseTestCmd` is a control protocol used in the communication between the
   156  testing thread and the FUSE server. Such commands are sent from the testing
   157  thread to the FUSE server to set up, control, or inspect the behavior of the
   158  FUSE server in response to a sequence of FUSE requests.
   159  
   160  The lifecycle of a command contains following steps:
   161  
   162  1.  The testing thread sends a `FuseTestCmd` via socket and waits for
   163      completion.
   164  2.  The FUSE server receives the command and does corresponding action.
   165  3.  (Optional) The testing thread reads data from socket.
   166  4.  The FUSE server sends a success indicator via socket after processing.
   167  5.  The testing thread gets the success signal and continues testing.
   168  
   169  The success indicator, i.e. `WaitServerComplete()`, is crucial at the end of
   170  each `FuseTestCmd` sent from the testing thread. Because we don't want to begin
   171  filesystem operation if the requests have not been completely set up. Also, to
   172  test FUSE interactions in a sequential manner, concurrent requests are not
   173  supported now.
   174  
   175  To add a new `FuseTestCmd`, one must comply with following format:
   176  
   177  1.  Add a new `FuseTestCmd` enum class item defined in `linux/fuse_base.h`
   178  2.  Add a `SetServerXXX()` or `GetServerXXX()` public function in `FuseTest`.
   179      This is how the testing thread will call to send control message. Define how
   180      many bytes you want to send along with the command and what you will expect
   181      to receive. Finally it should block and wait for a success indicator from
   182      the FUSE server.
   183  3.  Add a handler logic in the switch condition of `ServerHandleCommand()`. Use
   184      `ServerSendData()` or declare a new private function such as
   185      `ServerReceiveXXX()` or `ServerSendXXX()`. It is mandatory to set it private
   186      since only the FUSE server (forked from `FuseTest` base class) can call it.
   187      This is the server part of the specific `FuseTestCmd` and the format of the
   188      data should be consistent with what the client expects in the previous step.