1
0
mirror of https://github.com/rclone/rclone.git synced 2026-01-06 18:43:50 +00:00

test: move test commands under "rclone test" and make them visible

This commit is contained in:
Nick Craig-Wood
2020-08-08 18:02:18 +01:00
parent fd94b3a473
commit f88a5542cf
10 changed files with 60 additions and 18 deletions

View File

@@ -0,0 +1,158 @@
package main
import (
"encoding/csv"
"encoding/json"
"flag"
"fmt"
"io"
"log"
"os"
"sort"
"strconv"
"github.com/rclone/rclone/cmd/test/info/internal"
)
func main() {
fOut := flag.String("o", "out.csv", "Output file")
flag.Parse()
args := flag.Args()
remotes := make([]internal.InfoReport, 0, len(args))
for _, fn := range args {
f, err := os.Open(fn)
if err != nil {
log.Fatalf("Unable to open %q: %s", fn, err)
}
var remote internal.InfoReport
dec := json.NewDecoder(f)
err = dec.Decode(&remote)
if err != nil {
log.Fatalf("Unable to decode %q: %s", fn, err)
}
if remote.ControlCharacters == nil {
log.Printf("Skipping remote %s: no ControlCharacters", remote.Remote)
} else {
remotes = append(remotes, remote)
}
if err := f.Close(); err != nil {
log.Fatalf("Closing %q failed: %s", fn, err)
}
}
charsMap := make(map[string]string)
var remoteNames []string
for _, r := range remotes {
remoteNames = append(remoteNames, r.Remote)
for k, v := range *r.ControlCharacters {
v.Text = k
quoted := strconv.Quote(k)
charsMap[k] = quoted[1 : len(quoted)-1]
}
}
sort.Strings(remoteNames)
chars := make([]string, 0, len(charsMap))
for k := range charsMap {
chars = append(chars, k)
}
sort.Strings(chars)
// char remote output
recordsMap := make(map[string]map[string][]string)
// remote output
hRemoteMap := make(map[string][]string)
hOperation := []string{"Write", "Write", "Write", "Get", "Get", "Get", "List", "List", "List"}
hPosition := []string{"L", "M", "R", "L", "M", "R", "L", "M", "R"}
// remote
// write get list
// left middle right left middle right left middle right
for _, r := range remotes {
hRemoteMap[r.Remote] = []string{r.Remote, "", "", "", "", "", "", "", ""}
for k, v := range *r.ControlCharacters {
cMap, ok := recordsMap[k]
if !ok {
cMap = make(map[string][]string, 1)
recordsMap[k] = cMap
}
cMap[r.Remote] = []string{
sok(v.WriteError[internal.PositionLeft]), sok(v.WriteError[internal.PositionMiddle]), sok(v.WriteError[internal.PositionRight]),
sok(v.GetError[internal.PositionLeft]), sok(v.GetError[internal.PositionMiddle]), sok(v.GetError[internal.PositionRight]),
pok(v.InList[internal.PositionLeft]), pok(v.InList[internal.PositionMiddle]), pok(v.InList[internal.PositionRight]),
}
}
}
records := [][]string{
{"", ""},
{"", ""},
{"Bytes", "Char"},
}
for _, r := range remoteNames {
records[0] = append(records[0], hRemoteMap[r]...)
records[1] = append(records[1], hOperation...)
records[2] = append(records[2], hPosition...)
}
for _, c := range chars {
k := charsMap[c]
row := []string{fmt.Sprintf("%X", c), k}
for _, r := range remoteNames {
if m, ok := recordsMap[c][r]; ok {
row = append(row, m...)
} else {
row = append(row, "", "", "", "", "", "", "", "", "")
}
}
records = append(records, row)
}
var writer io.Writer
if *fOut == "-" {
writer = os.Stdout
} else {
f, err := os.Create(*fOut)
if err != nil {
log.Fatalf("Unable to create %q: %s", *fOut, err)
}
defer func() {
if err := f.Close(); err != nil {
log.Fatalln("Error writing csv:", err)
}
}()
writer = f
}
w := csv.NewWriter(writer)
err := w.WriteAll(records)
if err != nil {
log.Fatalln("Error writing csv:", err)
} else if err := w.Error(); err != nil {
log.Fatalln("Error writing csv:", err)
}
}
func sok(s string) string {
if s != "" {
return "ERR"
}
return "OK"
}
func pok(p internal.Presence) string {
switch p {
case internal.Absent:
return "MIS"
case internal.Present:
return "OK"
case internal.Renamed:
return "REN"
case internal.Multiple:
return "MUL"
default:
return "ERR"
}
}

View File

@@ -0,0 +1,156 @@
package internal
import (
"bytes"
"encoding/json"
"fmt"
"strings"
)
// Presence describes the presence of a filename in file listing
type Presence int
// Possible Presence states
const (
Absent Presence = iota
Present
Renamed
Multiple
)
// Position is the placement of the test character in the filename
type Position int
// Predefined positions
const (
PositionMiddle Position = 1 << iota
PositionLeft
PositionRight
PositionNone Position = 0
PositionAll Position = PositionRight<<1 - 1
)
// PositionList contains all valid positions
var PositionList = []Position{PositionMiddle, PositionLeft, PositionRight}
// ControlResult contains the result of a single character test
type ControlResult struct {
Text string `json:"-"`
WriteError map[Position]string
GetError map[Position]string
InList map[Position]Presence
}
// InfoReport is the structure of the JSON output
type InfoReport struct {
Remote string
ControlCharacters *map[string]ControlResult
MaxFileLength *int
CanStream *bool
CanWriteUnnormalized *bool
CanReadUnnormalized *bool
CanReadRenormalized *bool
}
func (e Position) String() string {
switch e {
case PositionNone:
return "none"
case PositionAll:
return "all"
}
var buf bytes.Buffer
if e&PositionMiddle != 0 {
buf.WriteString("middle")
e &= ^PositionMiddle
}
if e&PositionLeft != 0 {
if buf.Len() != 0 {
buf.WriteRune(',')
}
buf.WriteString("left")
e &= ^PositionLeft
}
if e&PositionRight != 0 {
if buf.Len() != 0 {
buf.WriteRune(',')
}
buf.WriteString("right")
e &= ^PositionRight
}
if e != PositionNone {
panic("invalid position")
}
return buf.String()
}
// MarshalText encodes the position when used as a map key
func (e Position) MarshalText() ([]byte, error) {
return []byte(e.String()), nil
}
// UnmarshalText decodes a position when used as a map key
func (e *Position) UnmarshalText(text []byte) error {
switch s := strings.ToLower(string(text)); s {
default:
*e = PositionNone
for _, p := range strings.Split(s, ",") {
switch p {
case "left":
*e |= PositionLeft
case "middle":
*e |= PositionMiddle
case "right":
*e |= PositionRight
default:
return fmt.Errorf("unknown position: %s", e)
}
}
case "none":
*e = PositionNone
case "all":
*e = PositionAll
}
return nil
}
func (e Presence) String() string {
switch e {
case Absent:
return "absent"
case Present:
return "present"
case Renamed:
return "renamed"
case Multiple:
return "multiple"
default:
panic("invalid presence")
}
}
// MarshalJSON encodes the presence when used as a JSON value
func (e Presence) MarshalJSON() ([]byte, error) {
return json.Marshal(e.String())
}
// UnmarshalJSON decodes a presence when used as a JSON value
func (e *Presence) UnmarshalJSON(text []byte) error {
var s string
if err := json.Unmarshal(text, &s); err != nil {
return err
}
switch s := strings.ToLower(s); s {
case "absent":
*e = Absent
case "present":
*e = Present
case "renamed":
*e = Renamed
case "multiple":
*e = Multiple
default:
return fmt.Errorf("unknown presence: %s", e)
}
return nil
}