From b355ac5bf5d6f1a86fe23abde851c64495c40cb5 Mon Sep 17 00:00:00 2001 From: xdrm-brackets Date: Wed, 7 Nov 2018 22:13:47 +0100 Subject: [PATCH] create internal/buildfile reader+errors | tests for reader [only check syntax and extract without command-specific splitting] --- internal/buildfile/errors.go | 14 +++++ internal/buildfile/reader.go | 96 +++++++++++++++++++++++++++++++ internal/buildfile/reader_test.go | 67 +++++++++++++++++++++ 3 files changed, 177 insertions(+) create mode 100644 internal/buildfile/errors.go create mode 100644 internal/buildfile/reader.go create mode 100644 internal/buildfile/reader_test.go diff --git a/internal/buildfile/errors.go b/internal/buildfile/errors.go new file mode 100644 index 0000000..d00ffdb --- /dev/null +++ b/internal/buildfile/errors.go @@ -0,0 +1,14 @@ +package buildfile + +import ( + "fmt" +) + +type LineError struct { + Line int + Err error +} + +func (le LineError) Error() string { + return fmt.Sprintf("line %d | %s", le.Line, le.Err.Error()) +} diff --git a/internal/buildfile/reader.go b/internal/buildfile/reader.go new file mode 100644 index 0000000..c6f4047 --- /dev/null +++ b/internal/buildfile/reader.go @@ -0,0 +1,96 @@ +package buildfile + +import ( + "bufio" + "errors" + "io" + "strings" +) + +var ErrInvalidSyntax = errors.New("invalid instruction format") +var ErrUnknownInstruction = errors.New("unknown instruction") + +type instruction_type byte + +const ( + // INSTALL one or more package(s) + INSTALL instruction_type = iota + // UPDATE all installed packages candidates + UPDATE + // REMOVE one or more package(s) + REMOVE + // SERVICE management (enable, disable, start, stop, restart, reload) + SERVICE + // CONF edits configuration files + CONF +) + +// instruction is self-explanatory +type instruction struct { + Type instruction_type + // Sub command instruction-type-specific + Sub []string + Args string +} + +// Reader is the buildfile reader +type Reader struct { + // linux distribution to run on + distribution string + Content []*instruction +} + +// NewReader creates a new reader for the specified build file and linux distribution +func NewReader(distro string, buildfile io.Reader) (*Reader, error) { + + r := &Reader{ + distribution: distro, + Content: make([]*instruction, 0), + } + + // read each line + l, reader := 0, bufio.NewReader(buildfile) + for { + l++ + + line, err := reader.ReadString('\n') + if err == io.EOF { + break + } else if err != nil { + return nil, LineError{l, err} + } + line = strings.Trim(line, " \t\n") + + // check line base syntax 'cmd args...' + if len(line) < 5 || len(strings.Trim(line[0:3], " ")) < 3 || line[1] == ' ' || line[3] != ' ' { + return nil, LineError{l, ErrInvalidSyntax} + } + + // build instruction from line + inst := &instruction{ + Type: INSTALL, + Sub: make([]string, 0), + Args: line[4:], + } + + switch line[0:3] { + case "ins": + inst.Type = INSTALL + case "upd": + inst.Type = UPDATE + case "del": + inst.Type = REMOVE + case "ser": + inst.Type = SERVICE + case "cnf": + inst.Type = CONF + default: + return nil, LineError{l, ErrUnknownInstruction} + } + + r.Content = append(r.Content, inst) + + } + + return r, nil +} diff --git a/internal/buildfile/reader_test.go b/internal/buildfile/reader_test.go new file mode 100644 index 0000000..e388710 --- /dev/null +++ b/internal/buildfile/reader_test.go @@ -0,0 +1,67 @@ +package buildfile + +import ( + "bytes" + "testing" +) + +func TestInstructionSyntax(t *testing.T) { + tests := []struct { + File string + Line int + Err error + }{ + {"ins args\ncnf args\n", 0, nil}, + {" ins args\ncnf args\n", 0, nil}, + {"\tins args\ncnf args\n", 0, nil}, + {" \t ins args\ncnf args\n", 0, nil}, + {" \t ins args\ncnf args\n", 0, nil}, + + {"cmd args\ncmd args\n", 1, ErrUnknownInstruction}, + {"ins args\ncmd args\n", 2, ErrUnknownInstruction}, + {" cmd args\ncmd args\n", 1, ErrUnknownInstruction}, + {"\tcmd args\ncmd args\n", 1, ErrUnknownInstruction}, + {" \t cmd args\ncmd args\n", 1, ErrUnknownInstruction}, + {" \t cmd args\ncmd args\n", 1, ErrUnknownInstruction}, + + {" md args\ncmd args\n", 1, ErrInvalidSyntax}, + {"c d args\ncmd args\n", 1, ErrInvalidSyntax}, + {"cm args\ncmd args\n", 1, ErrInvalidSyntax}, + + {"ins args\n md args\n", 2, ErrInvalidSyntax}, + {"ins args\nc d args\n", 2, ErrInvalidSyntax}, + {"ins args\ncm args\n", 2, ErrInvalidSyntax}, + } + + for i, test := range tests { + + // create reader + buffer := bytes.NewBufferString(test.File) + _, err := NewReader("ubuntu", buffer) + + // no error expected + if test.Err == nil { + if err != nil { + lineerr := err.(LineError) + t.Errorf("[%d] expect no error, got <%s>", i, lineerr.Err) + } + continue + } else if err == nil { + t.Errorf("[%d] expect error <%s>, got none", i, test.Err) + continue + } + + lineerr := err.(LineError) + + // error expected + if lineerr.Err != test.Err { + t.Errorf("[%d] expect error <%s>, got <%s>", i, test.Err, lineerr.Err) + continue + } + + if lineerr.Line != test.Line { + t.Errorf("[%d] expect error at line %d, got %d", i, test.Line, lineerr.Line) + } + + } +}