Skip to content

Commit

Permalink
read analysis from file
Browse files Browse the repository at this point in the history
  • Loading branch information
dundee committed Jul 20, 2021
1 parent a883793 commit 5f31cd1
Show file tree
Hide file tree
Showing 13 changed files with 458 additions and 3 deletions.
19 changes: 18 additions & 1 deletion cmd/gdu/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import (
log "github.com/sirupsen/logrus"

"github.com/dundee/gdu/v5/build"
"github.com/dundee/gdu/v5/report"
"github.com/dundee/gdu/v5/internal/common"
"github.com/dundee/gdu/v5/pkg/analyze"
"github.com/dundee/gdu/v5/pkg/device"
"github.com/dundee/gdu/v5/report"
"github.com/dundee/gdu/v5/stdout"
"github.com/dundee/gdu/v5/tui"
"github.com/gdamore/tcell/v2"
Expand All @@ -23,6 +23,7 @@ import (
type UI interface {
ListDevices(getter device.DevicesInfoGetter) error
AnalyzePath(path string, parentDir *analyze.Dir) error
ReadAnalysis(input io.Reader) error
SetIgnoreDirPaths(paths []string)
SetIgnoreDirPatterns(paths []string) error
SetIgnoreHidden(value bool)
Expand All @@ -32,6 +33,7 @@ type UI interface {
// Flags define flags accepted by Run
type Flags struct {
LogFile string
InputFile string
OutputFile string
IgnoreDirs []string
IgnoreDirPatterns []string
Expand Down Expand Up @@ -178,6 +180,21 @@ func (a *App) runAction(ui UI, path string) error {
if err := ui.ListDevices(a.Getter); err != nil {
return fmt.Errorf("loading mount points: %w", err)
}
} else if a.Flags.InputFile != "" {
var input io.Reader
var err error
if a.Flags.InputFile == "-" {
input = os.Stdin
} else {
input, err = os.OpenFile(a.Flags.InputFile, os.O_RDONLY, 0644)
if err != nil {
return fmt.Errorf("opening input file: %w", err)
}
}

if err := ui.ReadAnalysis(input); err != nil {
return fmt.Errorf("reading analysis: %w", err)
}
} else {
if err := ui.AnalyzePath(path, nil); err != nil {
return fmt.Errorf("scanning dir: %w", err)
Expand Down
37 changes: 37 additions & 0 deletions cmd/gdu/app/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,43 @@ func TestAnalyzePathWithExport(t *testing.T) {
assert.Nil(t, err)
}

func TestReadAnalysisFromFile(t *testing.T) {
out, err := runApp(
&Flags{LogFile: "/dev/null", InputFile: "../../../internal/testdata/test.json"},
[]string{"test_dir"},
false,
testdev.DevicesInfoGetterMock{},
)

assert.NotEmpty(t, out)
assert.Contains(t, out, "main.go")
assert.Nil(t, err)
}

func TestReadWrongAnalysisFromFile(t *testing.T) {
out, err := runApp(
&Flags{LogFile: "/dev/null", InputFile: "../../../internal/testdata/wrong.json"},
[]string{"test_dir"},
false,
testdev.DevicesInfoGetterMock{},
)

assert.Empty(t, out)
assert.Contains(t, err.Error(), "Array of maps not found")
}

func TestReadWrongAnalysisFromNotExistingFile(t *testing.T) {
out, err := runApp(
&Flags{LogFile: "/dev/null", InputFile: "xxx.json"},
[]string{"test_dir"},
false,
testdev.DevicesInfoGetterMock{},
)

assert.Empty(t, out)
assert.Contains(t, err.Error(), "no such file or directory")
}

func TestAnalyzePathWithErr(t *testing.T) {
fin := testdir.CreateTestDir()
defer fin()
Expand Down
1 change: 1 addition & 0 deletions cmd/gdu/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ func init() {
flags := rootCmd.Flags()
flags.StringVarP(&af.LogFile, "log-file", "l", "/dev/null", "Path to a logfile")
flags.StringVarP(&af.OutputFile, "output-file", "o", "", "Export analysis into file as JSON")
flags.StringVarP(&af.InputFile, "input-file", "f", "", "Import analysis from JSON file")
flags.IntVarP(&af.MaxCores, "max-cores", "m", runtime.NumCPU(), fmt.Sprintf("Set max cores that GDU will use. %d cores available", runtime.NumCPU()))
flags.BoolVarP(&af.ShowVersion, "version", "v", false, "Print version")

Expand Down
7 changes: 7 additions & 0 deletions internal/testdata/test.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[1,2,{"progname":"gdu","progver":"development","timestamp":1626807263},
[{"name":"/home/gdu"},
[{"name":"app"},
{"name":"app.go","asize":4638,"dsize":8192},
{"name":"app_linux_test.go","asize":1410,"dsize":4096},
{"name":"app_test.go","asize":4974,"dsize":8192}],
{"name":"main.go","asize":3205,"dsize":4096}]]
1 change: 1 addition & 0 deletions internal/testdata/wrong.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[1,2,3,4]
5 changes: 5 additions & 0 deletions report/export.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ func (ui *UI) ListDevices(getter device.DevicesInfoGetter) error {
return errors.New("Exporting devices list is not supported")
}

// ReadAnalysis reads analysis report from JSON file
func (ui *UI) ReadAnalysis(input io.Reader) error {
return errors.New("Reading analysis is not possible while exporting")
}

// AnalyzePath analyzes recursively disk usage in given path
func (ui *UI) AnalyzePath(path string, _ *analyze.Dir) error {
var (
Expand Down
10 changes: 10 additions & 0 deletions report/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,16 @@ func TestShowDevices(t *testing.T) {
assert.Contains(t, err.Error(), "not supported")
}

func TestReadAnalysisWhileExporting(t *testing.T) {
output := bytes.NewBuffer(make([]byte, 10))
reportOutput := bytes.NewBuffer(make([]byte, 10))

ui := CreateExportUI(output, reportOutput, true)
err := ui.ReadAnalysis(output)

assert.Contains(t, err.Error(), "not possible while exporting")
}

func TestExportToFile(t *testing.T) {
fin := testdir.CreateTestDir()
defer fin()
Expand Down
82 changes: 82 additions & 0 deletions report/import.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package report

import (
"bytes"
"encoding/json"
"errors"
"io"
"strings"

"github.com/dundee/gdu/v5/pkg/analyze"
)

// ReadAnalysis reads analysis report from JSON file and returns directory item
func ReadAnalysis(input io.Reader) (*analyze.Dir, error) {
var data interface{}

var buff bytes.Buffer
buff.ReadFrom(input)
json.Unmarshal(buff.Bytes(), &data)

dataArray, ok := data.([]interface{})
if !ok {
return nil, errors.New("JSON file does not contain top level array")
}
if len(dataArray) < 4 {
return nil, errors.New("Top level array must have at least 4 items")
}

items, ok := dataArray[3].([]interface{})
if !ok {
return nil, errors.New("Array of maps not found in the top level array on 4th position")
}

return processDir(items)
}

func processDir(items []interface{}) (*analyze.Dir, error) {
dir := &analyze.Dir{
File: &analyze.File{
Flag: ' ',
},
}
dirMap, ok := items[0].(map[string]interface{})
if !ok {
return nil, errors.New("Directory item is not a map")
}
name, ok := dirMap["name"].(string)
if !ok {
return nil, errors.New("Directory name is not a string")
}

slashPos := strings.LastIndex(name, "/")
if slashPos > -1 {
dir.Name = name[slashPos+1:]
dir.BasePath = name[:slashPos+1]
} else {
dir.Name = name
}

for _, v := range items[1:] {
switch item := v.(type) {
case map[string]interface{}:
file := &analyze.File{}
file.Name = item["name"].(string)
file.Size = int64(item["asize"].(float64))
file.Usage = int64(item["dsize"].(float64))
file.Parent = dir
file.Flag = ' '

dir.Files.Append(file)
case []interface{}:
subdir, err := processDir(item)
if err != nil {
return nil, err
}
subdir.Parent = dir
dir.Files.Append(subdir)
}
}

return dir, nil
}
81 changes: 81 additions & 0 deletions report/import_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package report

import (
"bytes"
"testing"

log "github.com/sirupsen/logrus"

"github.com/stretchr/testify/assert"
)

func init() {
log.SetLevel(log.WarnLevel)
}

func TestReadAnalysis(t *testing.T) {
buff := bytes.NewBuffer([]byte(`
[1,2,{"progname":"gdu","progver":"development","timestamp":1626806293},
[{"name":"/home/xxx"},
{"name":"gdu.json","asize":33805233,"dsize":33808384},
[{"name":"app"},
{"name":"app.go","asize":4638,"dsize":8192},
{"name":"app_linux_test.go","asize":1410,"dsize":4096},
{"name":"app_test.go","asize":4974,"dsize":8192}],
{"name":"main.go","asize":3205,"dsize":4096}]]
`))

dir, err := ReadAnalysis(buff)

assert.Nil(t, err)
assert.Equal(t, "xxx", dir.GetName())
assert.Equal(t, "/home/xxx", dir.GetPath())
}

func TestReadAnalysisWithEmptyInput(t *testing.T) {
buff := bytes.NewBuffer([]byte(``))

_, err := ReadAnalysis(buff)

assert.Equal(t, "JSON file does not contain top level array", err.Error())
}

func TestReadAnalysisWithEmptyArray(t *testing.T) {
buff := bytes.NewBuffer([]byte(`[]`))

_, err := ReadAnalysis(buff)

assert.Equal(t, "Top level array must have at least 4 items", err.Error())
}

func TestReadAnalysisWithWrongContent(t *testing.T) {
buff := bytes.NewBuffer([]byte(`[1,2,3,4]`))

_, err := ReadAnalysis(buff)

assert.Equal(t, "Array of maps not found in the top level array on 4th position", err.Error())
}

func TestReadAnalysisWithEmptyDirContent(t *testing.T) {
buff := bytes.NewBuffer([]byte(`[1,2,3,[{}]]`))

_, err := ReadAnalysis(buff)

assert.Equal(t, "Directory name is not a string", err.Error())
}

func TestReadAnalysisWithWrongDirItem(t *testing.T) {
buff := bytes.NewBuffer([]byte(`[1,2,3,[1, 2, 3]]`))

_, err := ReadAnalysis(buff)

assert.Equal(t, "Directory item is not a map", err.Error())
}

func TestReadAnalysisWithWrongSubdirItem(t *testing.T) {
buff := bytes.NewBuffer([]byte(`[1,2,3,[{"name":"xxx"}, [1,2,3]]]`))

_, err := ReadAnalysis(buff)

assert.Equal(t, "Directory item is not a map", err.Error())
}
Loading

0 comments on commit 5f31cd1

Please sign in to comment.