From 27e0615ec528c4846bd50a1e0a8011f0570c5927 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Denis=20Nu=C8=9Biu?= Date: Sat, 28 Oct 2017 19:02:26 +0300 Subject: [PATCH] Implementing a new Stack type --- server/server/errors.go | 32 ++++++-- server/server/parser.go | 6 +- server/server/stack.go | 77 +++++++++++++++++ server/server/stack_test.go | 159 ++++++++++++++++++++++++++++++++++++ 4 files changed, 262 insertions(+), 12 deletions(-) create mode 100644 server/server/stack.go create mode 100644 server/server/stack_test.go diff --git a/server/server/errors.go b/server/server/errors.go index b50d544..b264982 100644 --- a/server/server/errors.go +++ b/server/server/errors.go @@ -2,25 +2,39 @@ package server import "errors" -// InputError will be raised when the input is not right. +// InputError will be raised when the input given to the parser by the client is not right. type InputError struct { // The operation that caused the error. - Op string + Op string // The error that occurred during the operation. Err error } -func (e *InputError) Error() string { return "Error: " + e.Op + ": " + e.Err.Error() } +func (e InputError) Error() string { return "Error: " + e.Op + ": " + e.Err.Error() } // Input Errors var ( - UnknownError = errors.New("Unknown Error.") - InvalidCommand = errors.New("Invalid command.") - TooManyArguments = errors.New("Too many arguments.") - TooFewArguments = errors.New("Too few arguments.") + InputUnknownError = errors.New("Unknown Error.") + InputInvalidCommand = errors.New("Invalid command.") + InputTooManyArguments = errors.New("Too many arguments.") + InputTooFewArguments = errors.New("Too few arguments.") ) -// Command Errors +// Command Errors represent errors that occur when the server is executing commands var ( GetNoBitsError = errors.New("The file/directory contains zero bits!") -) \ No newline at end of file +) + +type StackError struct { + ErrorName string + Err error +} + +func (e StackError) Error() string { return e.ErrorName + ": " + e.Err.Error() } + +// Stack Errors +var ( + StackInvalidTypeError = StackError{"InvalidTypeError", errors.New("Invalid item type for the Stack")} + StackOverflowError = StackError{"StackOverflowError", errors.New("Stack capacity exceeded!")} + StackUnderflowError = StackError{"StackUnderflowError", errors.New("Stack is empty!")} +) diff --git a/server/server/parser.go b/server/server/parser.go index 431db1d..ecf284b 100644 --- a/server/server/parser.go +++ b/server/server/parser.go @@ -8,9 +8,9 @@ import ( // checkArgumentsLength returns an error if length is not equal to expected. func checkArgumentsLength(length int, expected int) error { if length > expected { - return TooManyArguments + return InputTooManyArguments } else if length < expected { - return TooFewArguments + return InputTooFewArguments } return nil } @@ -81,7 +81,7 @@ func ProcessInput(c net.Conn, text string) error { c.Close() default: - return &InputError{thisCommand, InvalidCommand} + return &InputError{thisCommand, InputInvalidCommand} } return nil diff --git a/server/server/stack.go b/server/server/stack.go new file mode 100644 index 0000000..13c20ed --- /dev/null +++ b/server/server/stack.go @@ -0,0 +1,77 @@ +package server + +// Stack interface +type Stack interface { + // Push pushes an item to the stack. Returns an error if it fails. + Push(item interface{}) + // Pop retrieves an item from the stack and removes it. + Pop() interface{} + // Top peeks at an item from the stack. + Top() interface{} + // IsEmpty returns bool indicating if the stack is empty. + IsEmpty() bool + // Capacity returns the capacity of the stack. + Capacity() int + // Size returns the size of the stack + Size() int +} + +// StringStack is a stack that holds string objects +type StringStack struct { + index int + capacity int + items []string +} + +func MakeStringStack(capacity int) *StringStack { + st := StringStack{} + + st.capacity = capacity + st.index = 0 + st.items = make([]string, capacity, capacity) + + return &st +} + +func (st *StringStack) Push(item interface{}) { + if st.index == st.Capacity() { + panic(StackOverflowError) + } + + value, ok := item.(string) + + if ok == false { + panic(StackInvalidTypeError) + } + + st.items[st.index] = value + st.index++ + +} + +func (st *StringStack) Pop() interface{} { + if st.Size() == 0 { + panic(StackUnderflowError) + } + st.index-- + return st.items[st.index] +} + +func (st *StringStack) Top() interface{} { + if st.Size() == 0 { + panic(StackUnderflowError) + } + return st.items[st.index-1] +} + +func (st *StringStack) IsEmpty() bool { + return st.Size() == 0 +} + +func (st *StringStack) Capacity() int { + return st.capacity +} + +func (st *StringStack) Size() int { + return st.index +} diff --git a/server/server/stack_test.go b/server/server/stack_test.go new file mode 100644 index 0000000..a97867d --- /dev/null +++ b/server/server/stack_test.go @@ -0,0 +1,159 @@ +package server + +import ( + "testing" + "math/rand" +) + +func TestMakeStringStack(t *testing.T) { + st := MakeStringStack(10) + + if st == nil { + t.Errorf("MakeStringStack returned null!") + } + + if st.Capacity() != 10 { + t.Errorf("StringStack: Stack capacity is not ok! Want: 10 Got: %d", st.Capacity()) + } + + if st.IsEmpty() != true { + t.Errorf("StringStack: Newly created stack is not empty!") + } +} + +func TestStringStack_CanPush(t *testing.T) { + var st Stack = MakeStringStack(10) + + str := "Hello World" + + st.Push(str) + + if st.Top() != str { + t.Errorf("StringStack: Push() failed. Want: %s Got: %s", str, st.Top()) + } + + if st.Size() != 1 { + t.Errorf("StringStack: Size is not correct after one push. Want %d Got %d", 1, st.Size()) + } +} + +func TestStringStack_StackOverflows(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Errorf("StringStack: Capacity of 0 doesn't overflow on Push()!") + } + }() + + st := MakeStringStack(0) + st.Push(".") +} + +func TestStringStack_InvalidType(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Errorf("StringStack: Push() pushed a non-string type.") + } + }() + + st := MakeStringStack(1) + st.Push(1) +} + +func TestStringStack_Capacity(t *testing.T) { + stCap := rand.Intn(100) + st := MakeStringStack(stCap) + + if st.Capacity() != stCap { + t.Errorf("StringStack: Invalid capacity! Want: %d Got: %d", stCap, st.Capacity()) + } +} + +func TestStringStack_Size(t *testing.T) { + pushes := rand.Intn(10) + st := MakeStringStack(15) + + for i := 0; i < pushes; i++ { + st.Push("a") + } + + if st.Size() != pushes { + t.Errorf("StringStack: Invalid size! Want: %d Got %d", pushes, st.Size()) + } +} + +func TestStringStack_IsEmpty(t *testing.T) { + st := MakeStringStack(10) + + if st.IsEmpty() == false { + t.Errorf("StringStack: With no push, the stack is not empty!") + } +} + +func TestStringStack_IsEmptyAfterPushAndPop(t *testing.T) { + st := MakeStringStack(10) + pushes := rand.Intn(5) + + for i := 0; i < pushes; i++ { + st.Push("a") + } + + for i := 0; i < pushes; i++ { + st.Pop() + } + + if st.IsEmpty() == false { + t.Errorf("StringStack: After push and pop, the stack is not empty!") + } +} + +func TestStringStack_Top(t *testing.T) { + st := MakeStringStack(1) + + st.Push("A") + + if st.Top() != "A" { + t.Errorf("StringStack: Top() returned invalid value!") + } +} + +func TestStringStack_TopUnderflow(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Errorf("StringStack: Top() on empty stack didn't underflow.") + } + }() + + st := MakeStringStack(1) + st.Top() +} + +func TestStringStack_Pop(t *testing.T) { + st := MakeStringStack(1) + + st.Push("A") + + if st.Pop() != "A" { + t.Errorf("StringStack: Pop() returned invalid value!") + } +} + +func TestStringStack_Push(t *testing.T) { + st := MakeStringStack(1) + st.Push("A") +} + +func TestStringStack_PushAndPop(t *testing.T) { + st := MakeStringStack(12) + characters := []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k"} + pushes := rand.Intn(len(characters)) + + for i := 0; i < pushes; i++ { + st.Push(characters[i]) + } + + for i := pushes - 1; i >= 0; i-- { + if val := st.Pop(); val != characters[i] { + t.Errorf("StringStack: Pop() and Push() don't work correctly. Want %s Got %s", characters[i], val) + } + } +}