1
0
mirror of https://github.com/minio/mc.git synced 2025-04-19 21:02:15 +03:00
mc/cmd/find_test.go
2023-04-08 09:43:27 -07:00

451 lines
12 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright (c) 2015-2022 MinIO, Inc.
//
// This file is part of MinIO Object Storage stack
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package cmd
import (
"context"
"os/exec"
"regexp"
"runtime"
"strings"
"testing"
"time"
)
// Tests match find function with all supported inputs on
// file pattern, size and time.
func TestMatchFind(t *testing.T) {
// List of various contexts used in each tests,
// tests are run in the same order as this list.
listFindContexts := []*findContext{
{
clnt: &S3Client{
targetURL: &ClientURL{},
},
ignorePattern: "*.go",
},
{
clnt: &S3Client{
targetURL: &ClientURL{},
},
namePattern: "console",
},
{
clnt: &S3Client{
targetURL: &ClientURL{},
},
pathPattern: "*console*",
},
{
clnt: &S3Client{
targetURL: &ClientURL{},
},
regexPattern: regexp.MustCompile(`^(\d+\.){3}\d+$`),
},
{
clnt: &S3Client{
targetURL: &ClientURL{},
},
olderThan: "1d",
},
{
clnt: &S3Client{
targetURL: &ClientURL{},
},
newerThan: "32000d",
},
{
clnt: &S3Client{
targetURL: &ClientURL{},
},
largerSize: 1024 * 1024,
},
{
clnt: &S3Client{
targetURL: &ClientURL{},
},
smallerSize: 1024,
},
{
clnt: &S3Client{
targetURL: &ClientURL{},
},
ignorePattern: "*.txt",
},
{
clnt: &S3Client{
targetURL: &ClientURL{},
},
},
}
testCases := []struct {
content contentMessage
expectedMatch bool
}{
// Matches ignore pattern, so match will be false - Test 1.
{
content: contentMessage{
Key: "pkg/console/console.go",
},
expectedMatch: false,
},
// Matches name pattern - Test 2.
{
content: contentMessage{
Key: "pkg/console/console.go",
},
expectedMatch: true,
},
// Matches path pattern - Test 3.
{
content: contentMessage{
Key: "pkg/console/console.go",
},
expectedMatch: true,
},
// Matches regex pattern - Test 4.
{
content: contentMessage{
Key: "192.168.1.1",
},
expectedMatch: true,
},
// Matches older than time - Test 5.
{
content: contentMessage{
Time: time.Unix(11999, 0).UTC(),
},
expectedMatch: true,
},
// Matches newer than time - Test 6.
{
content: contentMessage{
Time: time.Unix(12001, 0).UTC(),
},
expectedMatch: true,
},
// Matches size larger - Test 7.
{
content: contentMessage{
Size: 1024 * 1024 * 2,
},
expectedMatch: true,
},
// Matches size smaller - Test 8.
{
content: contentMessage{
Size: 1023,
},
expectedMatch: true,
},
// Does not match ignore pattern, so match will be true - Test 9.
{
content: contentMessage{
Key: "pkg/console/console.go",
},
expectedMatch: true,
},
// No matching inputs were provided, so nothing to match return value is true - Test 10.
{
content: contentMessage{},
expectedMatch: true,
},
}
// Runs all the test cases and validate the expected conditions.
for i, testCase := range testCases {
gotMatch := matchFind(listFindContexts[i], testCase.content)
if testCase.expectedMatch != gotMatch {
t.Errorf("Test: %d, expected match %t, got %t", i+1, testCase.expectedMatch, gotMatch)
}
}
}
// Tests suffix strings trimmed off correctly at maxdepth.
func TestSuffixTrimmingAtMaxDepth(t *testing.T) {
testCases := []struct {
startPrefix string
path string
separator string
maxDepth uint
expectedNewPath string
}{
// Tests at max depth 0.
{
startPrefix: "./",
path: ".git/refs/remotes",
separator: "/",
maxDepth: 0,
expectedNewPath: ".git/refs/remotes",
},
// Tests at max depth 1.
{
startPrefix: "./",
path: ".git/refs/remotes",
separator: "/",
maxDepth: 1,
expectedNewPath: "./.git/",
},
// Tests at max depth 2.
{
startPrefix: "./",
path: ".git/refs/remotes",
separator: "/",
maxDepth: 2,
expectedNewPath: "./.git/refs/",
},
// Tests at max depth 3.
{
startPrefix: "./",
path: ".git/refs/remotes",
separator: "/",
maxDepth: 3,
expectedNewPath: "./.git/refs/remotes",
},
// Tests with startPrefix empty.
{
startPrefix: "",
path: ".git/refs/remotes",
separator: "/",
maxDepth: 2,
expectedNewPath: ".git/refs/",
},
// Tests with separator empty.
{
startPrefix: "",
path: ".git/refs/remotes",
separator: "",
maxDepth: 2,
expectedNewPath: ".g",
},
// Tests with nested startPrefix paths - 1.
{
startPrefix: ".git/refs/",
path: ".git/refs/remotes",
separator: "/",
maxDepth: 1,
expectedNewPath: ".git/refs/remotes",
},
// Tests with nested startPrefix paths - 2.
{
startPrefix: ".git/refs",
path: ".git/refs/remotes",
separator: "/",
maxDepth: 1,
expectedNewPath: ".git/refs/",
},
}
// Run all the test cases and validate for returned new path.
for i, testCase := range testCases {
gotNewPath := trimSuffixAtMaxDepth(testCase.startPrefix, testCase.path, testCase.separator, testCase.maxDepth)
if testCase.expectedNewPath != gotNewPath {
t.Errorf("Test: %d, expected path %s, got %s", i+1, testCase.expectedNewPath, gotNewPath)
}
}
}
// Tests matching functions for name, path and regex.
func TestFindMatch(t *testing.T) {
// testFind is the structure used to contain params pertinent to find related tests
type testFind struct {
pattern, filePath, flagName string
match bool
}
basicTests := []testFind{
// Name match tests - success cases.
{"*.jpg", "carter.jpg", "name", true},
{"console", "pkg/console/console.go", "name", true},
{"console.go", "pkg/console/console.go", "name", true},
{"*XA==", "I/enjoy/morning/walks/XA==", "name ", true},
{"*parser", "/This/might/mess up./the/parser", "name", true},
{"*LTIxNDc0ODM2NDgvLTE=", "What/A/Naughty/String/LTIxNDc0ODM2NDgvLTE=", "name", true},
{"*", "/bla/bla/bla/ ", "name", true},
// Name match tests - failure cases.
{"*.jpg", "carter.jpeg", "name", false},
{"*/test/*", "/test/bob/likes/cake", "name", false},
{"*test/*", "bob/test/likes/cake", "name", false},
{"*test/*", "bob/likes/test/cake", "name", false},
{"*/test/*", "bob/likes/cake/test", "name", false},
{"*.jpg", ".jpg/elves/are/evil", "name", false},
{
"wq3YgNiB2ILYg9iE2IXYnNud3I/hoI7igIvigIzigI3igI7igI/igKrigKvigKzigK3igK7igaDi",
"An/Even/Bigger/String/wq3YgNiB2ILYg9iE2IXYnNud3I/hoI7igIvigIzigI3igI7igI/igKrigKvigKzigK3igK7igaDi", "name", false,
},
{"𝕿𝖍𝖊", "well/this/isAN/odd/font/THE", "name", false},
{"𝕿𝖍𝖊", "well/this/isAN/odd/font/The", "name", false},
{"𝕿𝖍𝖊", "well/this/isAN/odd/font/𝓣𝓱𝓮", "name", false},
{"𝕿𝖍𝖊", "what/a/strange/turn/of/events/𝓣he", "name", false},
{"𝕿𝖍𝖊", "well/this/isAN/odd/font/𝕿𝖍𝖊", "name", true},
// Path match tests - success cases.
{"*/test/*", "bob/test/likes/cake", "path", true},
{"*/test/*", "/test/bob/likes/cake", "path", true},
// Path match tests - failure cases.
{"*.jpg", ".jpg/elves/are/evil", "path", false},
{"*/test/*", "test1/test2/test3/test", "path", false},
{"*/ test /*", "test/test1/test2/test3/test", "path", false},
{"*/test/*", " test /I/have/Really/Long/hair", "path", false},
{"*XA==", "XA==/Height/is/a/social/construct", "path", false},
{"*W", "/Word//this/is a/trickyTest", "path", false},
{"LTIxNDc0ODM2NDgvLTE=", "LTIxNDc0ODM2NDgvLTE=/I/Am/One/Baaaaad/String", "path", false},
{"/", "funky/path/name", "path", false},
}
for _, test := range basicTests {
switch test.flagName {
case "name":
testMatch := nameMatch(test.pattern, test.filePath)
if testMatch != test.match {
t.Fatalf("Unexpected result %t, with pattern %s, flag %s and filepath %s \n",
!test.match, test.pattern, test.flagName, test.filePath)
}
case "path":
testMatch := pathMatch(test.pattern, test.filePath)
if testMatch != test.match {
t.Fatalf("Unexpected result %t, with pattern %s, flag %s and filepath %s \n",
!test.match, test.pattern, test.flagName, test.filePath)
}
}
}
}
// Tests string substitution function.
func TestStringReplace(t *testing.T) {
testCases := []struct {
str string
expectedStr string
content contentMessage
}{
// Tests string replace {} without quotes.
{
str: "{}",
expectedStr: "path/1",
content: contentMessage{Key: "path/1"},
},
// Tests string replace {} with quotes.
{
str: `{""}`,
expectedStr: `"path/1"`,
content: contentMessage{Key: "path/1"},
},
// Tests string replace {base}
{
str: "{base}",
expectedStr: "1",
content: contentMessage{Key: "path/1"},
},
// Tests string replace {"base"} with quotes.
{
str: `{"base"}`,
expectedStr: `"1"`,
content: contentMessage{Key: "path/1"},
},
// Tests string replace {dir}
{
str: `{dir}`,
expectedStr: `path`,
content: contentMessage{Key: "path/1"},
},
// Tests string replace {"dir"} with quotes.
{
str: `{"dir"}`,
expectedStr: `"path"`,
content: contentMessage{Key: "path/1"},
},
// Tests string replace {"size"} with quotes.
{
str: `{"size"}`,
expectedStr: `"0 B"`,
content: contentMessage{Size: 0},
},
// Tests string replace {"time"} with quotes.
{
str: `{"time"}`,
expectedStr: `"2038-01-19 03:14:07 UTC"`,
content: contentMessage{
Time: time.Unix(2147483647, 0).UTC(),
},
},
// Tests string replace {size}
{
str: `{size}`,
expectedStr: `1.0 MiB`,
content: contentMessage{Size: 1024 * 1024},
},
// Tests string replace {time}
{
str: `{time}`,
expectedStr: `2038-01-19 03:14:07 UTC`,
content: contentMessage{
Time: time.Unix(2147483647, 0).UTC(),
},
},
}
for i, testCase := range testCases {
gotStr := stringsReplace(context.Background(), testCase.str, testCase.content)
if gotStr != testCase.expectedStr {
t.Errorf("Test %d: Expected %s, got %s", i+1, testCase.expectedStr, gotStr)
}
}
}
// Tests exit status, getExitStatus() function
func TestGetExitStatus(t *testing.T) {
if runtime.GOOS != "linux" {
t.Skip("Skipping on non-linux")
return
}
testCases := []struct {
command string
expectedExitStatus int
}{
// Tests "No such file or directory", exit status code 2
{
command: "ls asdf",
expectedExitStatus: 2,
},
{
command: "cp x x",
expectedExitStatus: 1,
},
// expectedExitStatus for "command not found" case is 127,
// but exec command cannot capture anything since a process
// for the command could not be started at all,
// so the expectedExitStatus is 1
{
command: "asdf",
expectedExitStatus: 1,
},
}
for i, testCase := range testCases {
commandArgs := strings.Split(testCase.command, " ")
cmd := exec.Command(commandArgs[0], commandArgs[1:]...)
// Return exit status of the command run
exitStatus := getExitStatus(cmd.Run())
if exitStatus != testCase.expectedExitStatus {
t.Errorf("Test %d: Expected error status code for command \"%v\" is %v, got %v",
i+1, testCase.command, testCase.expectedExitStatus, exitStatus)
}
}
}