1package disk
2
3import (
4 "testing"
5)
6
7// TestNewBtrfsSubvolume tests BtrfsSubvolume creation
8func TestNewBtrfsSubvolume(t *testing.T) {
9 tests := []struct {
10 name string
11 svName string
12 mountPoint string
13 shouldErr bool
14 errMsg string
15 }{
16 {"valid root subvolume", "@", "/", false, ""},
17 {"valid home subvolume", "@home", "/home", false, ""},
18 {"valid snapshots subvolume", "@snapshots", "/.snapshots", false, ""},
19 {"valid cache subvolume", "@cache", "/var/cache", false, ""},
20 {"valid log subvolume", "@log", "/var/log", false, ""},
21 {"valid with underscore", "@my_data", "/data", false, ""},
22 {"valid with hyphen", "@my-data", "/data", false, ""},
23 {"valid with numbers", "@data123", "/data", false, ""},
24 {"empty name", "", "/", true, "name cannot be empty"},
25 {"no @ prefix", "home", "/home", true, "must start with @"},
26 {"space in name", "@ home", "/home", true, "invalid character"},
27 {"invalid char in name", "@home!", "/home", true, "invalid character"},
28 {"mount point no slash", "@home", "home", true, "must start with /"},
29 {"empty mount point", "@tmp", "", false, ""},
30 {"name too long", "@" + string(make([]byte, 64)), "/", true, "too long"},
31 }
32
33 for _, tt := range tests {
34 t.Run(tt.name, func(t *testing.T) {
35 sv, err := NewBtrfsSubvolume(tt.svName, tt.mountPoint)
36
37 if (err != nil) != tt.shouldErr {
38 t.Errorf("got error %v, expected error=%v", err, tt.shouldErr)
39 }
40
41 if tt.shouldErr && err != nil && tt.errMsg != "" {
42 if !contains(err.Error(), tt.errMsg) {
43 t.Errorf("error message %q does not contain %q", err.Error(), tt.errMsg)
44 }
45 }
46
47 if !tt.shouldErr && sv == nil {
48 t.Error("expected non-nil subvolume, got nil")
49 }
50
51 if !tt.shouldErr && sv != nil {
52 if sv.Name() != tt.svName {
53 t.Errorf("got name %q, want %q", sv.Name(), tt.svName)
54 }
55 if sv.MountPoint() != tt.mountPoint {
56 t.Errorf("got mount point %q, want %q", sv.MountPoint(), tt.mountPoint)
57 }
58 }
59 })
60 }
61}
62
63// TestBtrfsSubvolumeEquals tests subvolume equality
64func TestBtrfsSubvolumeEquals(t *testing.T) {
65 sv1, _ := NewBtrfsSubvolume("@", "/")
66 sv2, _ := NewBtrfsSubvolume("@", "/")
67 sv3, _ := NewBtrfsSubvolume("@home", "/home")
68
69 if !sv1.Equals(sv2) {
70 t.Error("identical subvolumes should be equal")
71 }
72
73 if sv1.Equals(sv3) {
74 t.Error("different subvolumes should not be equal")
75 }
76
77 if sv1.Equals(nil) {
78 t.Error("subvolume should not equal nil")
79 }
80}
81
82// TestValidateSubvolumeName tests subvolume name validation
83func TestValidateSubvolumeName(t *testing.T) {
84 tests := []struct {
85 name string
86 svName string
87 shouldErr bool
88 }{
89 {"valid @", "@", false},
90 {"valid @home", "@home", false},
91 {"valid @snapshots", "@snapshots", false},
92 {"valid @cache", "@cache", false},
93 {"valid @log", "@log", false},
94 {"valid with underscore", "@my_data", false},
95 {"valid with hyphen", "@my-data", false},
96 {"valid with numbers", "@data123", false},
97 {"empty", "", true},
98 {"no @ prefix", "home", true},
99 {"space in name", "@ home", true},
100 {"invalid char !", "@home!", true},
101 {"invalid char #", "@home#test", true},
102 {"too long", "@" + string(make([]byte, 64)), true},
103 }
104
105 for _, tt := range tests {
106 t.Run(tt.name, func(t *testing.T) {
107 err := ValidateSubvolumeName(tt.svName)
108 if (err != nil) != tt.shouldErr {
109 t.Errorf("got error %v, expected error=%v", err, tt.shouldErr)
110 }
111 })
112 }
113}
114
115// TestNewBtrfsLayout tests BtrfsLayout creation
116func TestNewBtrfsLayout(t *testing.T) {
117 layout := NewBtrfsLayout()
118 if layout == nil {
119 t.Fatal("expected non-nil layout, got nil")
120 }
121
122 if layout.SubvolumeCount() != 0 {
123 t.Errorf("new layout should have 0 subvolumes, got %d", layout.SubvolumeCount())
124 }
125}
126
127// TestNewStandardBtrfsLayout tests standard layout creation
128func TestNewStandardBtrfsLayout(t *testing.T) {
129 layout, err := NewStandardBtrfsLayout()
130 if err != nil {
131 t.Fatalf("failed to create standard layout: %v", err)
132 }
133
134 if layout.SubvolumeCount() != 2 {
135 t.Errorf("standard layout should have 2 subvolumes, got %d", layout.SubvolumeCount())
136 }
137
138 // Check for @ subvolume
139 root := layout.GetRootSubvolume()
140 if root == nil {
141 t.Error("standard layout should have root (@) subvolume")
142 } else {
143 if root.Name() != "@" {
144 t.Errorf("root subvolume should be named @, got %q", root.Name())
145 }
146 if root.MountPoint() != "/" {
147 t.Errorf("root subvolume should mount at /, got %q", root.MountPoint())
148 }
149 }
150
151 // Check for @home subvolume
152 home := layout.FindSubvolumeByName("@home")
153 if home == nil {
154 t.Error("standard layout should have @home subvolume")
155 } else {
156 if home.MountPoint() != "/home" {
157 t.Errorf("@home should mount at /home, got %q", home.MountPoint())
158 }
159 }
160}
161
162// TestBtrfsLayoutAddSubvolume tests adding subvolumes to layout
163func TestBtrfsLayoutAddSubvolume(t *testing.T) {
164 layout := NewBtrfsLayout()
165
166 // Add root subvolume
167 root, _ := NewBtrfsSubvolume("@", "/")
168 err := layout.AddSubvolume(root)
169 if err != nil {
170 t.Fatalf("failed to add root subvolume: %v", err)
171 }
172
173 if layout.SubvolumeCount() != 1 {
174 t.Errorf("expected 1 subvolume, got %d", layout.SubvolumeCount())
175 }
176
177 // Try adding duplicate name
178 dup, _ := NewBtrfsSubvolume("@", "/other")
179 err = layout.AddSubvolume(dup)
180 if err == nil {
181 t.Error("should not allow duplicate subvolume names")
182 }
183
184 // Try adding duplicate mount point
185 other, _ := NewBtrfsSubvolume("@other", "/")
186 err = layout.AddSubvolume(other)
187 if err == nil {
188 t.Error("should not allow duplicate mount points")
189 }
190
191 // Add valid @home
192 home, _ := NewBtrfsSubvolume("@home", "/home")
193 err = layout.AddSubvolume(home)
194 if err != nil {
195 t.Fatalf("failed to add @home subvolume: %v", err)
196 }
197
198 if layout.SubvolumeCount() != 2 {
199 t.Errorf("expected 2 subvolumes, got %d", layout.SubvolumeCount())
200 }
201
202 // Try adding nil subvolume
203 err = layout.AddSubvolume(nil)
204 if err == nil {
205 t.Error("should not allow nil subvolume")
206 }
207}
208
209// TestBtrfsLayoutFindSubvolume tests finding subvolumes
210func TestBtrfsLayoutFindSubvolume(t *testing.T) {
211 layout, _ := NewStandardBtrfsLayout()
212
213 // Find by name
214 sv := layout.FindSubvolumeByName("@")
215 if sv == nil {
216 t.Error("should find @ subvolume")
217 }
218
219 sv = layout.FindSubvolumeByName("@home")
220 if sv == nil {
221 t.Error("should find @home subvolume")
222 }
223
224 sv = layout.FindSubvolumeByName("@nonexistent")
225 if sv != nil {
226 t.Error("should not find nonexistent subvolume")
227 }
228
229 // Find by mount point
230 sv = layout.FindSubvolumeByMountPoint("/")
231 if sv == nil {
232 t.Error("should find root subvolume by mount point")
233 }
234
235 sv = layout.FindSubvolumeByMountPoint("/home")
236 if sv == nil {
237 t.Error("should find @home by mount point")
238 }
239
240 sv = layout.FindSubvolumeByMountPoint("/nonexistent")
241 if sv != nil {
242 t.Error("should not find nonexistent mount point")
243 }
244}
245
246// TestBtrfsLayoutHasRootSubvolume tests root subvolume detection
247func TestBtrfsLayoutHasRootSubvolume(t *testing.T) {
248 layout := NewBtrfsLayout()
249
250 if layout.HasRootSubvolume() {
251 t.Error("empty layout should not have root subvolume")
252 }
253
254 root, _ := NewBtrfsSubvolume("@", "/")
255 if err := layout.AddSubvolume(root); err != nil {
256 t.Fatalf("expected no error, got %v", err)
257 }
258
259 if !layout.HasRootSubvolume() {
260 t.Error("layout with @ subvolume should have root subvolume")
261 }
262}
263
264// TestBtrfsLayoutValidateLayout tests layout validation
265func TestBtrfsLayoutValidateLayout(t *testing.T) {
266 // Empty layout should fail
267 layout := NewBtrfsLayout()
268 err := layout.ValidateLayout()
269 if err == nil {
270 t.Error("empty layout should fail validation")
271 }
272
273 // Layout without root should fail
274 home, _ := NewBtrfsSubvolume("@home", "/home")
275 if err := layout.AddSubvolume(home); err != nil {
276 t.Fatalf("expected no error, got %v", err)
277 }
278 err = layout.ValidateLayout()
279 if err == nil {
280 t.Error("layout without root subvolume should fail validation")
281 }
282
283 // Layout with wrong root name should fail
284 wrongRoot, _ := NewBtrfsSubvolume("@root", "/")
285 layout2 := NewBtrfsLayout()
286 if err := layout2.AddSubvolume(wrongRoot); err != nil {
287 t.Fatalf("expected no error, got %v", err)
288 }
289 err = layout2.ValidateLayout()
290 if err == nil {
291 t.Error("layout with wrong root name should fail validation")
292 }
293
294 // Standard layout should pass
295 standardLayout, _ := NewStandardBtrfsLayout()
296 err = standardLayout.ValidateLayout()
297 if err != nil {
298 t.Errorf("standard layout should pass validation: %v", err)
299 }
300}
301
302// TestBtrfsLayoutSubvolumes tests getting subvolumes list
303func TestBtrfsLayoutSubvolumes(t *testing.T) {
304 layout, _ := NewStandardBtrfsLayout()
305 subvolumes := layout.Subvolumes()
306
307 if len(subvolumes) != 2 {
308 t.Errorf("expected 2 subvolumes, got %d", len(subvolumes))
309 }
310
311 // Verify immutability - modifying returned slice shouldn't affect layout
312 subvolumes[0] = nil
313 if layout.SubvolumeCount() != 2 {
314 t.Error("modifying returned subvolumes should not affect layout")
315 }
316}
317
318// TestBtrfsSubvolumeString tests String method
319func TestBtrfsSubvolumeString(t *testing.T) {
320 sv1, _ := NewBtrfsSubvolume("@", "/")
321 str := sv1.String()
322 if !contains(str, "@") || !contains(str, "/") {
323 t.Errorf("String() output should contain name and mount point: %s", str)
324 }
325
326 sv2, _ := NewBtrfsSubvolume("@temp", "")
327 str = sv2.String()
328 if !contains(str, "@temp") {
329 t.Errorf("String() output should contain name: %s", str)
330 }
331}
332
333// TestBtrfsLayoutString tests String method
334func TestBtrfsLayoutString(t *testing.T) {
335 layout, _ := NewStandardBtrfsLayout()
336 str := layout.String()
337 if !contains(str, "BtrfsLayout") {
338 t.Errorf("String() output should contain BtrfsLayout: %s", str)
339 }
340}
341
342// Helper function
343func contains(s, substr string) bool {
344 return len(s) >= len(substr) && (s == substr || len(s) > len(substr) && containsHelper(s, substr))
345}
346
347func containsHelper(s, substr string) bool {
348 for i := 0; i <= len(s)-len(substr); i++ {
349 if s[i:i+len(substr)] == substr {
350 return true
351 }
352 }
353 return false
354}