2011-09-05 23:36:14 +02:00
|
|
|
|
package ftp
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bytes"
|
2017-08-11 14:28:41 +02:00
|
|
|
|
"crypto/tls"
|
2011-09-05 23:36:14 +02:00
|
|
|
|
"io/ioutil"
|
2015-08-19 00:17:44 +02:00
|
|
|
|
"net/textproto"
|
2017-08-11 14:28:41 +02:00
|
|
|
|
"os"
|
2017-08-11 18:23:27 +02:00
|
|
|
|
"path/filepath"
|
|
|
|
|
"sort"
|
2017-04-15 11:53:19 +02:00
|
|
|
|
"strings"
|
2011-09-05 23:36:14 +02:00
|
|
|
|
"testing"
|
2015-03-16 23:45:56 +01:00
|
|
|
|
"time"
|
2011-09-05 23:36:14 +02:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
testData = "Just some text"
|
2013-02-17 10:31:56 +01:00
|
|
|
|
testDir = "mydir"
|
2011-09-05 23:36:14 +02:00
|
|
|
|
)
|
|
|
|
|
|
2017-08-11 14:28:41 +02:00
|
|
|
|
func isTLSServer() bool {
|
|
|
|
|
return os.Getenv("FTP_SERVER") == "vsftpd_implicit_tls"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func getConnection() (*ServerConn, error) {
|
|
|
|
|
if isTLSServer() {
|
|
|
|
|
return DialImplicitTLS("localhost:21", &tls.Config{InsecureSkipVerify: true})
|
|
|
|
|
} else {
|
|
|
|
|
return DialTimeout("localhost:21", 5*time.Second)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-20 01:46:22 +02:00
|
|
|
|
func TestConnPASV(t *testing.T) {
|
2017-08-11 18:23:27 +02:00
|
|
|
|
testConn(t, true)
|
2015-08-20 01:46:22 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestConnEPSV(t *testing.T) {
|
2017-08-11 18:23:27 +02:00
|
|
|
|
testConn(t, false)
|
2015-08-20 01:46:22 +02:00
|
|
|
|
}
|
|
|
|
|
|
2017-08-11 14:28:41 +02:00
|
|
|
|
func TestConnTLS(t *testing.T) {
|
|
|
|
|
if !isTLSServer() {
|
|
|
|
|
t.Skip("skipping test in non TLS server env.")
|
|
|
|
|
}
|
2017-08-11 18:23:27 +02:00
|
|
|
|
testConn(t, false)
|
2017-08-11 14:28:41 +02:00
|
|
|
|
}
|
|
|
|
|
|
2017-08-11 18:23:27 +02:00
|
|
|
|
func testConn(t *testing.T, disableEPSV bool) {
|
2015-08-18 18:39:31 +02:00
|
|
|
|
if testing.Short() {
|
|
|
|
|
t.Skip("skipping test in short mode.")
|
|
|
|
|
}
|
2017-08-11 14:28:41 +02:00
|
|
|
|
c, err := getConnection()
|
2015-08-18 18:39:31 +02:00
|
|
|
|
|
2011-09-06 18:27:30 +02:00
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-24 14:49:16 +01:00
|
|
|
|
if disableEPSV {
|
2015-08-20 01:46:22 +02:00
|
|
|
|
delete(c.features, "EPSV")
|
2017-02-05 21:02:16 +01:00
|
|
|
|
c.DisableEPSV = true
|
2015-08-20 01:46:22 +02:00
|
|
|
|
}
|
|
|
|
|
|
2011-09-06 18:27:30 +02:00
|
|
|
|
err = c.Login("anonymous", "anonymous")
|
2011-09-05 23:36:14 +02:00
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = c.NoOp()
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-18 19:34:22 +02:00
|
|
|
|
err = c.ChangeDir("incoming")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
}
|
|
|
|
|
|
2011-09-05 23:36:14 +02:00
|
|
|
|
data := bytes.NewBufferString(testData)
|
|
|
|
|
err = c.Stor("test", data)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, err = c.List(".")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = c.Rename("test", "tset")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-15 11:53:19 +02:00
|
|
|
|
// Read without deadline
|
2011-09-05 23:36:14 +02:00
|
|
|
|
r, err := c.Retr("tset")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
} else {
|
|
|
|
|
buf, err := ioutil.ReadAll(r)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
}
|
|
|
|
|
if string(buf) != testData {
|
|
|
|
|
t.Errorf("'%s'", buf)
|
|
|
|
|
}
|
|
|
|
|
r.Close()
|
2017-05-05 02:46:29 +02:00
|
|
|
|
r.Close() // test we can close two times
|
2011-09-05 23:36:14 +02:00
|
|
|
|
}
|
|
|
|
|
|
2017-04-15 11:53:19 +02:00
|
|
|
|
// Read with deadline
|
|
|
|
|
r, err = c.Retr("tset")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
} else {
|
|
|
|
|
r.SetDeadline(time.Now())
|
|
|
|
|
_, err := ioutil.ReadAll(r)
|
|
|
|
|
if err == nil {
|
|
|
|
|
t.Error("deadline should have caused error")
|
|
|
|
|
} else if !strings.HasSuffix(err.Error(), "i/o timeout") {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
}
|
|
|
|
|
r.Close()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Read with offset
|
2015-08-20 22:47:09 +02:00
|
|
|
|
r, err = c.RetrFrom("tset", 5)
|
2013-07-10 12:18:32 +02:00
|
|
|
|
if err != nil {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
} else {
|
2015-08-20 22:47:09 +02:00
|
|
|
|
buf, err := ioutil.ReadAll(r)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
}
|
|
|
|
|
expected := testData[5:]
|
|
|
|
|
if string(buf) != expected {
|
|
|
|
|
t.Errorf("read %q, expected %q", buf, expected)
|
|
|
|
|
}
|
2013-07-10 12:18:32 +02:00
|
|
|
|
r.Close()
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-19 21:43:08 +01:00
|
|
|
|
fileSize, err := c.FileSize("tset")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
}
|
|
|
|
|
if fileSize != 14 {
|
|
|
|
|
t.Errorf("file size %q, expected %q", fileSize, 14)
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-20 06:34:20 +01:00
|
|
|
|
data = bytes.NewBufferString("")
|
|
|
|
|
err = c.Stor("tset", data)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fileSize, err = c.FileSize("tset")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
}
|
|
|
|
|
if fileSize != 0 {
|
|
|
|
|
t.Errorf("file size %q, expected %q", fileSize, 0)
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-04 12:58:20 +01:00
|
|
|
|
_, err = c.FileSize("not-found")
|
2017-02-19 21:43:08 +01:00
|
|
|
|
if err == nil {
|
|
|
|
|
t.Fatal("expected error, got nil")
|
|
|
|
|
}
|
|
|
|
|
|
2011-09-06 00:02:01 +02:00
|
|
|
|
err = c.Delete("tset")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
}
|
|
|
|
|
|
2011-09-07 16:52:52 +02:00
|
|
|
|
err = c.MakeDir(testDir)
|
2011-09-06 00:02:01 +02:00
|
|
|
|
if err != nil {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
}
|
|
|
|
|
|
2011-09-07 16:52:52 +02:00
|
|
|
|
err = c.ChangeDir(testDir)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dir, err := c.CurrentDir()
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
} else {
|
2015-08-18 19:34:22 +02:00
|
|
|
|
if dir != "/incoming/"+testDir {
|
2011-09-07 16:52:52 +02:00
|
|
|
|
t.Error("Wrong dir: " + dir)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = c.ChangeDirToParent()
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-20 10:32:28 +02:00
|
|
|
|
entries, err := c.NameList("/")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
}
|
|
|
|
|
if len(entries) != 1 || entries[0] != "/incoming" {
|
|
|
|
|
t.Errorf("Unexpected entries: %v", entries)
|
|
|
|
|
}
|
|
|
|
|
|
2011-09-07 16:52:52 +02:00
|
|
|
|
err = c.RemoveDir(testDir)
|
2011-09-06 00:02:01 +02:00
|
|
|
|
if err != nil {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-19 21:15:23 +02:00
|
|
|
|
err = c.Logout()
|
|
|
|
|
if err != nil {
|
2015-08-19 00:17:44 +02:00
|
|
|
|
if protoErr := err.(*textproto.Error); protoErr != nil {
|
|
|
|
|
if protoErr.Code != StatusNotImplemented {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
}
|
2013-05-19 21:15:23 +02:00
|
|
|
|
}
|
|
|
|
|
|
2011-09-05 23:36:14 +02:00
|
|
|
|
c.Quit()
|
2011-09-07 16:52:52 +02:00
|
|
|
|
|
|
|
|
|
err = c.NoOp()
|
|
|
|
|
if err == nil {
|
|
|
|
|
t.Error("Expected error")
|
|
|
|
|
}
|
2011-09-05 23:36:14 +02:00
|
|
|
|
}
|
2013-07-08 07:48:11 +02:00
|
|
|
|
|
2013-12-04 19:12:09 +01:00
|
|
|
|
func TestConnIPv6(t *testing.T) {
|
2015-08-18 19:02:23 +02:00
|
|
|
|
if testing.Short() {
|
|
|
|
|
t.Skip("skipping test in short mode.")
|
|
|
|
|
}
|
2017-08-11 14:28:41 +02:00
|
|
|
|
if isTLSServer() {
|
|
|
|
|
t.Skip("skipping test in TLS mode.")
|
|
|
|
|
}
|
2015-08-18 19:02:23 +02:00
|
|
|
|
|
2015-08-18 22:10:32 +02:00
|
|
|
|
c, err := DialTimeout("[::1]:21", 5*time.Second)
|
2013-12-04 19:12:09 +01:00
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = c.Login("anonymous", "anonymous")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, err = c.List(".")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
c.Quit()
|
|
|
|
|
}
|
2015-08-21 18:36:56 +02:00
|
|
|
|
|
2015-08-21 18:47:44 +02:00
|
|
|
|
// TestConnect tests the legacy Connect function
|
|
|
|
|
func TestConnect(t *testing.T) {
|
2017-08-11 14:28:41 +02:00
|
|
|
|
if testing.Short() || isTLSServer() {
|
2015-08-21 18:47:44 +02:00
|
|
|
|
t.Skip("skipping test in short mode.")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
c, err := Connect("localhost:21")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
c.Quit()
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-21 18:36:56 +02:00
|
|
|
|
func TestTimeout(t *testing.T) {
|
|
|
|
|
if testing.Short() {
|
|
|
|
|
t.Skip("skipping test in short mode.")
|
|
|
|
|
}
|
2017-08-11 14:28:41 +02:00
|
|
|
|
if isTLSServer() {
|
|
|
|
|
t.Skip("skipping test in TLS mode.")
|
|
|
|
|
}
|
2015-08-21 18:36:56 +02:00
|
|
|
|
|
|
|
|
|
c, err := DialTimeout("localhost:2121", 1*time.Second)
|
|
|
|
|
if err == nil {
|
|
|
|
|
t.Fatal("expected timeout, got nil error")
|
|
|
|
|
c.Quit()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
func TestWrongLogin(t *testing.T) {
|
|
|
|
|
if testing.Short() {
|
|
|
|
|
t.Skip("skipping test in short mode.")
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-11 14:28:41 +02:00
|
|
|
|
c, err := getConnection()
|
2015-08-21 18:36:56 +02:00
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
defer c.Quit()
|
|
|
|
|
|
|
|
|
|
err = c.Login("zoo2Shia", "fei5Yix9")
|
|
|
|
|
if err == nil {
|
|
|
|
|
t.Fatal("expected error, got nil")
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-06-06 00:52:51 +02:00
|
|
|
|
|
|
|
|
|
func TestDeleteDirRecur(t *testing.T) {
|
|
|
|
|
if testing.Short() {
|
|
|
|
|
t.Skip("skipping test in short mode.")
|
|
|
|
|
}
|
2017-08-11 14:28:41 +02:00
|
|
|
|
c, err := getConnection()
|
2017-06-06 00:52:51 +02:00
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = c.Login("anonymous", "anonymous")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = c.NoOp()
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = c.ChangeDir("incoming")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = c.MakeDir("testDir")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = c.ChangeDir("testDir")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = c.MakeDir("anotherDir")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
data := bytes.NewBufferString("test text")
|
|
|
|
|
err = c.Stor("fileTest", data)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = c.ChangeDirToParent()
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
}
|
|
|
|
|
err = c.RemoveDirRecur("testDir")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
}
|
|
|
|
|
dir, err := c.CurrentDir()
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
} else {
|
|
|
|
|
if dir != "/incoming" {
|
|
|
|
|
t.Error("Wrong dir: " + dir)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = c.ChangeDir("testDir")
|
|
|
|
|
if err == nil {
|
|
|
|
|
t.Fatal("expected error, got nil")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = c.Logout()
|
|
|
|
|
if err != nil {
|
|
|
|
|
if protoErr := err.(*textproto.Error); protoErr != nil {
|
|
|
|
|
if protoErr.Code != StatusNotImplemented {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
c.Quit()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestFileDeleteDirRecur(t *testing.T) {
|
|
|
|
|
if testing.Short() {
|
|
|
|
|
t.Skip("skipping test in short mode.")
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-11 14:28:41 +02:00
|
|
|
|
c, err := getConnection()
|
2017-06-06 00:52:51 +02:00
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = c.Login("anonymous", "anonymous")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = c.ChangeDir("incoming")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
data := bytes.NewBufferString(testData)
|
|
|
|
|
err = c.Stor("testFile", data)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = c.RemoveDirRecur("testFile")
|
|
|
|
|
if err == nil {
|
|
|
|
|
t.Fatal("expected error got nill")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dir, err := c.CurrentDir()
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
} else {
|
|
|
|
|
if dir != "/incoming" {
|
|
|
|
|
t.Error("Wrong dir: " + dir)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = c.Delete("testFile")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = c.Logout()
|
|
|
|
|
if err != nil {
|
|
|
|
|
if protoErr := err.(*textproto.Error); protoErr != nil {
|
|
|
|
|
if protoErr.Code != StatusNotImplemented {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
c.Quit()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestMissingFolderDeleteDirRecur(t *testing.T) {
|
|
|
|
|
if testing.Short() {
|
|
|
|
|
t.Skip("skipping test in short mode.")
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-11 14:28:41 +02:00
|
|
|
|
c, err := getConnection()
|
2017-06-06 00:52:51 +02:00
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = c.Login("anonymous", "anonymous")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = c.ChangeDir("incoming")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = c.RemoveDirRecur("test")
|
|
|
|
|
if err == nil {
|
|
|
|
|
t.Fatal("expected error got nill")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dir, err := c.CurrentDir()
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
} else {
|
|
|
|
|
if dir != "/incoming" {
|
|
|
|
|
t.Error("Wrong dir: " + dir)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = c.Logout()
|
|
|
|
|
if err != nil {
|
|
|
|
|
if protoErr := err.(*textproto.Error); protoErr != nil {
|
|
|
|
|
if protoErr.Code != StatusNotImplemented {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
c.Quit()
|
|
|
|
|
}
|
2017-08-11 18:23:27 +02:00
|
|
|
|
|
|
|
|
|
var globTests = []struct {
|
|
|
|
|
pattern, result string
|
|
|
|
|
}{
|
|
|
|
|
{"glob/match.go", "glob/match.go"},
|
|
|
|
|
{"glob/mat?h.go", "glob/match.go"},
|
|
|
|
|
{"glob/ma*ch.go", "glob/match.go"},
|
|
|
|
|
{"**/match.go", "glob/match.go"},
|
|
|
|
|
{"**/*", "glob/match.go"},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type MatchTest struct {
|
|
|
|
|
pattern, s string
|
|
|
|
|
match bool
|
|
|
|
|
err error
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var matchTests = []MatchTest{
|
|
|
|
|
{"abc", "abc", true, nil},
|
|
|
|
|
{"*", "abc", true, nil},
|
|
|
|
|
{"*c", "abc", true, nil},
|
|
|
|
|
{"a*", "a", true, nil},
|
|
|
|
|
{"a*", "abc", true, nil},
|
|
|
|
|
{"a*", "ab/c", false, nil},
|
|
|
|
|
{"a*/b", "abc/b", true, nil},
|
|
|
|
|
{"a*/b", "a/c/b", false, nil},
|
|
|
|
|
{"a*b*c*d*e*/f", "axbxcxdxe/f", true, nil},
|
|
|
|
|
{"a*b*c*d*e*/f", "axbxcxdxexxx/f", true, nil},
|
|
|
|
|
{"a*b*c*d*e*/f", "axbxcxdxe/xxx/f", false, nil},
|
|
|
|
|
{"a*b*c*d*e*/f", "axbxcxdxexxx/fff", false, nil},
|
|
|
|
|
{"a*b?c*x", "abxbbxdbxebxczzx", true, nil},
|
|
|
|
|
{"a*b?c*x", "abxbbxdbxebxczzy", false, nil},
|
|
|
|
|
{"ab[c]", "abc", true, nil},
|
|
|
|
|
{"ab[b-d]", "abc", true, nil},
|
|
|
|
|
{"ab[e-g]", "abc", false, nil},
|
|
|
|
|
{"ab[^c]", "abc", false, nil},
|
|
|
|
|
{"ab[^b-d]", "abc", false, nil},
|
|
|
|
|
{"ab[^e-g]", "abc", true, nil},
|
|
|
|
|
{"a\\*b", "a*b", true, nil},
|
|
|
|
|
{"a\\*b", "ab", false, nil},
|
|
|
|
|
{"a?b", "a☺b", true, nil},
|
|
|
|
|
{"a[^a]b", "a☺b", true, nil},
|
|
|
|
|
{"a???b", "a☺b", false, nil},
|
|
|
|
|
{"a[^a][^a][^a]b", "a☺b", false, nil},
|
|
|
|
|
{"[a-ζ]*", "α", true, nil},
|
|
|
|
|
{"*[a-ζ]", "A", false, nil},
|
|
|
|
|
{"a?b", "a/b", false, nil},
|
|
|
|
|
{"a*b", "a/b", false, nil},
|
|
|
|
|
{"[\\]a]", "]", true, nil},
|
|
|
|
|
{"[\\-]", "-", true, nil},
|
|
|
|
|
{"[x\\-]", "x", true, nil},
|
|
|
|
|
{"[x\\-]", "-", true, nil},
|
|
|
|
|
{"[x\\-]", "z", false, nil},
|
|
|
|
|
{"[\\-x]", "x", true, nil},
|
|
|
|
|
{"[\\-x]", "-", true, nil},
|
|
|
|
|
{"[\\-x]", "a", false, nil},
|
|
|
|
|
{"[]a]", "]", false, ErrBadPattern},
|
|
|
|
|
{"[-]", "-", false, ErrBadPattern},
|
|
|
|
|
{"[x-]", "x", false, ErrBadPattern},
|
|
|
|
|
{"[x-]", "-", false, ErrBadPattern},
|
|
|
|
|
{"[x-]", "z", false, ErrBadPattern},
|
|
|
|
|
{"[-x]", "x", false, ErrBadPattern},
|
|
|
|
|
{"[-x]", "-", false, ErrBadPattern},
|
|
|
|
|
{"[-x]", "a", false, ErrBadPattern},
|
|
|
|
|
{"\\", "a", false, ErrBadPattern},
|
|
|
|
|
{"[a-b-c]", "a", false, ErrBadPattern},
|
|
|
|
|
{"[", "a", false, ErrBadPattern},
|
|
|
|
|
{"[^", "a", false, ErrBadPattern},
|
|
|
|
|
{"[^bc", "a", false, ErrBadPattern},
|
|
|
|
|
{"a[", "a", false, nil},
|
|
|
|
|
{"a[", "ab", false, ErrBadPattern},
|
|
|
|
|
{"*x", "xxx", true, nil},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func errp(e error) string {
|
|
|
|
|
if e == nil {
|
|
|
|
|
return "<nil>"
|
|
|
|
|
}
|
|
|
|
|
return e.Error()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// contains returns true if vector contains the string s.
|
|
|
|
|
func contains(vector []string, s string) bool {
|
|
|
|
|
for _, elem := range vector {
|
|
|
|
|
if elem == s {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type globTest struct {
|
|
|
|
|
pattern string
|
|
|
|
|
matches []string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (test *globTest) buildWant(root string) []string {
|
|
|
|
|
var want []string
|
|
|
|
|
for _, m := range test.matches {
|
|
|
|
|
want = append(want, root+filepath.FromSlash(m))
|
|
|
|
|
}
|
|
|
|
|
sort.Strings(want)
|
|
|
|
|
return want
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestMatch(t *testing.T) {
|
|
|
|
|
for _, tt := range matchTests {
|
|
|
|
|
pattern := tt.pattern
|
|
|
|
|
s := tt.s
|
|
|
|
|
ok, err := Match(pattern, s)
|
|
|
|
|
if ok != tt.match || err != tt.err {
|
|
|
|
|
t.Errorf("Match(%#q, %#q) = %v, %q want %v, %q", pattern, s, ok, errp(err), tt.match, errp(tt.err))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestGlob(t *testing.T) {
|
|
|
|
|
|
|
|
|
|
c, err := getConnection()
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if !c.mlstSupported {
|
|
|
|
|
t.Skip("skipping test, server not supporting MLST.")
|
|
|
|
|
}
|
|
|
|
|
err = c.Login("anonymous", "anonymous")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = c.ChangeDir("incoming")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = c.MakeDir("glob")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
data := bytes.NewBufferString("")
|
|
|
|
|
err = c.Stor("glob/match.go", data)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, tt := range globTests {
|
|
|
|
|
pattern := tt.pattern
|
|
|
|
|
result := tt.result
|
|
|
|
|
matches, err := c.Glob(pattern)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Errorf("Glob error for %q: %s", pattern, err)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if !contains(matches, result) {
|
|
|
|
|
t.Errorf("Glob(%#q) = %#v want %v", pattern, matches, result)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for _, pattern := range []string{"no_match", "../*/no_match"} {
|
|
|
|
|
matches, err := c.Glob(pattern)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Errorf("Glob error for %q: %s", pattern, err)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if len(matches) != 0 {
|
|
|
|
|
t.Errorf("Glob(%#q) = %#v want []", pattern, matches)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = c.Logout()
|
|
|
|
|
if err != nil {
|
|
|
|
|
if protoErr := err.(*textproto.Error); protoErr != nil {
|
|
|
|
|
if protoErr.Code != StatusNotImplemented {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
c.Quit()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestGlobError(t *testing.T) {
|
|
|
|
|
c, err := getConnection()
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if !c.mlstSupported {
|
|
|
|
|
t.Skip("skipping test, server not supporting MLST.")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = c.Login("anonymous", "anonymous")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, err = c.Glob("[7]")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Error("expected error for bad pattern; got none")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = c.Logout()
|
|
|
|
|
if err != nil {
|
|
|
|
|
if protoErr := err.(*textproto.Error); protoErr != nil {
|
|
|
|
|
if protoErr.Code != StatusNotImplemented {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
c.Quit()
|
|
|
|
|
}
|