The reconcile package is used for DOM reconcilation in Isomorphic Go web applications.

file_test.go 27KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184
  1. // Copyright 2014 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package webdav
  5. import (
  6. "encoding/xml"
  7. "fmt"
  8. "io"
  9. "io/ioutil"
  10. "os"
  11. "path"
  12. "path/filepath"
  13. "reflect"
  14. "runtime"
  15. "sort"
  16. "strconv"
  17. "strings"
  18. "testing"
  19. "golang.org/x/net/context"
  20. )
  21. func TestSlashClean(t *testing.T) {
  22. testCases := []string{
  23. "",
  24. ".",
  25. "/",
  26. "/./",
  27. "//",
  28. "//.",
  29. "//a",
  30. "/a",
  31. "/a/b/c",
  32. "/a//b/./../c/d/",
  33. "a",
  34. "a/b/c",
  35. }
  36. for _, tc := range testCases {
  37. got := slashClean(tc)
  38. want := path.Clean("/" + tc)
  39. if got != want {
  40. t.Errorf("tc=%q: got %q, want %q", tc, got, want)
  41. }
  42. }
  43. }
  44. func TestDirResolve(t *testing.T) {
  45. testCases := []struct {
  46. dir, name, want string
  47. }{
  48. {"/", "", "/"},
  49. {"/", "/", "/"},
  50. {"/", ".", "/"},
  51. {"/", "./a", "/a"},
  52. {"/", "..", "/"},
  53. {"/", "..", "/"},
  54. {"/", "../", "/"},
  55. {"/", "../.", "/"},
  56. {"/", "../a", "/a"},
  57. {"/", "../..", "/"},
  58. {"/", "../bar/a", "/bar/a"},
  59. {"/", "../baz/a", "/baz/a"},
  60. {"/", "...", "/..."},
  61. {"/", ".../a", "/.../a"},
  62. {"/", ".../..", "/"},
  63. {"/", "a", "/a"},
  64. {"/", "a/./b", "/a/b"},
  65. {"/", "a/../../b", "/b"},
  66. {"/", "a/../b", "/b"},
  67. {"/", "a/b", "/a/b"},
  68. {"/", "a/b/c/../../d", "/a/d"},
  69. {"/", "a/b/c/../../../d", "/d"},
  70. {"/", "a/b/c/../../../../d", "/d"},
  71. {"/", "a/b/c/d", "/a/b/c/d"},
  72. {"/foo/bar", "", "/foo/bar"},
  73. {"/foo/bar", "/", "/foo/bar"},
  74. {"/foo/bar", ".", "/foo/bar"},
  75. {"/foo/bar", "./a", "/foo/bar/a"},
  76. {"/foo/bar", "..", "/foo/bar"},
  77. {"/foo/bar", "../", "/foo/bar"},
  78. {"/foo/bar", "../.", "/foo/bar"},
  79. {"/foo/bar", "../a", "/foo/bar/a"},
  80. {"/foo/bar", "../..", "/foo/bar"},
  81. {"/foo/bar", "../bar/a", "/foo/bar/bar/a"},
  82. {"/foo/bar", "../baz/a", "/foo/bar/baz/a"},
  83. {"/foo/bar", "...", "/foo/bar/..."},
  84. {"/foo/bar", ".../a", "/foo/bar/.../a"},
  85. {"/foo/bar", ".../..", "/foo/bar"},
  86. {"/foo/bar", "a", "/foo/bar/a"},
  87. {"/foo/bar", "a/./b", "/foo/bar/a/b"},
  88. {"/foo/bar", "a/../../b", "/foo/bar/b"},
  89. {"/foo/bar", "a/../b", "/foo/bar/b"},
  90. {"/foo/bar", "a/b", "/foo/bar/a/b"},
  91. {"/foo/bar", "a/b/c/../../d", "/foo/bar/a/d"},
  92. {"/foo/bar", "a/b/c/../../../d", "/foo/bar/d"},
  93. {"/foo/bar", "a/b/c/../../../../d", "/foo/bar/d"},
  94. {"/foo/bar", "a/b/c/d", "/foo/bar/a/b/c/d"},
  95. {"/foo/bar/", "", "/foo/bar"},
  96. {"/foo/bar/", "/", "/foo/bar"},
  97. {"/foo/bar/", ".", "/foo/bar"},
  98. {"/foo/bar/", "./a", "/foo/bar/a"},
  99. {"/foo/bar/", "..", "/foo/bar"},
  100. {"/foo//bar///", "", "/foo/bar"},
  101. {"/foo//bar///", "/", "/foo/bar"},
  102. {"/foo//bar///", ".", "/foo/bar"},
  103. {"/foo//bar///", "./a", "/foo/bar/a"},
  104. {"/foo//bar///", "..", "/foo/bar"},
  105. {"/x/y/z", "ab/c\x00d/ef", ""},
  106. {".", "", "."},
  107. {".", "/", "."},
  108. {".", ".", "."},
  109. {".", "./a", "a"},
  110. {".", "..", "."},
  111. {".", "..", "."},
  112. {".", "../", "."},
  113. {".", "../.", "."},
  114. {".", "../a", "a"},
  115. {".", "../..", "."},
  116. {".", "../bar/a", "bar/a"},
  117. {".", "../baz/a", "baz/a"},
  118. {".", "...", "..."},
  119. {".", ".../a", ".../a"},
  120. {".", ".../..", "."},
  121. {".", "a", "a"},
  122. {".", "a/./b", "a/b"},
  123. {".", "a/../../b", "b"},
  124. {".", "a/../b", "b"},
  125. {".", "a/b", "a/b"},
  126. {".", "a/b/c/../../d", "a/d"},
  127. {".", "a/b/c/../../../d", "d"},
  128. {".", "a/b/c/../../../../d", "d"},
  129. {".", "a/b/c/d", "a/b/c/d"},
  130. {"", "", "."},
  131. {"", "/", "."},
  132. {"", ".", "."},
  133. {"", "./a", "a"},
  134. {"", "..", "."},
  135. }
  136. for _, tc := range testCases {
  137. d := Dir(filepath.FromSlash(tc.dir))
  138. if got := filepath.ToSlash(d.resolve(tc.name)); got != tc.want {
  139. t.Errorf("dir=%q, name=%q: got %q, want %q", tc.dir, tc.name, got, tc.want)
  140. }
  141. }
  142. }
  143. func TestWalk(t *testing.T) {
  144. type walkStep struct {
  145. name, frag string
  146. final bool
  147. }
  148. testCases := []struct {
  149. dir string
  150. want []walkStep
  151. }{
  152. {"", []walkStep{
  153. {"", "", true},
  154. }},
  155. {"/", []walkStep{
  156. {"", "", true},
  157. }},
  158. {"/a", []walkStep{
  159. {"", "a", true},
  160. }},
  161. {"/a/", []walkStep{
  162. {"", "a", true},
  163. }},
  164. {"/a/b", []walkStep{
  165. {"", "a", false},
  166. {"a", "b", true},
  167. }},
  168. {"/a/b/", []walkStep{
  169. {"", "a", false},
  170. {"a", "b", true},
  171. }},
  172. {"/a/b/c", []walkStep{
  173. {"", "a", false},
  174. {"a", "b", false},
  175. {"b", "c", true},
  176. }},
  177. // The following test case is the one mentioned explicitly
  178. // in the method description.
  179. {"/foo/bar/x", []walkStep{
  180. {"", "foo", false},
  181. {"foo", "bar", false},
  182. {"bar", "x", true},
  183. }},
  184. }
  185. ctx := context.Background()
  186. for _, tc := range testCases {
  187. fs := NewMemFS().(*memFS)
  188. parts := strings.Split(tc.dir, "/")
  189. for p := 2; p < len(parts); p++ {
  190. d := strings.Join(parts[:p], "/")
  191. if err := fs.Mkdir(ctx, d, 0666); err != nil {
  192. t.Errorf("tc.dir=%q: mkdir: %q: %v", tc.dir, d, err)
  193. }
  194. }
  195. i, prevFrag := 0, ""
  196. err := fs.walk("test", tc.dir, func(dir *memFSNode, frag string, final bool) error {
  197. got := walkStep{
  198. name: prevFrag,
  199. frag: frag,
  200. final: final,
  201. }
  202. want := tc.want[i]
  203. if got != want {
  204. return fmt.Errorf("got %+v, want %+v", got, want)
  205. }
  206. i, prevFrag = i+1, frag
  207. return nil
  208. })
  209. if err != nil {
  210. t.Errorf("tc.dir=%q: %v", tc.dir, err)
  211. }
  212. }
  213. }
  214. // find appends to ss the names of the named file and its children. It is
  215. // analogous to the Unix find command.
  216. //
  217. // The returned strings are not guaranteed to be in any particular order.
  218. func find(ctx context.Context, ss []string, fs FileSystem, name string) ([]string, error) {
  219. stat, err := fs.Stat(ctx, name)
  220. if err != nil {
  221. return nil, err
  222. }
  223. ss = append(ss, name)
  224. if stat.IsDir() {
  225. f, err := fs.OpenFile(ctx, name, os.O_RDONLY, 0)
  226. if err != nil {
  227. return nil, err
  228. }
  229. defer f.Close()
  230. children, err := f.Readdir(-1)
  231. if err != nil {
  232. return nil, err
  233. }
  234. for _, c := range children {
  235. ss, err = find(ctx, ss, fs, path.Join(name, c.Name()))
  236. if err != nil {
  237. return nil, err
  238. }
  239. }
  240. }
  241. return ss, nil
  242. }
  243. func testFS(t *testing.T, fs FileSystem) {
  244. errStr := func(err error) string {
  245. switch {
  246. case os.IsExist(err):
  247. return "errExist"
  248. case os.IsNotExist(err):
  249. return "errNotExist"
  250. case err != nil:
  251. return "err"
  252. }
  253. return "ok"
  254. }
  255. // The non-"find" non-"stat" test cases should change the file system state. The
  256. // indentation of the "find"s and "stat"s helps distinguish such test cases.
  257. testCases := []string{
  258. " stat / want dir",
  259. " stat /a want errNotExist",
  260. " stat /d want errNotExist",
  261. " stat /d/e want errNotExist",
  262. "create /a A want ok",
  263. " stat /a want 1",
  264. "create /d/e EEE want errNotExist",
  265. "mk-dir /a want errExist",
  266. "mk-dir /d/m want errNotExist",
  267. "mk-dir /d want ok",
  268. " stat /d want dir",
  269. "create /d/e EEE want ok",
  270. " stat /d/e want 3",
  271. " find / /a /d /d/e",
  272. "create /d/f FFFF want ok",
  273. "create /d/g GGGGGGG want ok",
  274. "mk-dir /d/m want ok",
  275. "mk-dir /d/m want errExist",
  276. "create /d/m/p PPPPP want ok",
  277. " stat /d/e want 3",
  278. " stat /d/f want 4",
  279. " stat /d/g want 7",
  280. " stat /d/h want errNotExist",
  281. " stat /d/m want dir",
  282. " stat /d/m/p want 5",
  283. " find / /a /d /d/e /d/f /d/g /d/m /d/m/p",
  284. "rm-all /d want ok",
  285. " stat /a want 1",
  286. " stat /d want errNotExist",
  287. " stat /d/e want errNotExist",
  288. " stat /d/f want errNotExist",
  289. " stat /d/g want errNotExist",
  290. " stat /d/m want errNotExist",
  291. " stat /d/m/p want errNotExist",
  292. " find / /a",
  293. "mk-dir /d/m want errNotExist",
  294. "mk-dir /d want ok",
  295. "create /d/f FFFF want ok",
  296. "rm-all /d/f want ok",
  297. "mk-dir /d/m want ok",
  298. "rm-all /z want ok",
  299. "rm-all / want err",
  300. "create /b BB want ok",
  301. " stat / want dir",
  302. " stat /a want 1",
  303. " stat /b want 2",
  304. " stat /c want errNotExist",
  305. " stat /d want dir",
  306. " stat /d/m want dir",
  307. " find / /a /b /d /d/m",
  308. "move__ o=F /b /c want ok",
  309. " stat /b want errNotExist",
  310. " stat /c want 2",
  311. " stat /d/m want dir",
  312. " stat /d/n want errNotExist",
  313. " find / /a /c /d /d/m",
  314. "move__ o=F /d/m /d/n want ok",
  315. "create /d/n/q QQQQ want ok",
  316. " stat /d/m want errNotExist",
  317. " stat /d/n want dir",
  318. " stat /d/n/q want 4",
  319. "move__ o=F /d /d/n/z want err",
  320. "move__ o=T /c /d/n/q want ok",
  321. " stat /c want errNotExist",
  322. " stat /d/n/q want 2",
  323. " find / /a /d /d/n /d/n/q",
  324. "create /d/n/r RRRRR want ok",
  325. "mk-dir /u want ok",
  326. "mk-dir /u/v want ok",
  327. "move__ o=F /d/n /u want errExist",
  328. "create /t TTTTTT want ok",
  329. "move__ o=F /d/n /t want errExist",
  330. "rm-all /t want ok",
  331. "move__ o=F /d/n /t want ok",
  332. " stat /d want dir",
  333. " stat /d/n want errNotExist",
  334. " stat /d/n/r want errNotExist",
  335. " stat /t want dir",
  336. " stat /t/q want 2",
  337. " stat /t/r want 5",
  338. " find / /a /d /t /t/q /t/r /u /u/v",
  339. "move__ o=F /t / want errExist",
  340. "move__ o=T /t /u/v want ok",
  341. " stat /u/v/r want 5",
  342. "move__ o=F / /z want err",
  343. " find / /a /d /u /u/v /u/v/q /u/v/r",
  344. " stat /a want 1",
  345. " stat /b want errNotExist",
  346. " stat /c want errNotExist",
  347. " stat /u/v/r want 5",
  348. "copy__ o=F d=0 /a /b want ok",
  349. "copy__ o=T d=0 /a /c want ok",
  350. " stat /a want 1",
  351. " stat /b want 1",
  352. " stat /c want 1",
  353. " stat /u/v/r want 5",
  354. "copy__ o=F d=0 /u/v/r /b want errExist",
  355. " stat /b want 1",
  356. "copy__ o=T d=0 /u/v/r /b want ok",
  357. " stat /a want 1",
  358. " stat /b want 5",
  359. " stat /u/v/r want 5",
  360. "rm-all /a want ok",
  361. "rm-all /b want ok",
  362. "mk-dir /u/v/w want ok",
  363. "create /u/v/w/s SSSSSSSS want ok",
  364. " stat /d want dir",
  365. " stat /d/x want errNotExist",
  366. " stat /d/y want errNotExist",
  367. " stat /u/v/r want 5",
  368. " stat /u/v/w/s want 8",
  369. " find / /c /d /u /u/v /u/v/q /u/v/r /u/v/w /u/v/w/s",
  370. "copy__ o=T d=0 /u/v /d/x want ok",
  371. "copy__ o=T d=∞ /u/v /d/y want ok",
  372. "rm-all /u want ok",
  373. " stat /d/x want dir",
  374. " stat /d/x/q want errNotExist",
  375. " stat /d/x/r want errNotExist",
  376. " stat /d/x/w want errNotExist",
  377. " stat /d/x/w/s want errNotExist",
  378. " stat /d/y want dir",
  379. " stat /d/y/q want 2",
  380. " stat /d/y/r want 5",
  381. " stat /d/y/w want dir",
  382. " stat /d/y/w/s want 8",
  383. " stat /u want errNotExist",
  384. " find / /c /d /d/x /d/y /d/y/q /d/y/r /d/y/w /d/y/w/s",
  385. "copy__ o=F d=∞ /d/y /d/x want errExist",
  386. }
  387. ctx := context.Background()
  388. for i, tc := range testCases {
  389. tc = strings.TrimSpace(tc)
  390. j := strings.IndexByte(tc, ' ')
  391. if j < 0 {
  392. t.Fatalf("test case #%d %q: invalid command", i, tc)
  393. }
  394. op, arg := tc[:j], tc[j+1:]
  395. switch op {
  396. default:
  397. t.Fatalf("test case #%d %q: invalid operation %q", i, tc, op)
  398. case "create":
  399. parts := strings.Split(arg, " ")
  400. if len(parts) != 4 || parts[2] != "want" {
  401. t.Fatalf("test case #%d %q: invalid write", i, tc)
  402. }
  403. f, opErr := fs.OpenFile(ctx, parts[0], os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
  404. if got := errStr(opErr); got != parts[3] {
  405. t.Fatalf("test case #%d %q: OpenFile: got %q (%v), want %q", i, tc, got, opErr, parts[3])
  406. }
  407. if f != nil {
  408. if _, err := f.Write([]byte(parts[1])); err != nil {
  409. t.Fatalf("test case #%d %q: Write: %v", i, tc, err)
  410. }
  411. if err := f.Close(); err != nil {
  412. t.Fatalf("test case #%d %q: Close: %v", i, tc, err)
  413. }
  414. }
  415. case "find":
  416. got, err := find(ctx, nil, fs, "/")
  417. if err != nil {
  418. t.Fatalf("test case #%d %q: find: %v", i, tc, err)
  419. }
  420. sort.Strings(got)
  421. want := strings.Split(arg, " ")
  422. if !reflect.DeepEqual(got, want) {
  423. t.Fatalf("test case #%d %q:\ngot %s\nwant %s", i, tc, got, want)
  424. }
  425. case "copy__", "mk-dir", "move__", "rm-all", "stat":
  426. nParts := 3
  427. switch op {
  428. case "copy__":
  429. nParts = 6
  430. case "move__":
  431. nParts = 5
  432. }
  433. parts := strings.Split(arg, " ")
  434. if len(parts) != nParts {
  435. t.Fatalf("test case #%d %q: invalid %s", i, tc, op)
  436. }
  437. got, opErr := "", error(nil)
  438. switch op {
  439. case "copy__":
  440. depth := 0
  441. if parts[1] == "d=∞" {
  442. depth = infiniteDepth
  443. }
  444. _, opErr = copyFiles(ctx, fs, parts[2], parts[3], parts[0] == "o=T", depth, 0)
  445. case "mk-dir":
  446. opErr = fs.Mkdir(ctx, parts[0], 0777)
  447. case "move__":
  448. _, opErr = moveFiles(ctx, fs, parts[1], parts[2], parts[0] == "o=T")
  449. case "rm-all":
  450. opErr = fs.RemoveAll(ctx, parts[0])
  451. case "stat":
  452. var stat os.FileInfo
  453. fileName := parts[0]
  454. if stat, opErr = fs.Stat(ctx, fileName); opErr == nil {
  455. if stat.IsDir() {
  456. got = "dir"
  457. } else {
  458. got = strconv.Itoa(int(stat.Size()))
  459. }
  460. if fileName == "/" {
  461. // For a Dir FileSystem, the virtual file system root maps to a
  462. // real file system name like "/tmp/webdav-test012345", which does
  463. // not end with "/". We skip such cases.
  464. } else if statName := stat.Name(); path.Base(fileName) != statName {
  465. t.Fatalf("test case #%d %q: file name %q inconsistent with stat name %q",
  466. i, tc, fileName, statName)
  467. }
  468. }
  469. }
  470. if got == "" {
  471. got = errStr(opErr)
  472. }
  473. if parts[len(parts)-2] != "want" {
  474. t.Fatalf("test case #%d %q: invalid %s", i, tc, op)
  475. }
  476. if want := parts[len(parts)-1]; got != want {
  477. t.Fatalf("test case #%d %q: got %q (%v), want %q", i, tc, got, opErr, want)
  478. }
  479. }
  480. }
  481. }
  482. func TestDir(t *testing.T) {
  483. switch runtime.GOOS {
  484. case "nacl":
  485. t.Skip("see golang.org/issue/12004")
  486. case "plan9":
  487. t.Skip("see golang.org/issue/11453")
  488. }
  489. td, err := ioutil.TempDir("", "webdav-test")
  490. if err != nil {
  491. t.Fatal(err)
  492. }
  493. defer os.RemoveAll(td)
  494. testFS(t, Dir(td))
  495. }
  496. func TestMemFS(t *testing.T) {
  497. testFS(t, NewMemFS())
  498. }
  499. func TestMemFSRoot(t *testing.T) {
  500. ctx := context.Background()
  501. fs := NewMemFS()
  502. for i := 0; i < 5; i++ {
  503. stat, err := fs.Stat(ctx, "/")
  504. if err != nil {
  505. t.Fatalf("i=%d: Stat: %v", i, err)
  506. }
  507. if !stat.IsDir() {
  508. t.Fatalf("i=%d: Stat.IsDir is false, want true", i)
  509. }
  510. f, err := fs.OpenFile(ctx, "/", os.O_RDONLY, 0)
  511. if err != nil {
  512. t.Fatalf("i=%d: OpenFile: %v", i, err)
  513. }
  514. defer f.Close()
  515. children, err := f.Readdir(-1)
  516. if err != nil {
  517. t.Fatalf("i=%d: Readdir: %v", i, err)
  518. }
  519. if len(children) != i {
  520. t.Fatalf("i=%d: got %d children, want %d", i, len(children), i)
  521. }
  522. if _, err := f.Write(make([]byte, 1)); err == nil {
  523. t.Fatalf("i=%d: Write: got nil error, want non-nil", i)
  524. }
  525. if err := fs.Mkdir(ctx, fmt.Sprintf("/dir%d", i), 0777); err != nil {
  526. t.Fatalf("i=%d: Mkdir: %v", i, err)
  527. }
  528. }
  529. }
  530. func TestMemFileReaddir(t *testing.T) {
  531. ctx := context.Background()
  532. fs := NewMemFS()
  533. if err := fs.Mkdir(ctx, "/foo", 0777); err != nil {
  534. t.Fatalf("Mkdir: %v", err)
  535. }
  536. readdir := func(count int) ([]os.FileInfo, error) {
  537. f, err := fs.OpenFile(ctx, "/foo", os.O_RDONLY, 0)
  538. if err != nil {
  539. t.Fatalf("OpenFile: %v", err)
  540. }
  541. defer f.Close()
  542. return f.Readdir(count)
  543. }
  544. if got, err := readdir(-1); len(got) != 0 || err != nil {
  545. t.Fatalf("readdir(-1): got %d fileInfos with err=%v, want 0, <nil>", len(got), err)
  546. }
  547. if got, err := readdir(+1); len(got) != 0 || err != io.EOF {
  548. t.Fatalf("readdir(+1): got %d fileInfos with err=%v, want 0, EOF", len(got), err)
  549. }
  550. }
  551. func TestMemFile(t *testing.T) {
  552. testCases := []string{
  553. "wantData ",
  554. "wantSize 0",
  555. "write abc",
  556. "wantData abc",
  557. "write de",
  558. "wantData abcde",
  559. "wantSize 5",
  560. "write 5*x",
  561. "write 4*y+2*z",
  562. "write 3*st",
  563. "wantData abcdexxxxxyyyyzzststst",
  564. "wantSize 22",
  565. "seek set 4 want 4",
  566. "write EFG",
  567. "wantData abcdEFGxxxyyyyzzststst",
  568. "wantSize 22",
  569. "seek set 2 want 2",
  570. "read cdEF",
  571. "read Gx",
  572. "seek cur 0 want 8",
  573. "seek cur 2 want 10",
  574. "seek cur -1 want 9",
  575. "write J",
  576. "wantData abcdEFGxxJyyyyzzststst",
  577. "wantSize 22",
  578. "seek cur -4 want 6",
  579. "write ghijk",
  580. "wantData abcdEFghijkyyyzzststst",
  581. "wantSize 22",
  582. "read yyyz",
  583. "seek cur 0 want 15",
  584. "write ",
  585. "seek cur 0 want 15",
  586. "read ",
  587. "seek cur 0 want 15",
  588. "seek end -3 want 19",
  589. "write ZZ",
  590. "wantData abcdEFghijkyyyzzstsZZt",
  591. "wantSize 22",
  592. "write 4*A",
  593. "wantData abcdEFghijkyyyzzstsZZAAAA",
  594. "wantSize 25",
  595. "seek end 0 want 25",
  596. "seek end -5 want 20",
  597. "read Z+4*A",
  598. "write 5*B",
  599. "wantData abcdEFghijkyyyzzstsZZAAAABBBBB",
  600. "wantSize 30",
  601. "seek end 10 want 40",
  602. "write C",
  603. "wantData abcdEFghijkyyyzzstsZZAAAABBBBB..........C",
  604. "wantSize 41",
  605. "write D",
  606. "wantData abcdEFghijkyyyzzstsZZAAAABBBBB..........CD",
  607. "wantSize 42",
  608. "seek set 43 want 43",
  609. "write E",
  610. "wantData abcdEFghijkyyyzzstsZZAAAABBBBB..........CD.E",
  611. "wantSize 44",
  612. "seek set 0 want 0",
  613. "write 5*123456789_",
  614. "wantData 123456789_123456789_123456789_123456789_123456789_",
  615. "wantSize 50",
  616. "seek cur 0 want 50",
  617. "seek cur -99 want err",
  618. }
  619. ctx := context.Background()
  620. const filename = "/foo"
  621. fs := NewMemFS()
  622. f, err := fs.OpenFile(ctx, filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
  623. if err != nil {
  624. t.Fatalf("OpenFile: %v", err)
  625. }
  626. defer f.Close()
  627. for i, tc := range testCases {
  628. j := strings.IndexByte(tc, ' ')
  629. if j < 0 {
  630. t.Fatalf("test case #%d %q: invalid command", i, tc)
  631. }
  632. op, arg := tc[:j], tc[j+1:]
  633. // Expand an arg like "3*a+2*b" to "aaabb".
  634. parts := strings.Split(arg, "+")
  635. for j, part := range parts {
  636. if k := strings.IndexByte(part, '*'); k >= 0 {
  637. repeatCount, repeatStr := part[:k], part[k+1:]
  638. n, err := strconv.Atoi(repeatCount)
  639. if err != nil {
  640. t.Fatalf("test case #%d %q: invalid repeat count %q", i, tc, repeatCount)
  641. }
  642. parts[j] = strings.Repeat(repeatStr, n)
  643. }
  644. }
  645. arg = strings.Join(parts, "")
  646. switch op {
  647. default:
  648. t.Fatalf("test case #%d %q: invalid operation %q", i, tc, op)
  649. case "read":
  650. buf := make([]byte, len(arg))
  651. if _, err := io.ReadFull(f, buf); err != nil {
  652. t.Fatalf("test case #%d %q: ReadFull: %v", i, tc, err)
  653. }
  654. if got := string(buf); got != arg {
  655. t.Fatalf("test case #%d %q:\ngot %q\nwant %q", i, tc, got, arg)
  656. }
  657. case "seek":
  658. parts := strings.Split(arg, " ")
  659. if len(parts) != 4 {
  660. t.Fatalf("test case #%d %q: invalid seek", i, tc)
  661. }
  662. whence := 0
  663. switch parts[0] {
  664. default:
  665. t.Fatalf("test case #%d %q: invalid seek whence", i, tc)
  666. case "set":
  667. whence = os.SEEK_SET
  668. case "cur":
  669. whence = os.SEEK_CUR
  670. case "end":
  671. whence = os.SEEK_END
  672. }
  673. offset, err := strconv.Atoi(parts[1])
  674. if err != nil {
  675. t.Fatalf("test case #%d %q: invalid offset %q", i, tc, parts[1])
  676. }
  677. if parts[2] != "want" {
  678. t.Fatalf("test case #%d %q: invalid seek", i, tc)
  679. }
  680. if parts[3] == "err" {
  681. _, err := f.Seek(int64(offset), whence)
  682. if err == nil {
  683. t.Fatalf("test case #%d %q: Seek returned nil error, want non-nil", i, tc)
  684. }
  685. } else {
  686. got, err := f.Seek(int64(offset), whence)
  687. if err != nil {
  688. t.Fatalf("test case #%d %q: Seek: %v", i, tc, err)
  689. }
  690. want, err := strconv.Atoi(parts[3])
  691. if err != nil {
  692. t.Fatalf("test case #%d %q: invalid want %q", i, tc, parts[3])
  693. }
  694. if got != int64(want) {
  695. t.Fatalf("test case #%d %q: got %d, want %d", i, tc, got, want)
  696. }
  697. }
  698. case "write":
  699. n, err := f.Write([]byte(arg))
  700. if err != nil {
  701. t.Fatalf("test case #%d %q: write: %v", i, tc, err)
  702. }
  703. if n != len(arg) {
  704. t.Fatalf("test case #%d %q: write returned %d bytes, want %d", i, tc, n, len(arg))
  705. }
  706. case "wantData":
  707. g, err := fs.OpenFile(ctx, filename, os.O_RDONLY, 0666)
  708. if err != nil {
  709. t.Fatalf("test case #%d %q: OpenFile: %v", i, tc, err)
  710. }
  711. gotBytes, err := ioutil.ReadAll(g)
  712. if err != nil {
  713. t.Fatalf("test case #%d %q: ReadAll: %v", i, tc, err)
  714. }
  715. for i, c := range gotBytes {
  716. if c == '\x00' {
  717. gotBytes[i] = '.'
  718. }
  719. }
  720. got := string(gotBytes)
  721. if got != arg {
  722. t.Fatalf("test case #%d %q:\ngot %q\nwant %q", i, tc, got, arg)
  723. }
  724. if err := g.Close(); err != nil {
  725. t.Fatalf("test case #%d %q: Close: %v", i, tc, err)
  726. }
  727. case "wantSize":
  728. n, err := strconv.Atoi(arg)
  729. if err != nil {
  730. t.Fatalf("test case #%d %q: invalid size %q", i, tc, arg)
  731. }
  732. fi, err := fs.Stat(ctx, filename)
  733. if err != nil {
  734. t.Fatalf("test case #%d %q: Stat: %v", i, tc, err)
  735. }
  736. if got, want := fi.Size(), int64(n); got != want {
  737. t.Fatalf("test case #%d %q: got %d, want %d", i, tc, got, want)
  738. }
  739. }
  740. }
  741. }
  742. // TestMemFileWriteAllocs tests that writing N consecutive 1KiB chunks to a
  743. // memFile doesn't allocate a new buffer for each of those N times. Otherwise,
  744. // calling io.Copy(aMemFile, src) is likely to have quadratic complexity.
  745. func TestMemFileWriteAllocs(t *testing.T) {
  746. if runtime.Compiler == "gccgo" {
  747. t.Skip("gccgo allocates here")
  748. }
  749. ctx := context.Background()
  750. fs := NewMemFS()
  751. f, err := fs.OpenFile(ctx, "/xxx", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
  752. if err != nil {
  753. t.Fatalf("OpenFile: %v", err)
  754. }
  755. defer f.Close()
  756. xxx := make([]byte, 1024)
  757. for i := range xxx {
  758. xxx[i] = 'x'
  759. }
  760. a := testing.AllocsPerRun(100, func() {
  761. f.Write(xxx)
  762. })
  763. // AllocsPerRun returns an integral value, so we compare the rounded-down
  764. // number to zero.
  765. if a > 0 {
  766. t.Fatalf("%v allocs per run, want 0", a)
  767. }
  768. }
  769. func BenchmarkMemFileWrite(b *testing.B) {
  770. ctx := context.Background()
  771. fs := NewMemFS()
  772. xxx := make([]byte, 1024)
  773. for i := range xxx {
  774. xxx[i] = 'x'
  775. }
  776. b.ResetTimer()
  777. for i := 0; i < b.N; i++ {
  778. f, err := fs.OpenFile(ctx, "/xxx", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
  779. if err != nil {
  780. b.Fatalf("OpenFile: %v", err)
  781. }
  782. for j := 0; j < 100; j++ {
  783. f.Write(xxx)
  784. }
  785. if err := f.Close(); err != nil {
  786. b.Fatalf("Close: %v", err)
  787. }
  788. if err := fs.RemoveAll(ctx, "/xxx"); err != nil {
  789. b.Fatalf("RemoveAll: %v", err)
  790. }
  791. }
  792. }
  793. func TestCopyMoveProps(t *testing.T) {
  794. ctx := context.Background()
  795. fs := NewMemFS()
  796. create := func(name string) error {
  797. f, err := fs.OpenFile(ctx, name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
  798. if err != nil {
  799. return err
  800. }
  801. _, wErr := f.Write([]byte("contents"))
  802. cErr := f.Close()
  803. if wErr != nil {
  804. return wErr
  805. }
  806. return cErr
  807. }
  808. patch := func(name string, patches ...Proppatch) error {
  809. f, err := fs.OpenFile(ctx, name, os.O_RDWR, 0666)
  810. if err != nil {
  811. return err
  812. }
  813. _, pErr := f.(DeadPropsHolder).Patch(patches)
  814. cErr := f.Close()
  815. if pErr != nil {
  816. return pErr
  817. }
  818. return cErr
  819. }
  820. props := func(name string) (map[xml.Name]Property, error) {
  821. f, err := fs.OpenFile(ctx, name, os.O_RDWR, 0666)
  822. if err != nil {
  823. return nil, err
  824. }
  825. m, pErr := f.(DeadPropsHolder).DeadProps()
  826. cErr := f.Close()
  827. if pErr != nil {
  828. return nil, pErr
  829. }
  830. if cErr != nil {
  831. return nil, cErr
  832. }
  833. return m, nil
  834. }
  835. p0 := Property{
  836. XMLName: xml.Name{Space: "x:", Local: "boat"},
  837. InnerXML: []byte("pea-green"),
  838. }
  839. p1 := Property{
  840. XMLName: xml.Name{Space: "x:", Local: "ring"},
  841. InnerXML: []byte("1 shilling"),
  842. }
  843. p2 := Property{
  844. XMLName: xml.Name{Space: "x:", Local: "spoon"},
  845. InnerXML: []byte("runcible"),
  846. }
  847. p3 := Property{
  848. XMLName: xml.Name{Space: "x:", Local: "moon"},
  849. InnerXML: []byte("light"),
  850. }
  851. if err := create("/src"); err != nil {
  852. t.Fatalf("create /src: %v", err)
  853. }
  854. if err := patch("/src", Proppatch{Props: []Property{p0, p1}}); err != nil {
  855. t.Fatalf("patch /src +p0 +p1: %v", err)
  856. }
  857. if _, err := copyFiles(ctx, fs, "/src", "/tmp", true, infiniteDepth, 0); err != nil {
  858. t.Fatalf("copyFiles /src /tmp: %v", err)
  859. }
  860. if _, err := moveFiles(ctx, fs, "/tmp", "/dst", true); err != nil {
  861. t.Fatalf("moveFiles /tmp /dst: %v", err)
  862. }
  863. if err := patch("/src", Proppatch{Props: []Property{p0}, Remove: true}); err != nil {
  864. t.Fatalf("patch /src -p0: %v", err)
  865. }
  866. if err := patch("/src", Proppatch{Props: []Property{p2}}); err != nil {
  867. t.Fatalf("patch /src +p2: %v", err)
  868. }
  869. if err := patch("/dst", Proppatch{Props: []Property{p1}, Remove: true}); err != nil {
  870. t.Fatalf("patch /dst -p1: %v", err)
  871. }
  872. if err := patch("/dst", Proppatch{Props: []Property{p3}}); err != nil {
  873. t.Fatalf("patch /dst +p3: %v", err)
  874. }
  875. gotSrc, err := props("/src")
  876. if err != nil {
  877. t.Fatalf("props /src: %v", err)
  878. }
  879. wantSrc := map[xml.Name]Property{
  880. p1.XMLName: p1,
  881. p2.XMLName: p2,
  882. }
  883. if !reflect.DeepEqual(gotSrc, wantSrc) {
  884. t.Fatalf("props /src:\ngot %v\nwant %v", gotSrc, wantSrc)
  885. }
  886. gotDst, err := props("/dst")
  887. if err != nil {
  888. t.Fatalf("props /dst: %v", err)
  889. }
  890. wantDst := map[xml.Name]Property{
  891. p0.XMLName: p0,
  892. p3.XMLName: p3,
  893. }
  894. if !reflect.DeepEqual(gotDst, wantDst) {
  895. t.Fatalf("props /dst:\ngot %v\nwant %v", gotDst, wantDst)
  896. }
  897. }
  898. func TestWalkFS(t *testing.T) {
  899. testCases := []struct {
  900. desc string
  901. buildfs []string
  902. startAt string
  903. depth int
  904. walkFn filepath.WalkFunc
  905. want []string
  906. }{{
  907. "just root",
  908. []string{},
  909. "/",
  910. infiniteDepth,
  911. nil,
  912. []string{
  913. "/",
  914. },
  915. }, {
  916. "infinite walk from root",
  917. []string{
  918. "mkdir /a",
  919. "mkdir /a/b",
  920. "touch /a/b/c",
  921. "mkdir /a/d",
  922. "mkdir /e",
  923. "touch /f",
  924. },
  925. "/",
  926. infiniteDepth,
  927. nil,
  928. []string{
  929. "/",
  930. "/a",
  931. "/a/b",
  932. "/a/b/c",
  933. "/a/d",
  934. "/e",
  935. "/f",
  936. },
  937. }, {
  938. "infinite walk from subdir",
  939. []string{
  940. "mkdir /a",
  941. "mkdir /a/b",
  942. "touch /a/b/c",
  943. "mkdir /a/d",
  944. "mkdir /e",
  945. "touch /f",
  946. },
  947. "/a",
  948. infiniteDepth,
  949. nil,
  950. []string{
  951. "/a",
  952. "/a/b",
  953. "/a/b/c",
  954. "/a/d",
  955. },
  956. }, {
  957. "depth 1 walk from root",
  958. []string{
  959. "mkdir /a",
  960. "mkdir /a/b",
  961. "touch /a/b/c",
  962. "mkdir /a/d",
  963. "mkdir /e",
  964. "touch /f",
  965. },
  966. "/",
  967. 1,
  968. nil,
  969. []string{
  970. "/",
  971. "/a",
  972. "/e",
  973. "/f",
  974. },
  975. }, {
  976. "depth 1 walk from subdir",
  977. []string{
  978. "mkdir /a",
  979. "mkdir /a/b",
  980. "touch /a/b/c",
  981. "mkdir /a/b/g",
  982. "mkdir /a/b/g/h",
  983. "touch /a/b/g/i",
  984. "touch /a/b/g/h/j",
  985. },
  986. "/a/b",
  987. 1,
  988. nil,
  989. []string{
  990. "/a/b",
  991. "/a/b/c",
  992. "/a/b/g",
  993. },
  994. }, {
  995. "depth 0 walk from subdir",
  996. []string{
  997. "mkdir /a",
  998. "mkdir /a/b",
  999. "touch /a/b/c",
  1000. "mkdir /a/b/g",
  1001. "mkdir /a/b/g/h",
  1002. "touch /a/b/g/i",
  1003. "touch /a/b/g/h/j",
  1004. },
  1005. "/a/b",
  1006. 0,
  1007. nil,
  1008. []string{
  1009. "/a/b",
  1010. },
  1011. }, {
  1012. "infinite walk from file",
  1013. []string{
  1014. "mkdir /a",
  1015. "touch /a/b",
  1016. "touch /a/c",
  1017. },
  1018. "/a/b",
  1019. 0,
  1020. nil,
  1021. []string{
  1022. "/a/b",
  1023. },
  1024. }, {
  1025. "infinite walk with skipped subdir",
  1026. []string{
  1027. "mkdir /a",
  1028. "mkdir /a/b",
  1029. "touch /a/b/c",
  1030. "mkdir /a/b/g",
  1031. "mkdir /a/b/g/h",
  1032. "touch /a/b/g/i",
  1033. "touch /a/b/g/h/j",
  1034. "touch /a/b/z",
  1035. },
  1036. "/",
  1037. infiniteDepth,
  1038. func(path string, info os.FileInfo, err error) error {
  1039. if path == "/a/b/g" {
  1040. return filepath.SkipDir
  1041. }
  1042. return nil
  1043. },
  1044. []string{
  1045. "/",
  1046. "/a",
  1047. "/a/b",
  1048. "/a/b/c",
  1049. "/a/b/z",
  1050. },
  1051. }}
  1052. ctx := context.Background()
  1053. for _, tc := range testCases {
  1054. fs, err := buildTestFS(tc.buildfs)
  1055. if err != nil {
  1056. t.Fatalf("%s: cannot create test filesystem: %v", tc.desc, err)
  1057. }
  1058. var got []string
  1059. traceFn := func(path string, info os.FileInfo, err error) error {
  1060. if tc.walkFn != nil {
  1061. err = tc.walkFn(path, info, err)
  1062. if err != nil {
  1063. return err
  1064. }
  1065. }
  1066. got = append(got, path)
  1067. return nil
  1068. }
  1069. fi, err := fs.Stat(ctx, tc.startAt)
  1070. if err != nil {
  1071. t.Fatalf("%s: cannot stat: %v", tc.desc, err)
  1072. }
  1073. err = walkFS(ctx, fs, tc.depth, tc.startAt, fi, traceFn)
  1074. if err != nil {
  1075. t.Errorf("%s:\ngot error %v, want nil", tc.desc, err)
  1076. continue
  1077. }
  1078. sort.Strings(got)
  1079. sort.Strings(tc.want)
  1080. if !reflect.DeepEqual(got, tc.want) {
  1081. t.Errorf("%s:\ngot %q\nwant %q", tc.desc, got, tc.want)
  1082. continue
  1083. }
  1084. }
  1085. }
  1086. func buildTestFS(buildfs []string) (FileSystem, error) {
  1087. // TODO: Could this be merged with the build logic in TestFS?
  1088. ctx := context.Background()
  1089. fs := NewMemFS()
  1090. for _, b := range buildfs {
  1091. op := strings.Split(b, " ")
  1092. switch op[0] {
  1093. case "mkdir":
  1094. err := fs.Mkdir(ctx, op[1], os.ModeDir|0777)
  1095. if err != nil {
  1096. return nil, err
  1097. }
  1098. case "touch":
  1099. f, err := fs.OpenFile(ctx, op[1], os.O_RDWR|os.O_CREATE, 0666)
  1100. if err != nil {
  1101. return nil, err
  1102. }
  1103. f.Close()
  1104. case "write":
  1105. f, err := fs.OpenFile(ctx, op[1], os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
  1106. if err != nil {
  1107. return nil, err
  1108. }
  1109. _, err = f.Write([]byte(op[2]))
  1110. f.Close()
  1111. if err != nil {
  1112. return nil, err
  1113. }
  1114. default:
  1115. return nil, fmt.Errorf("unknown file operation %q", op[0])
  1116. }
  1117. }
  1118. return fs, nil
  1119. }