1 // Copyright 2017 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #pragma once
6 
7 #include <stdbool.h>
8 #include <stdint.h>
9 #include <stdio.h>
10 #include <string.h>
11 
12 #include <fs-management/mount.h>
13 #include <unittest/unittest.h>
14 #include <zircon/compiler.h>
15 #include <zircon/device/block.h>
16 
17 __BEGIN_CDECLS;
18 
19 typedef struct fs_info {
20     const char* name;
21     bool (*should_test)(void);
22     int (*mkfs)(const char* disk_path);
23     int (*mount)(const char* disk_path, const char* mount_path);
24     int (*unmount)(const char* mount_path);
25     int (*fsck)(const char* mount_path);
26     bool can_be_mounted;
27     bool can_mount_sub_filesystems;
28     bool supports_hardlinks;
29     bool supports_watchers;
30     bool supports_create_by_vmo;
31     bool supports_mmap;
32     bool supports_resize;
33     int64_t nsec_granularity;
34 } fs_info_t;
35 
36 // Path to mounted filesystem currently being tested
37 extern const char* kTmpfsPath;
38 extern const char* kMountPath;
39 
40 // Path to the mounted filesystem's backing store (if it exists)
41 extern char test_disk_path[];
42 
43 // Path to the mounted filesystems's backing ramdisk (if it exists).
44 extern char* ramdisk_path;
45 
46 // Identify if a real disk is being tested instead of a ramdisk.
47 extern bool use_real_disk;
48 
49 // The disk's cached info.
50 extern block_info_t test_disk_info;
51 
52 // A filter of the filesystems; indicates which one should be tested.
53 extern const char* filesystem_name_filter;
54 
55 // Current filesystem's info
56 extern fs_info_t* test_info;
57 
58 extern const fsck_options_t test_fsck_options;
59 
60 #define NUM_FILESYSTEMS 3
61 extern fs_info_t FILESYSTEMS[NUM_FILESYSTEMS];
62 
63 typedef enum fs_test_type {
64     // The partition may appear as any generic block device
65     FS_TEST_NORMAL,
66     // The partition should appear on top of a resizable
67     // FVM device
68     FS_TEST_FVM,
69 } fs_test_type_t;
70 
71 #define TEST_BLOCK_COUNT_DEFAULT (1LLU << 17)
72 #define TEST_BLOCK_SIZE_DEFAULT (1LLU << 9)
73 #define TEST_FVM_SLICE_SIZE_DEFAULT (8 * (1 << 20))
74 
75 typedef struct test_disk {
76     uint64_t block_count;
77     uint64_t block_size;
78     // Only applicable for FVM tests.
79     uint64_t slice_size;
80 } test_disk_t;
81 
82 extern const test_disk_t default_test_disk;
83 
84 void setup_fs_test(test_disk_t disk, fs_test_type_t test_class);
85 void teardown_fs_test(fs_test_type_t test_class);
86 
can_execute_test(const fs_info_t * info,const test_disk_t * requested_disk,fs_test_type_t t)87 inline bool can_execute_test(const fs_info_t* info, const test_disk_t* requested_disk,
88                              fs_test_type_t t) {
89 
90     uint64_t requested_size = requested_disk->block_count * requested_disk->block_size;
91     uint64_t real_size = test_disk_info.block_count * test_disk_info.block_size;
92 
93     if (use_real_disk && (real_size < requested_size)) {
94         printf("Disk too small (is %zu bytes, must be %zu bytes): \n",
95                real_size, requested_size);
96         return false;
97     }
98 
99     switch (t) {
100     case FS_TEST_NORMAL:
101         return info->should_test();
102     case FS_TEST_FVM:
103         return info->should_test() && info->supports_resize;
104     default:
105         printf("Unknown filesystem type: ");
106         return false;
107     }
108 }
109 
110 // As a small optimization, avoid even creating a ramdisk
111 // for filesystem tests when "utest_test_type" is not at
112 // LEAST size "medium". This avoids the overhead of creating
113 // a ramdisk when running small tests.
114 #define BEGIN_FS_TEST_CASE(case_name, disk, fs_type, fs_name, info)  \
115     BEGIN_TEST_CASE(case_name##_##fs_name)                           \
116     if (utest_test_type & ~TEST_SMALL) {                             \
117         test_info = info;                                            \
118         if (can_execute_test(test_info, &disk, fs_type)) {           \
119             setup_fs_test(disk, fs_type);
120 
121 #define END_FS_TEST_CASE(case_name, fs_type, fs_name) \
122             teardown_fs_test(fs_type);                \
123         } else {                                      \
124             printf("Filesystem not tested\n");        \
125         }                                             \
126     }                                                 \
127     END_TEST_CASE(case_name##_##fs_name)
128 
129 #define FS_TEST_CASE(case_name, disk, CASE_TESTS, test_type, fs_type, index)     \
130     BEGIN_FS_TEST_CASE(case_name, disk, test_type, fs_type, &FILESYSTEMS[index]) \
131     CASE_TESTS                                                                   \
132     END_FS_TEST_CASE(case_name, test_type, fs_type)
133 
134 #define RUN_FOR_ALL_FILESYSTEMS_TYPE(case_name, disk, test_type, CASE_TESTS)     \
135     FS_TEST_CASE(case_name, disk, CASE_TESTS, test_type, memfs, 0)  \
136     FS_TEST_CASE(case_name, disk, CASE_TESTS, test_type, minfs, 1)  \
137     FS_TEST_CASE(case_name, disk, CASE_TESTS, test_type, thinfs, 2)
138 
139 #define RUN_FOR_ALL_FILESYSTEMS_SIZE(case_name, disk, CASE_TESTS)          \
140     FS_TEST_CASE(case_name, disk, CASE_TESTS, FS_TEST_NORMAL, memfs, 0)    \
141     FS_TEST_CASE(case_name, disk, CASE_TESTS, FS_TEST_NORMAL, minfs, 1)    \
142     FS_TEST_CASE(case_name##_fvm, disk, CASE_TESTS, FS_TEST_FVM, minfs, 1) \
143     FS_TEST_CASE(case_name, disk, CASE_TESTS, FS_TEST_NORMAL, thinfs, 2)
144 
145 #define RUN_FOR_ALL_FILESYSTEMS(case_name, CASE_TESTS)                     \
146     RUN_FOR_ALL_FILESYSTEMS_SIZE(case_name, default_test_disk, CASE_TESTS)
147 
148 __END_CDECLS;
149