| 1 | |
package org.webslinger.commons.vfs.tests; |
| 2 | |
|
| 3 | |
import org.webslinger.httpunit.WebslingerTestCase; |
| 4 | |
|
| 5 | |
import java.io.EOFException; |
| 6 | |
import java.io.File; |
| 7 | |
import java.io.InputStream; |
| 8 | |
import java.io.IOException; |
| 9 | |
import java.io.OutputStream; |
| 10 | |
import java.util.Collection; |
| 11 | |
import java.util.LinkedHashMap; |
| 12 | |
|
| 13 | |
import org.apache.commons.vfs.FileContent; |
| 14 | |
import org.apache.commons.vfs.FileObject; |
| 15 | |
import org.apache.commons.vfs.FileSystem; |
| 16 | |
import org.apache.commons.vfs.FileSystemOptions; |
| 17 | |
import org.apache.commons.vfs.RandomAccessContent; |
| 18 | |
import org.apache.commons.vfs.Selectors; |
| 19 | |
import org.apache.commons.vfs.impl.StandardFileSystemManager; |
| 20 | |
import org.apache.commons.vfs.util.RandomAccessMode; |
| 21 | |
|
| 22 | |
import org.webslinger.containers.TestContainer; |
| 23 | |
import org.webslinger.commons.vfs.VFSUtil; |
| 24 | |
import org.webslinger.commons.vfs.cow.COWConfigBuilder; |
| 25 | |
import org.webslinger.commons.vfs.cow.COWFileSystem; |
| 26 | |
import org.webslinger.commons.vfs.cow.COWFsck; |
| 27 | |
import org.webslinger.commons.vfs.operations.COWStateOperation; |
| 28 | |
import org.webslinger.commons.vfs.operations.SymlinkOperation; |
| 29 | |
import org.webslinger.commons.vfs.tests.COWFileMapping; |
| 30 | |
import org.webslinger.commons.vfs.tests.COWTester; |
| 31 | |
import org.webslinger.commons.vfs.tests.SymlinkTester; |
| 32 | |
import org.webslinger.commons.vfs.tests.TestUtil; |
| 33 | |
import org.webslinger.io.IOUtil; |
| 34 | |
|
| 35 | |
public class TestCow extends TestContainer.TestContainerTestCase { |
| 36 | |
protected StandardFileSystemManager sfsm; |
| 37 | |
protected FileObject root; |
| 38 | |
public TestCow(String name) { |
| 39 | 7 | super(name); |
| 40 | 7 | } |
| 41 | |
|
| 42 | |
protected void setUp() throws Exception { |
| 43 | 7 | super.setUp(); |
| 44 | 7 | FileSystemOptions options = new FileSystemOptions(); |
| 45 | 7 | COWConfigBuilder.getInstance().setCOWEntryFactory(options, new COWFileSystem.DefaultCOWEntryFactory()); |
| 46 | 7 | sfsm = VFSUtil.createStandardFileSystemManager(); |
| 47 | 7 | root = sfsm.createVirtualFileSystem(VFSUtil.toFileObject(sfsm, new File(getTestLocation(), getName()), options)); |
| 48 | 7 | root.delete(Selectors.SELECT_ALL); |
| 49 | 7 | } |
| 50 | |
|
| 51 | |
protected void tearDown() throws Exception { |
| 52 | 7 | root.delete(Selectors.SELECT_ALL); |
| 53 | 7 | super.tearDown(); |
| 54 | 7 | } |
| 55 | |
|
| 56 | |
protected SymlinkOperation getSymlinkOperation(FileObject file) throws Exception { |
| 57 | 0 | return VFSUtil.getOperation(file, SymlinkOperation.class); |
| 58 | |
} |
| 59 | |
|
| 60 | |
protected SymlinkOperation getSymlinkOperation(FileSystem fs, String path) throws Exception { |
| 61 | 0 | return VFSUtil.getOperation(fs, path, SymlinkOperation.class); |
| 62 | |
} |
| 63 | |
|
| 64 | |
public void testCOWDirectory() throws Exception { |
| 65 | 1 | new COWTester( |
| 66 | |
root, |
| 67 | |
"cow", |
| 68 | |
true, |
| 69 | |
new COWFileMapping("view", "layer1", "FileInLayer1"), |
| 70 | |
new COWFileMapping("view", "layer2", "Layer2File"), |
| 71 | |
new COWFileMapping("view", "layer3", "FileForTheThirdLayer") |
| 72 | |
).doTest(); |
| 73 | 1 | } |
| 74 | |
|
| 75 | |
public void testCOWFile() throws Exception { |
| 76 | 1 | new COWTester( |
| 77 | |
root, |
| 78 | |
"cow", |
| 79 | |
false, |
| 80 | |
new COWFileMapping("view/FileInLayer1", "../layer1/FileInLayer1", "FileInLayer1"), |
| 81 | |
new COWFileMapping("view/Layer2File", "../layer2/Layer2File", "Layer2File"), |
| 82 | |
new COWFileMapping("view/FileForTheThirdLayer", "../layer3/FileForTheThirdLayer", "FileForTheThirdLayer") |
| 83 | |
).doTest(); |
| 84 | 1 | } |
| 85 | |
|
| 86 | |
public void testWsvfsDirectory() throws Exception { |
| 87 | 1 | new COWTester( |
| 88 | |
root, |
| 89 | |
"wsvfs", |
| 90 | |
true, |
| 91 | |
new COWFileMapping("view", "layer1", "FileInLayer1"), |
| 92 | |
new COWFileMapping("view", "layer2", "Layer2File"), |
| 93 | |
new COWFileMapping("view", "layer3", "FileForTheThirdLayer") |
| 94 | |
).doTest(); |
| 95 | 1 | } |
| 96 | |
|
| 97 | |
public void testWsvfsFile() throws Exception { |
| 98 | 1 | new COWTester( |
| 99 | |
root, |
| 100 | |
"wsvfs", |
| 101 | |
false, |
| 102 | |
new COWFileMapping("view/FileInLayer1", "../layer1/FileInLayer1", "FileInLayer1"), |
| 103 | |
new COWFileMapping("view/Layer2File", "../layer2/Layer2File", "Layer2File"), |
| 104 | |
new COWFileMapping("view/FileForTheThirdLayer", "../layer3/FileForTheThirdLayer", "FileForTheThirdLayer") |
| 105 | |
).doTest(); |
| 106 | 1 | } |
| 107 | |
|
| 108 | |
public void testSymlinks() throws Exception { |
| 109 | 1 | new SymlinkTester(sfsm.createFileSystem("cow", root)).doTest(); |
| 110 | 1 | } |
| 111 | |
|
| 112 | |
public void testCOWStateReading() throws Exception { |
| 113 | 1 | FileSystem cfs = sfsm.createFileSystem("cow", root).getFileSystem(); |
| 114 | 1 | FileObject cow = cfs.getRoot(); |
| 115 | 1 | VFSUtil.setString( |
| 116 | |
root, |
| 117 | |
".cowstate.xml", |
| 118 | |
"<state>\n" |
| 119 | |
+ " <item name=\"cow\">\n" |
| 120 | |
+ " <base target=\"real/dir1\"/>\n" |
| 121 | |
+ " <base target=\"real/dir2\"/>\n" |
| 122 | |
+ " <base target=\"real/dir3\"/>\n" |
| 123 | |
+ " </item>\n" |
| 124 | |
+ "</state>\n" |
| 125 | |
); |
| 126 | 1 | VFSUtil.setString( |
| 127 | |
root, |
| 128 | |
"cow/.cowstate.xml", |
| 129 | |
"<state>\n" |
| 130 | |
+ " <item name=\"link\" symlink=\"file3\"/>\n" |
| 131 | |
+ " <item name=\"file1\" deleted=\"true\"/>\n" |
| 132 | |
+ " <item name=\"fileN\">\n" |
| 133 | |
+ " <base target=\"file2\"/>\n" |
| 134 | |
+ " </item>\n" |
| 135 | |
+ "</state>\n" |
| 136 | |
); |
| 137 | 1 | VFSUtil.setString(root, "real/dir1/file1", "textual content"); |
| 138 | 1 | VFSUtil.setString(root, "real/dir2/file2", "bits and bytes"); |
| 139 | 1 | VFSUtil.setString(root, "real/dir3/file3", "letters and numbers"); |
| 140 | 1 | VFSUtil.setString(root, "cow/fileX", "on top"); |
| 141 | 1 | VFSUtil.setString(root, "cow/file2", "new content"); |
| 142 | |
|
| 143 | |
|
| 144 | 1 | assertTrue("real/dir1/file1 exists", cow.resolveFile("real/dir1/file1").exists()); |
| 145 | 1 | assertEquals("real/dir1/file1 content", "textual content", VFSUtil.getString(root, "real/dir1/file1")); |
| 146 | 1 | FileObject[] children = cow.resolveFile("real/dir1").getChildren(); |
| 147 | 1 | assertEquals("real/dir1 number of children", 1, children.length); |
| 148 | 1 | assertEquals("real/dir1 single child", "file1", children[0].getName().getBaseName()); |
| 149 | |
|
| 150 | 1 | assertTrue("real/dir2/file2 exists", cow.resolveFile("real/dir2/file2").exists()); |
| 151 | 1 | assertEquals("real/dir2/file2 content", "bits and bytes", VFSUtil.getString(root, "real/dir2/file2")); |
| 152 | 1 | children = cow.resolveFile("real/dir2").getChildren(); |
| 153 | 1 | assertEquals("real-dir2 number of children", 1, children.length); |
| 154 | 1 | assertEquals("real-dir2 single child", "file2", children[0].getName().getBaseName()); |
| 155 | |
|
| 156 | 1 | assertTrue("real/dir3/file3 exists", cow.resolveFile("real/dir3/file3").exists()); |
| 157 | 1 | assertEquals("real/dir3/file3 content", "letters and numbers", VFSUtil.getString(root, "real/dir3/file3")); |
| 158 | 1 | children = cow.resolveFile("real/dir3").getChildren(); |
| 159 | 1 | assertEquals("real/dir3 number of children", 1, children.length); |
| 160 | 1 | assertEquals("real/dir3 single child", "file3", children[0].getName().getBaseName()); |
| 161 | |
|
| 162 | |
|
| 163 | 1 | children = cow.resolveFile("cow").getChildren(); |
| 164 | 1 | assertEquals("top level children count", 5, children.length); |
| 165 | 1 | int matchedBits = 0; |
| 166 | 1 | int matchedCount = 0; |
| 167 | 6 | for (int i = 0; i < children.length; i++) { |
| 168 | 5 | String base = children[i].getName().getBaseName(); |
| 169 | 5 | if (base.equals("file2")) { |
| 170 | 1 | matchedBits |= 1; |
| 171 | 1 | matchedCount++; |
| 172 | 4 | } else if (base.equals("file3")) { |
| 173 | 1 | matchedBits |= 2; |
| 174 | 1 | matchedCount++; |
| 175 | 3 | } else if (base.equals("fileN")) { |
| 176 | 1 | matchedBits |= 4; |
| 177 | 1 | matchedCount++; |
| 178 | 2 | } else if (base.equals("fileX")) { |
| 179 | 1 | matchedBits |= 8; |
| 180 | 1 | matchedCount++; |
| 181 | 1 | } else if (base.equals("link")) { |
| 182 | 1 | matchedBits |= 16; |
| 183 | 1 | matchedCount++; |
| 184 | |
} else { |
| 185 | 0 | fail("Found unknown child in cow(" + base + ")"); |
| 186 | |
} |
| 187 | |
} |
| 188 | 1 | assertEquals("found all children(bits)", 31, matchedBits); |
| 189 | 1 | assertEquals("found all children(count)", 5, matchedCount); |
| 190 | |
|
| 191 | 1 | assertTrue("cow:cow/link exists", cow.resolveFile("cow/link").exists()); |
| 192 | 1 | assertEquals("cow/link content", "letters and numbers", VFSUtil.getString(cow, "cow/link")); |
| 193 | 1 | assertFalse("real:cow/link does not exist", root.resolveFile("cow/link").exists()); |
| 194 | |
|
| 195 | 1 | assertFalse("cow/file1 does not exist", cow.resolveFile("cow/file1").exists()); |
| 196 | |
|
| 197 | 1 | assertTrue("cow/file2 exists", cow.resolveFile("cow/file2").exists()); |
| 198 | |
|
| 199 | 1 | assertTrue("cow:cow/fileN exists", cow.resolveFile("cow/fileN").exists()); |
| 200 | 1 | assertEquals("cow/fileN content", "new content", VFSUtil.getString(cow, "cow/fileN")); |
| 201 | 1 | assertFalse("real:cow/fileN does not exist", root.resolveFile("cow/fileN").exists()); |
| 202 | |
|
| 203 | 1 | assertTrue("cow:cow/file3 exists", cow.resolveFile("cow/file3").exists()); |
| 204 | 1 | assertEquals("cow/file3 content", "letters and numbers", VFSUtil.getString(cow, "cow/file3")); |
| 205 | 1 | assertFalse("real:cow/file3 does not exist", root.resolveFile("cow/file3").exists()); |
| 206 | |
|
| 207 | 1 | VFSUtil.tree(cow, System.err); |
| 208 | 1 | } |
| 209 | |
|
| 210 | |
public void testCOWFsck() throws Exception { |
| 211 | 1 | final LinkedHashMap messages = new LinkedHashMap(); |
| 212 | 1 | COWFsck.MessageHandler messageHandler = new COWFsck.MessageHandler() { |
| 213 | |
public void warn(FileObject file, int code, String message) { |
| 214 | 17 | addMessage("warn", file, code, message); |
| 215 | 17 | } |
| 216 | |
|
| 217 | |
public void info(FileObject file, int code, String message) { |
| 218 | 5 | addMessage("info", file, code, message); |
| 219 | 5 | } |
| 220 | |
|
| 221 | |
public void error(FileObject file, int code, String message) { |
| 222 | 0 | addMessage("error", file, code, message); |
| 223 | 0 | } |
| 224 | |
|
| 225 | |
public void error(FileObject file, int code, String message, Exception e) { |
| 226 | 3 | addMessage("error", file, code, message); |
| 227 | 3 | } |
| 228 | |
|
| 229 | |
protected void addMessage(String level, FileObject file, int code, String message) { |
| 230 | 25 | String key = level + ':' + code + ':' + message + ':' + file.getName().getPath(); |
| 231 | 25 | Integer count = (Integer) messages.get(key); |
| 232 | 25 | messages.put(key, count == null ? Integer.valueOf(1) : Integer.valueOf(count.intValue() + 1)); |
| 233 | 25 | } |
| 234 | |
}; |
| 235 | 1 | COWFsck fsck = new COWFsck(root, messageHandler); |
| 236 | 1 | root.resolveFile("stale empty cow/.cow").createFolder(); |
| 237 | 1 | root.resolveFile("corrupted/cow as file/.cow").createFile(); |
| 238 | |
|
| 239 | 1 | VFSUtil.touch(root.resolveFile("corrupted/symlink/.cow/deleted/linked-deleted-file")); |
| 240 | 1 | VFSUtil.setString(root, "corrupted/symlink/.cow/link/linked-deleted-file", "target"); |
| 241 | |
|
| 242 | 1 | VFSUtil.setString(root, "corrupted/undelete/not-deleted-file", "content"); |
| 243 | 1 | VFSUtil.touch(root.resolveFile("corrupted/undelete/.cow/deleted/not-deleted-file")); |
| 244 | |
|
| 245 | 1 | VFSUtil.touch(root.resolveFile("corrupted/nested-cow-dir/.cow/deleted/.cow/deleted")); |
| 246 | 1 | VFSUtil.touch(root.resolveFile("corrupted/nested-cow-dir/.cow/link/.cow/link")); |
| 247 | 1 | VFSUtil.touch(root.resolveFile("corrupted/nested-cow-dir/.cow/cow-links/.cow/cow-links")); |
| 248 | |
|
| 249 | 1 | VFSUtil.touch(root.resolveFile("corrupted/nested-cow-file/.cow/deleted/.cow")); |
| 250 | 1 | VFSUtil.touch(root.resolveFile("corrupted/nested-cow-file/.cow/link/.cow")); |
| 251 | 1 | VFSUtil.touch(root.resolveFile("corrupted/nested-cow-file/.cow/cow-links/.cow")); |
| 252 | |
|
| 253 | 1 | VFSUtil.touch(root.resolveFile("corrupted/nested-state/.cow/deleted/.cowstate.xml")); |
| 254 | 1 | VFSUtil.touch(root.resolveFile("corrupted/nested-state/.cow/link/.cowstate.xml")); |
| 255 | 1 | VFSUtil.touch(root.resolveFile("corrupted/nested-state/.cow/cow-links/.cowstate.xml")); |
| 256 | |
|
| 257 | 1 | VFSUtil.touch(root.resolveFile("corrupted/not-a-folder/.cow/deleted")); |
| 258 | 1 | VFSUtil.touch(root.resolveFile("corrupted/not-a-folder/.cow/link")); |
| 259 | 1 | VFSUtil.touch(root.resolveFile("corrupted/not-a-folder/.cow/cow-links")); |
| 260 | |
|
| 261 | 1 | VFSUtil.touch(root.resolveFile("corrupted/nested-dir/.cow/unknown1/file")); |
| 262 | 1 | VFSUtil.touch(root.resolveFile("corrupted/nested-dir/.cow/unknown2/file")); |
| 263 | |
|
| 264 | 1 | VFSUtil.touch(root.resolveFile("corrupted/blank-state/.cowstate.xml")); |
| 265 | |
|
| 266 | 1 | VFSUtil.touch(root.resolveFile("corrupted/blank-both/.cowstate.xml")); |
| 267 | 1 | VFSUtil.touch(root.resolveFile("corrupted/blank-both/.cow/deleted/file1")); |
| 268 | |
|
| 269 | 1 | VFSUtil.touch(root.resolveFile("corrupted/both/.cow/deleted/file1")); |
| 270 | 1 | VFSUtil.setString(root, "corrupted/both/.cowstate.xml", "<state><item name=\"file1\" deleted=\"true\"/></state>\n"); |
| 271 | |
|
| 272 | 1 | OutputStream out = root.resolveFile("corrupted/bad-link-file/.cow/link/bad-utf8").getContent().getOutputStream(); |
| 273 | 1 | out.write(0xc2); |
| 274 | 1 | out.close(); |
| 275 | |
|
| 276 | 1 | VFSUtil.setString(root, "corrupted/empty/.cowstate.xml", "<state/>\n"); |
| 277 | |
|
| 278 | 1 | VFSUtil.touch(root.resolveFile("convert/.cow/deleted/deleted-file")); |
| 279 | 1 | VFSUtil.setString(root, "convert/.cow/link/linked-file", "target"); |
| 280 | 1 | VFSUtil.setString(root, "convert/.cow/cow-links/overlay", "base-1\nbase-2\nbase-3\n"); |
| 281 | 1 | fsck.run(); |
| 282 | 1 | } |
| 283 | |
} |