| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350 |
- // Copyright 2015 The Go Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- package webdav
- import (
- "encoding/xml"
- "fmt"
- "net/http"
- "reflect"
- "sort"
- "testing"
- )
- func TestMemPS(t *testing.T) {
- // calcProps calculates the getlastmodified and getetag DAV: property
- // values in pstats for resource name in file-system fs.
- calcProps := func(name string, fs FileSystem, pstats []Propstat) error {
- fi, err := fs.Stat(name)
- if err != nil {
- return err
- }
- for _, pst := range pstats {
- for i, p := range pst.Props {
- switch p.XMLName {
- case xml.Name{Space: "DAV:", Local: "getlastmodified"}:
- p.InnerXML = []byte(fi.ModTime().Format(http.TimeFormat))
- pst.Props[i] = p
- case xml.Name{Space: "DAV:", Local: "getetag"}:
- if fi.IsDir() {
- continue
- }
- p.InnerXML = []byte(detectETag(fi))
- pst.Props[i] = p
- }
- }
- }
- return nil
- }
- type propOp struct {
- op string
- name string
- propnames []xml.Name
- wantNames []xml.Name
- wantPropstats []Propstat
- }
- testCases := []struct {
- desc string
- buildfs []string
- propOp []propOp
- }{{
- "propname",
- []string{"mkdir /dir", "touch /file"},
- []propOp{{
- op: "propname",
- name: "/dir",
- wantNames: []xml.Name{
- xml.Name{Space: "DAV:", Local: "resourcetype"},
- xml.Name{Space: "DAV:", Local: "displayname"},
- xml.Name{Space: "DAV:", Local: "getcontentlength"},
- xml.Name{Space: "DAV:", Local: "getlastmodified"},
- xml.Name{Space: "DAV:", Local: "getcontenttype"},
- },
- }, {
- op: "propname",
- name: "/file",
- wantNames: []xml.Name{
- xml.Name{Space: "DAV:", Local: "resourcetype"},
- xml.Name{Space: "DAV:", Local: "displayname"},
- xml.Name{Space: "DAV:", Local: "getcontentlength"},
- xml.Name{Space: "DAV:", Local: "getlastmodified"},
- xml.Name{Space: "DAV:", Local: "getcontenttype"},
- xml.Name{Space: "DAV:", Local: "getetag"},
- },
- }},
- }, {
- "allprop dir and file",
- []string{"mkdir /dir", "write /file foobarbaz"},
- []propOp{{
- op: "allprop",
- name: "/dir",
- wantPropstats: []Propstat{{
- Status: http.StatusOK,
- Props: []Property{{
- XMLName: xml.Name{Space: "DAV:", Local: "resourcetype"},
- InnerXML: []byte(`<collection xmlns="DAV:"/>`),
- }, {
- XMLName: xml.Name{Space: "DAV:", Local: "displayname"},
- InnerXML: []byte("dir"),
- }, {
- XMLName: xml.Name{Space: "DAV:", Local: "getcontentlength"},
- InnerXML: []byte("0"),
- }, {
- XMLName: xml.Name{Space: "DAV:", Local: "getlastmodified"},
- InnerXML: nil, // Calculated during test.
- }, {
- XMLName: xml.Name{Space: "DAV:", Local: "getcontenttype"},
- InnerXML: []byte("text/plain; charset=utf-8"),
- }},
- }},
- }, {
- op: "allprop",
- name: "/file",
- wantPropstats: []Propstat{{
- Status: http.StatusOK,
- Props: []Property{{
- XMLName: xml.Name{Space: "DAV:", Local: "resourcetype"},
- InnerXML: []byte(""),
- }, {
- XMLName: xml.Name{Space: "DAV:", Local: "displayname"},
- InnerXML: []byte("file"),
- }, {
- XMLName: xml.Name{Space: "DAV:", Local: "getcontentlength"},
- InnerXML: []byte("9"),
- }, {
- XMLName: xml.Name{Space: "DAV:", Local: "getlastmodified"},
- InnerXML: nil, // Calculated during test.
- }, {
- XMLName: xml.Name{Space: "DAV:", Local: "getcontenttype"},
- InnerXML: []byte("text/plain; charset=utf-8"),
- }, {
- XMLName: xml.Name{Space: "DAV:", Local: "getetag"},
- InnerXML: nil, // Calculated during test.
- }},
- }},
- }, {
- op: "allprop",
- name: "/file",
- propnames: []xml.Name{
- {"DAV:", "resourcetype"},
- {"foo", "bar"},
- },
- wantPropstats: []Propstat{{
- Status: http.StatusOK,
- Props: []Property{{
- XMLName: xml.Name{Space: "DAV:", Local: "resourcetype"},
- InnerXML: []byte(""),
- }, {
- XMLName: xml.Name{Space: "DAV:", Local: "displayname"},
- InnerXML: []byte("file"),
- }, {
- XMLName: xml.Name{Space: "DAV:", Local: "getcontentlength"},
- InnerXML: []byte("9"),
- }, {
- XMLName: xml.Name{Space: "DAV:", Local: "getlastmodified"},
- InnerXML: nil, // Calculated during test.
- }, {
- XMLName: xml.Name{Space: "DAV:", Local: "getcontenttype"},
- InnerXML: []byte("text/plain; charset=utf-8"),
- }, {
- XMLName: xml.Name{Space: "DAV:", Local: "getetag"},
- InnerXML: nil, // Calculated during test.
- }}}, {
- Status: http.StatusNotFound,
- Props: []Property{{
- XMLName: xml.Name{Space: "foo", Local: "bar"},
- }}},
- },
- }},
- }, {
- "propfind DAV:resourcetype",
- []string{"mkdir /dir", "touch /file"},
- []propOp{{
- op: "propfind",
- name: "/dir",
- propnames: []xml.Name{{"DAV:", "resourcetype"}},
- wantPropstats: []Propstat{{
- Status: http.StatusOK,
- Props: []Property{{
- XMLName: xml.Name{Space: "DAV:", Local: "resourcetype"},
- InnerXML: []byte(`<collection xmlns="DAV:"/>`),
- }},
- }},
- }, {
- op: "propfind",
- name: "/file",
- propnames: []xml.Name{{"DAV:", "resourcetype"}},
- wantPropstats: []Propstat{{
- Status: http.StatusOK,
- Props: []Property{{
- XMLName: xml.Name{Space: "DAV:", Local: "resourcetype"},
- InnerXML: []byte(""),
- }},
- }},
- }},
- }, {
- "propfind unsupported DAV properties",
- []string{"mkdir /dir"},
- []propOp{{
- op: "propfind",
- name: "/dir",
- propnames: []xml.Name{{"DAV:", "getcontentlanguage"}},
- wantPropstats: []Propstat{{
- Status: http.StatusNotFound,
- Props: []Property{{
- XMLName: xml.Name{Space: "DAV:", Local: "getcontentlanguage"},
- }},
- }},
- }, {
- op: "propfind",
- name: "/dir",
- propnames: []xml.Name{{"DAV:", "creationdate"}},
- wantPropstats: []Propstat{{
- Status: http.StatusNotFound,
- Props: []Property{{
- XMLName: xml.Name{Space: "DAV:", Local: "creationdate"},
- }},
- }},
- }},
- }, {
- "propfind getetag for files but not for directories",
- []string{"mkdir /dir", "touch /file"},
- []propOp{{
- op: "propfind",
- name: "/dir",
- propnames: []xml.Name{{"DAV:", "getetag"}},
- wantPropstats: []Propstat{{
- Status: http.StatusNotFound,
- Props: []Property{{
- XMLName: xml.Name{Space: "DAV:", Local: "getetag"},
- }},
- }},
- }, {
- op: "propfind",
- name: "/file",
- propnames: []xml.Name{{"DAV:", "getetag"}},
- wantPropstats: []Propstat{{
- Status: http.StatusOK,
- Props: []Property{{
- XMLName: xml.Name{Space: "DAV:", Local: "getetag"},
- InnerXML: nil, // Calculated during test.
- }},
- }},
- }},
- }, {
- "bad: propfind unknown property",
- []string{"mkdir /dir"},
- []propOp{{
- op: "propfind",
- name: "/dir",
- propnames: []xml.Name{{"foo:", "bar"}},
- wantPropstats: []Propstat{{
- Status: http.StatusNotFound,
- Props: []Property{{
- XMLName: xml.Name{Space: "foo:", Local: "bar"},
- }},
- }},
- }},
- }}
- for _, tc := range testCases {
- fs, err := buildTestFS(tc.buildfs)
- if err != nil {
- t.Fatalf("%s: cannot create test filesystem: %v", tc.desc, err)
- }
- ls := NewMemLS()
- ps := NewMemPS(fs, ls)
- for _, op := range tc.propOp {
- desc := fmt.Sprintf("%s: %s %s", tc.desc, op.op, op.name)
- if err = calcProps(op.name, fs, op.wantPropstats); err != nil {
- t.Fatalf("%s: calcProps: %v", desc, err)
- }
- // Call property system.
- var propstats []Propstat
- switch op.op {
- case "propname":
- names, err := ps.Propnames(op.name)
- if err != nil {
- t.Errorf("%s: got error %v, want nil", desc, err)
- continue
- }
- sort.Sort(byXMLName(names))
- sort.Sort(byXMLName(op.wantNames))
- if !reflect.DeepEqual(names, op.wantNames) {
- t.Errorf("%s: names\ngot %q\nwant %q", desc, names, op.wantNames)
- }
- continue
- case "allprop":
- propstats, err = ps.Allprop(op.name, op.propnames)
- case "propfind":
- propstats, err = ps.Find(op.name, op.propnames)
- default:
- t.Fatalf("%s: %s not implemented", desc, op.op)
- }
- if err != nil {
- t.Errorf("%s: got error %v, want nil", desc, err)
- continue
- }
- // Compare return values from allprop or propfind.
- for _, pst := range propstats {
- sort.Sort(byPropname(pst.Props))
- }
- for _, pst := range op.wantPropstats {
- sort.Sort(byPropname(pst.Props))
- }
- sort.Sort(byStatus(propstats))
- sort.Sort(byStatus(op.wantPropstats))
- if !reflect.DeepEqual(propstats, op.wantPropstats) {
- t.Errorf("%s: propstat\ngot %q\nwant %q", desc, propstats, op.wantPropstats)
- }
- }
- }
- }
- func cmpXMLName(a, b xml.Name) bool {
- if a.Space != b.Space {
- return a.Space < b.Space
- }
- return a.Local < b.Local
- }
- type byXMLName []xml.Name
- func (b byXMLName) Len() int {
- return len(b)
- }
- func (b byXMLName) Swap(i, j int) {
- b[i], b[j] = b[j], b[i]
- }
- func (b byXMLName) Less(i, j int) bool {
- return cmpXMLName(b[i], b[j])
- }
- type byPropname []Property
- func (b byPropname) Len() int {
- return len(b)
- }
- func (b byPropname) Swap(i, j int) {
- b[i], b[j] = b[j], b[i]
- }
- func (b byPropname) Less(i, j int) bool {
- return cmpXMLName(b[i].XMLName, b[j].XMLName)
- }
- type byStatus []Propstat
- func (b byStatus) Len() int {
- return len(b)
- }
- func (b byStatus) Swap(i, j int) {
- b[i], b[j] = b[j], b[i]
- }
- func (b byStatus) Less(i, j int) bool {
- return b[i].Status < b[j].Status
- }
|