From ece18eddf67a67a4f200cef50b8765e5b51e7f43 Mon Sep 17 00:00:00 2001
From: ammar68 <ammaa@stud.ntnu.no>
Date: Tue, 29 Oct 2024 12:19:18 +0100
Subject: [PATCH] all modules connected (except database)

---
 behavior-testing/go.mod                       |   3 +
 behavior-testing/main.go                      | 100 ++++++++++++++++++
 llama/compiler/compiler.go                    |  10 --
 llama/go_compiler/go_compiler.go              |  91 ++++++++++++++++
 llama/go_compiler/go_compiler_test.go         |  59 +++++++++++
 llama/go_compiler/should_compile              |   7 ++
 .../go_compiler/should_compile_and_run_tests  |  48 +++++++++
 .../should_compile_with_external_dependencies |  37 +++++++
 .../should_compile_with_faulty_test           |  39 +++++++
 ...compile_with_standard_library_dependencies |  19 ++++
 llama/go_compiler/should_not_compile          |   8 ++
 llama/main.go                                 |  10 +-
 12 files changed, 416 insertions(+), 15 deletions(-)
 create mode 100644 behavior-testing/go.mod
 create mode 100644 behavior-testing/main.go
 delete mode 100644 llama/compiler/compiler.go
 create mode 100644 llama/go_compiler/go_compiler.go
 create mode 100644 llama/go_compiler/go_compiler_test.go
 create mode 100644 llama/go_compiler/should_compile
 create mode 100644 llama/go_compiler/should_compile_and_run_tests
 create mode 100644 llama/go_compiler/should_compile_with_external_dependencies
 create mode 100644 llama/go_compiler/should_compile_with_faulty_test
 create mode 100644 llama/go_compiler/should_compile_with_standard_library_dependencies
 create mode 100644 llama/go_compiler/should_not_compile

diff --git a/behavior-testing/go.mod b/behavior-testing/go.mod
new file mode 100644
index 0000000..400bb0a
--- /dev/null
+++ b/behavior-testing/go.mod
@@ -0,0 +1,3 @@
+module behavior-testing
+
+go 1.23.1
diff --git a/behavior-testing/main.go b/behavior-testing/main.go
new file mode 100644
index 0000000..5f5b1a4
--- /dev/null
+++ b/behavior-testing/main.go
@@ -0,0 +1,100 @@
+package main
+
+import (
+	"fmt"
+	"testing"
+)
+
+type NumberGenerator interface {
+        generateEvenNumbers() []int
+}
+
+type EvenNumber struct{}
+
+func (e *EvenNumber) generateEvenNumbers() []int {
+        evenNumbers := make([]int, 5)
+        for i := 0; i < 5; i++ {
+                evenNumbers[i] = 2 * (i + 1)
+        }
+        return evenNumbers
+}
+
+type TestNumberGenerator struct{}
+
+func (t *TestNumberGenerator) generateEvenNumbers() []int {
+        evenNumbers := make([]int, 10)
+        for i := 0; i < 5; i++ {
+                evenNumbers[i] = 2 * (i + 1)
+        }
+        return evenNumbers
+}
+
+type BrokenNumberGenerator struct{}
+
+func (b *BrokenNumberGenerator) generateEvenNumbers() []int {
+        // This function will not generate even numbers but it won't panic either
+        return make([]int, 0)
+}
+
+func main() {
+        evenNumber := &EvenNumber{}
+        generatedNumbers := evenNumber.generateEvenNumbers()
+        fmt.Println(generatedNumbers)
+
+        brokenNumberGenerator := &BrokenNumberGenerator{}
+        generatedNumbers = brokenNumberGenerator.generateEvenNumbers()
+        fmt.Println(generatedNumbers)
+
+        testNumberGenerator := &TestNumberGenerator{}
+        generatedNumbers = testNumberGenerator.generateEvenNumbers()
+        fmt.Println(generatedNumbers)
+}
+
+func TestGenerateEvenNumbers(t *testing.T) {
+        evenNumber := &EvenNumber{}
+        generatedNumbers := evenNumber.generateEvenNumbers()
+
+        if len(generatedNumbers) != 5 {
+                t.Errorf("Expected 5 numbers but got %d", len(generatedNumbers))
+        }
+        for _, number := range generatedNumbers {
+                if number%2 == 1 {
+                        t.Errorf("Expected all numbers to be even but got an odd number: %d", number)
+                }
+        }
+
+        testNumberGenerator := &TestNumberGenerator{}
+        generatedNumbers = testNumberGenerator.generateEvenNumbers()
+        if len(generatedNumbers) != 10 {
+                t.Errorf("Expected 10 numbers but got %d", len(generatedNumbers))
+        }
+        for _, number := range generatedNumbers[:5] {
+                if number%2 == 1 {
+                        t.Errorf("Expected all first 5 numbers to be even but got an odd number: %d", number)
+                }
+        }
+
+        brokenNumberGenerator := &BrokenNumberGenerator{}
+        generatedNumbers = brokenNumberGenerator.generateEvenNumbers()
+        if len(generatedNumbers) != 0 {
+                t.Errorf("Expected zero numbers but got %d", len(generatedNumbers))
+        }
+}
+
+func TestGenerateEvenNumbersSize(t *testing.T) {
+        evenNumber := &EvenNumber{}
+        generatedNumbers := evenNumber.generateEvenNumbers()
+
+        if len(generatedNumbers) == 5 && generatedNumbers[4] != 10 {
+                t.Errorf("Expected last number to be 10 but got %d", generatedNumbers[4])
+        }
+}
+
+func TestGenerateEvenNumbersBroken(t *testing.T) {
+        brokenNumberGenerator := &BrokenNumberGenerator{}
+        generatedNumbers := brokenNumberGenerator.generateEvenNumbers()
+
+        if len(generatedNumbers) != 0 {
+                t.Errorf("Expected zero numbers but got %d", len(generatedNumbers))
+        }
+}
\ No newline at end of file
diff --git a/llama/compiler/compiler.go b/llama/compiler/compiler.go
deleted file mode 100644
index 41da418..0000000
--- a/llama/compiler/compiler.go
+++ /dev/null
@@ -1,10 +0,0 @@
-package compiler
-
-// CompileStringToGo tries to compile a string of go code to a go executable, and returns the compiler output and an error.
-// The function does not produce any executables, since they are deleted after the function ends.
-func CompileStringToGo(code string) (string, error) {
-
-	var output string = ""
-	// SetupEnvironment
-	return output, nil
-}
diff --git a/llama/go_compiler/go_compiler.go b/llama/go_compiler/go_compiler.go
new file mode 100644
index 0000000..c9de3d7
--- /dev/null
+++ b/llama/go_compiler/go_compiler.go
@@ -0,0 +1,91 @@
+package go_compiler
+
+import (
+	"os"
+	"os/exec"
+	"runtime"
+)
+
+const TempOutputDir = "tempOutput/"
+const fileName = "main.go"
+
+type GoCompiler struct{}
+
+// NewGoCompiler creates a new GoCompiler
+func NewGoCompiler() *GoCompiler {
+	return &GoCompiler{}
+}
+
+// OS Operating system type
+type OS = string
+
+// Platform enums
+const (
+	Windows OS = "windows"
+	Linux   OS = "linux"
+	MacOS   OS = "darwin"
+)
+
+
+// MakeCommand creates a command based on the runtime platform
+func MakeCommand(cmd string) *exec.Cmd {
+	platform := runtime.GOOS
+	switch platform {
+	case Windows:
+		return exec.Command("cmd", "/c", cmd)
+	case Linux, MacOS:
+		return exec.Command("bash", "-c", cmd)
+	default:
+		panic("Unsupported platform")
+	}
+}
+
+// SetupTempFolders creates the temp output directory for compiled files, panics if it fails
+func SetupTempFolders(tempOutputDir string) {
+	// 0777 are the permissions for the directory, everyone can read, write and execute
+	err := os.MkdirAll(tempOutputDir, os.ModePerm)
+	if err != nil {
+		panic("Error creating temp output directory:\n\n" + err.Error())
+	}
+}
+
+// RemoveTempFolders removes the temp output directory for compiled files, panics if it fails
+func RemoveTempFolders(tempOutputDir string) {
+	err := os.RemoveAll(tempOutputDir)
+	if err != nil {
+		panic("Error removing temp output directory:\n\n" + err.Error())
+	}
+}
+
+
+
+// CheckCompileErrors takes Go source code and checks for compile errors.
+//
+// The dependencies are handled automatically by go mod and go tidy.
+//
+// NOTE: Make sure you have an up-to-date Go installed on the system
+//
+// Returns the output of the compilation and an error if any
+func (gb *GoCompiler) CheckCompileErrors(srcCode []byte) ([]byte, error) {
+	// Make temp folders
+	SetupTempFolders(TempOutputDir)
+	defer RemoveTempFolders(TempOutputDir)
+
+	// Write code to file
+	err := os.WriteFile(TempOutputDir+fileName, srcCode, 0644)
+	if err != nil {
+		return nil, err
+	}
+	// Init go mod and tidy
+	cmdString := "go mod init tempOutput && go mod tidy "
+
+	// Run go build
+	cmdString += " && go build -o main " + fileName
+
+	// Run tests
+	cmdString += " && go test " + fileName
+
+	cmd := MakeCommand(cmdString)
+	cmd.Dir = TempOutputDir
+	return cmd.CombinedOutput()
+}
diff --git a/llama/go_compiler/go_compiler_test.go b/llama/go_compiler/go_compiler_test.go
new file mode 100644
index 0000000..eef1365
--- /dev/null
+++ b/llama/go_compiler/go_compiler_test.go
@@ -0,0 +1,59 @@
+package go_compiler
+
+import (
+	"os"
+	"testing"
+)
+
+func TestCompileStringToGo(t *testing.T) {
+
+	tests := []struct {
+		filename      string
+		shouldCompile bool
+	}{
+		{
+			filename:      "should_compile",
+			shouldCompile: true,
+		},
+		{
+			filename:      "should_not_compile",
+			shouldCompile: false,
+		},
+		{
+			filename:      "should_compile_with_standard_library_dependencies",
+			shouldCompile: true,
+		},
+		{
+			filename:      "should_compile_with_external_dependencies",
+			shouldCompile: true,
+		},
+		{
+			filename:      "should_compile_and_run_tests",
+			shouldCompile: true,
+		},
+		{
+			filename:      "should_compile_with_faulty_test",
+			shouldCompile: true,
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.filename, func(t *testing.T) {
+			// Read the code from the file
+			code, err := os.ReadFile(test.filename)
+
+			output, err := NewGoCompiler().CheckCompileErrors(code)
+
+			if err != nil && test.shouldCompile {
+				t.Errorf("Expected the code to compile, but got an output: %v \n error: %v", string(output), err)
+			} else if err == nil && !test.shouldCompile {
+				t.Errorf("Expected the code to not compile, but got no error")
+			}
+
+			// Check if the output is empty when the code shouldn't compile
+			if output == nil && !test.shouldCompile {
+				t.Errorf("Expected compiler error output, but got none")
+			}
+		})
+	}
+}
diff --git a/llama/go_compiler/should_compile b/llama/go_compiler/should_compile
new file mode 100644
index 0000000..2d35af1
--- /dev/null
+++ b/llama/go_compiler/should_compile
@@ -0,0 +1,7 @@
+package main
+
+import "fmt"
+
+func main() {
+	fmt.Println("Hello, World!")
+}
\ No newline at end of file
diff --git a/llama/go_compiler/should_compile_and_run_tests b/llama/go_compiler/should_compile_and_run_tests
new file mode 100644
index 0000000..32c15ee
--- /dev/null
+++ b/llama/go_compiler/should_compile_and_run_tests
@@ -0,0 +1,48 @@
+package main
+
+import (
+    "errors"
+    "fmt"
+    "testing"
+)
+
+// Divide divides two numbers and returns the result.
+// Returns an error if division by zero is attempted.
+func Divide(a, b float64) (float64, error) {
+    if b == 0 {
+        return 0, errors.New("cannot divide by zero")
+    }
+    return a / b, nil
+}
+
+// Test cases for Divide function
+func TestDivide(t *testing.T) {
+    // Test case 1: Normal division
+    result, err := Divide(10, 2)
+    if err != nil || result != 5 {
+        t.Errorf("Expected 5, got %v, error: %v", result, err)
+    }
+
+    // Test case 2: Division by zero
+    _, err = Divide(10, 0)
+    if err == nil {
+        t.Error("Expected error for division by zero, got nil")
+    }
+
+    // Test case 3: Division with negative numbers
+    result, err = Divide(-10, 2)
+    if err != nil || result != -5 {
+        t.Errorf("Expected -5, got %v, error: %v", result, err)
+    }
+}
+
+// main function for demonstration purposes
+func main() {
+    a, b := 10.0, 2.0
+    result, err := Divide(a, b)
+    if err != nil {
+        fmt.Println("Error:", err)
+    } else {
+        fmt.Printf("Result of %.2f / %.2f = %.2f\n", a, b, result)
+    }
+}
diff --git a/llama/go_compiler/should_compile_with_external_dependencies b/llama/go_compiler/should_compile_with_external_dependencies
new file mode 100644
index 0000000..3a047ae
--- /dev/null
+++ b/llama/go_compiler/should_compile_with_external_dependencies
@@ -0,0 +1,37 @@
+// These are dependencies that are **NOT** part of the standard library
+package main
+
+import (
+    "fmt"
+    "golang.org/x/net/http2"
+    "golang.org/x/crypto/bcrypt"
+    "net/http"
+)
+
+func main() {
+    // Setting up a simple HTTP/2 server
+    srv := &http.Server{
+        Addr: ":8080",
+    }
+
+    // Register a simple handler
+    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+        // Hashing a password
+        password := "mysecretpassword"
+        hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
+        if err != nil {
+            http.Error(w, "Could not hash password", http.StatusInternalServerError)
+            return
+        }
+        fmt.Fprintf(w, "Hashed Password: %s\n", hashedPassword)
+    })
+
+    // Enable HTTP/2
+    http2.ConfigureServer(srv, nil)
+
+    // Start the server
+    fmt.Println("Starting server on https://localhost:8080")
+    if err := srv.ListenAndServeTLS("server.crt", "server.key"); err != nil {
+        fmt.Println("Error starting server:", err)
+    }
+}
diff --git a/llama/go_compiler/should_compile_with_faulty_test b/llama/go_compiler/should_compile_with_faulty_test
new file mode 100644
index 0000000..a988911
--- /dev/null
+++ b/llama/go_compiler/should_compile_with_faulty_test
@@ -0,0 +1,39 @@
+package main
+
+import (
+    "fmt"
+    "testing"
+)
+
+// Add adds two integers and returns the result.
+func Add(a, b int) int {
+    return a + b
+}
+
+// Test cases for Add function
+func TestAdd(t *testing.T) {
+    // Test case 1: Normal addition
+    result := Add(2, 3)
+    if result != 5 {
+        t.Errorf("Expected 5, got %v", result)
+    }
+
+    // Faulty Test case 2: Incorrect expected result
+    result = Add(2, 2)
+    if result != 5 { // This is faulty, it should expect 4, not 5
+        t.Errorf("Expected 5, got %v", result)
+    }
+
+    // Test case 3: Adding negative numbers
+    result = Add(-2, -3)
+    if result != -5 {
+        t.Errorf("Expected -5, got %v", result)
+    }
+}
+
+// main function for demonstration purposes
+func main() {
+    a, b := 2, 3
+    result := Add(a, b)
+    fmt.Printf("Result of %d + %d = %d\n", a, b, result)
+}
diff --git a/llama/go_compiler/should_compile_with_standard_library_dependencies b/llama/go_compiler/should_compile_with_standard_library_dependencies
new file mode 100644
index 0000000..7134eea
--- /dev/null
+++ b/llama/go_compiler/should_compile_with_standard_library_dependencies
@@ -0,0 +1,19 @@
+// These are dependencies that are part of the standard library
+package main
+
+import (
+    "fmt"
+    "math/rand"
+    "time"
+)
+
+func main() {
+    // Seed the random number generator
+    rand.Seed(time.Now().UnixNano())
+
+    // Generate a random number between 1 and 100
+    randomNum := rand.Intn(100) + 1 // rand.Intn(100) generates a number from 0 to 99
+
+    // Print the random number
+    fmt.Println("Random Number:", randomNum)
+}
\ No newline at end of file
diff --git a/llama/go_compiler/should_not_compile b/llama/go_compiler/should_not_compile
new file mode 100644
index 0000000..8c5b2be
--- /dev/null
+++ b/llama/go_compiler/should_not_compile
@@ -0,0 +1,8 @@
+package main
+
+import "fmt"
+
+func main() {
+	fmt.Println("Hello, World!")
+	}
+}
\ No newline at end of file
diff --git a/llama/main.go b/llama/main.go
index 7e2b774..742d82f 100644
--- a/llama/main.go
+++ b/llama/main.go
@@ -3,9 +3,9 @@ package main
 import (
 	"bufio"
 	"fmt"
-	"llama/compiler"
 	displayindicator "llama/display-indicator"
 	"llama/extraction"
+	"llama/go_compiler"
 	ollamaimplementation "llama/ollama-implementation"
 	"os"
 	"strings"
@@ -58,14 +58,14 @@ func main() {
 
 		fmt.Println("Ollama's response:", generatedCode)
 
-		output, err := compiler.CompileStringToGo(generatedCode)
+		output, err := go_compiler.NewGoCompiler().CheckCompileErrors([]byte(generatedCode))
 
 		if err != nil {
-			userPrompt = output + "\nFollowing are the errors, please fix the code. Write it again, and write only source code along with same test cases with no further explanation. The format should be ```rust <yourcode + testcases> ```"
+			fmt.Printf("The code did not compile and contains the following errors: %v\n", string(output))
+			userPrompt = "Following are the errors, please fix the code. Write it again, and write only source code along with same test cases with no further explanation. The format should be ```rust <yourcode + testcases> ```  :\n" +  string(output) 
 		} else {
-			fmt.Printf("Compiled successfully. Here is the output: %v", output)
+			fmt.Printf("Compiled successfully. Here is the output: %v", string(output))
 			userPrompt = "exit"
 		}
-
 	}
 }
-- 
GitLab