diff --git a/test/LOAD_TESTING.md b/test/LOAD_TESTING.md new file mode 100644 index 0000000..fe23172 --- /dev/null +++ b/test/LOAD_TESTING.md @@ -0,0 +1,116 @@ +# Load Testing Documentation for C Programming Learning Management System + +## Overview +This document describes how to perform load testing on the C Programming Learning Management System to ensure it can handle at least 50 concurrent students accessing the application. + +## Requirements +- Python 3.7+ +- Locust (installed via pip) +- The LMS application running on your local machine or a test server + +## Setup + +### 1. Install Locust +```bash +pip install locust +``` + +Alternatively, install from the requirements file in the test directory: +```bash +cd /path/to/lms-c/test +pip install -r requirements.txt +``` + +### 2. Ensure the LMS Application is Running +Start your LMS application: +```bash +cd /path/to/lms-c +./start.sh +``` +Or if running directly: +```bash +python app.py +``` + +By default, the application runs on `http://localhost:5000`. + +## Running the Load Test + +### Basic Load Test +To run a load test simulating 50 users with a hatch rate of 2 users per second: +```bash +cd /path/to/lms-c/test +locust -f load_test.py --host=http://localhost:5000 +``` + +Then open your browser and go to `http://localhost:8089` to configure the load test. + +### Command Line Load Test +To run the load test directly from the command line without the web interface: +```bash +locust -f load_test.py --host=http://localhost:5000 --users=50 --spawn-rate=2 --run-time=5m --headless +``` + +This command will: +- Simulate 50 users (`--users=50`) +- Spawn 2 users per second (`--spawn-rate=2`) +- Run for 5 minutes (`--run-time=5m`) +- Run in headless mode without the web interface (`--headless`) + +## Test Scenarios + +The load test script simulates the following student behaviors: + +1. **Viewing the homepage** (30% of requests) - Students browsing available lessons +2. **Viewing lesson pages** (40% of requests) - Students reading lesson content +3. **Compiling code** (20% of requests) - Students submitting C code for compilation +4. **Logging in** (10% of requests) - Students authenticating with tokens +5. **Validating tokens** (10% of requests) - Students validating their access tokens +6. **Tracking progress** (10% of requests) - Students saving their lesson progress + +## Monitoring and Metrics + +During the load test, Locust provides the following metrics: + +- **Users**: Number of concurrent users +- **Failures**: Number of failed requests +- **Requests per second**: Average request rate +- **Average response time**: Mean response time across all requests +- **95th percentile response time**: Response time below which 95% of requests complete +- **99th percentile response time**: Response time below which 99% of requests complete +- **Total requests**: Total number of requests made during the test + +## Performance Benchmarks + +For the application to be considered capable of handling 50 students simultaneously, it should meet these benchmarks: + +- Less than 1% failure rate +- Average response time under 2 seconds +- 95th percentile response time under 5 seconds +- No significant degradation in performance as user count increases + +## Customizing the Load Test + +You can modify the `load_test.py` file to adjust: + +- The number of users simulated +- The distribution of different types of requests +- The wait time between requests +- The sample code used for compilation tests +- The tokens used for authentication + +## Troubleshooting + +### Common Issues: + +1. **Connection Refused**: Ensure the LMS application is running on the specified host/port +2. **High Failure Rate**: Check application logs for errors during the load test +3. **Memory Issues**: Monitor system resources during the test +4. **GCC Compilation Errors**: The application compiles C code, which may consume resources + +### Resource Monitoring: +Consider monitoring system resources (CPU, memory, disk I/O) during the test using tools like `htop`, `iostat`, or `vmstat`. + +## Conclusion + +This load testing setup allows you to verify that the C Programming Learning Management System can handle at least 50 concurrent students. Regular load testing should be performed, especially after major updates, to ensure the application continues to meet performance requirements. \ No newline at end of file diff --git a/test/load_test.py b/test/load_test.py new file mode 100644 index 0000000..81036e9 --- /dev/null +++ b/test/load_test.py @@ -0,0 +1,145 @@ +""" +Load testing script for C Programming Learning Management System +Using Locust to simulate 50+ concurrent students accessing the application +""" + +from locust import HttpUser, TaskSet, task, between +import random +import json + + +class StudentBehavior(TaskSet): + """ + Define the behavior of a student using the LMS + """ + + def on_start(self): + """Initialize the user session - potentially with a valid token""" + self.token = None + self.student_name = None + self.current_lesson = None + self.lessons = [] + + # Get available lessons + response = self.client.get("/") + if response.status_code == 200: + # In a real scenario, we would parse the response to get lesson names + # For now, we'll use a predefined list of possible lessons + self.lessons = [ + "variables.md", "loops.md", "functions.md", + "arrays.md", "pointers.md", "structures.md" + ] + + @task(3) + def view_homepage(self): + """View the main page with all lessons""" + self.client.get("/") + + @task(4) + def view_lesson(self): + """View a random lesson page""" + if self.lessons: + lesson = random.choice(self.lessons) + self.client.get(f"/lesson/{lesson}") + + @task(2) + def compile_code(self): + """Submit code for compilation (simulating exercise completion)""" + sample_codes = [ + '''#include +int main() { + printf("Hello, World!\\n"); + return 0; +}''', + '''#include +int main() { + int a = 5, b = 10; + printf("Sum: %d\\n", a + b); + return 0; +}''', + '''#include +int main() { + for(int i = 0; i < 5; i++) { + printf("Count: %d\\n", i); + } + return 0; +}''' + ] + + code = random.choice(sample_codes) + + response = self.client.post( + "/compile", + json={"code": code}, + headers={"Content-Type": "application/json"} + ) + + @task(1) + def login(self): + """Attempt to log in with a token""" + # Using a random token for testing - in a real scenario, + # we would use valid tokens from a pool + tokens_pool = [ + "STUDENT001", "STUDENT002", "STUDENT003", "STUDENT004", "STUDENT005", + "STUDENT006", "STUDENT007", "STUDENT008", "STUDENT009", "STUDENT010" + ] + + token = random.choice(tokens_pool) + + response = self.client.post( + "/login", + json={"token": token}, + headers={"Content-Type": "application/json"} + ) + + if response.status_code == 200: + try: + data = response.json() + if data.get("success"): + self.token = token + self.student_name = data.get("student_name") + except json.JSONDecodeError: + pass # Handle non-JSON responses + + @task(1) + def validate_token(self): + """Validate a token""" + tokens_pool = [ + "STUDENT001", "STUDENT002", "STUDENT003", "STUDENT004", "STUDENT005", + "STUDENT006", "STUDENT007", "STUDENT008", "STUDENT009", "STUDENT010" + ] + + token = random.choice(tokens_pool) + + response = self.client.post( + "/validate-token", + json={"token": token}, + headers={"Content-Type": "application/json"} + ) + + @task(1) + def track_progress(self): + """Track progress for a lesson (only if logged in)""" + if self.token and self.lessons: + lesson_name = random.choice(self.lessons).replace('.md', '') + + response = self.client.post( + "/track-progress", + json={ + "token": self.token, + "lesson_name": lesson_name, + "status": "completed" + }, + headers={"Content-Type": "application/json"} + ) + + +class WebsiteUser(HttpUser): + """ + Main user class for the load test + """ + tasks = [StudentBehavior] + wait_time = between(1, 5) # Wait between 1-5 seconds between requests + + # Host where the application is running + host = "http://localhost:5000" \ No newline at end of file diff --git a/test/requirements.txt b/test/requirements.txt new file mode 100644 index 0000000..ffce1f2 --- /dev/null +++ b/test/requirements.txt @@ -0,0 +1 @@ +locust==2.24.1 \ No newline at end of file