update test suit and enhance render image
parent
2c07b8518e
commit
bdc0f58abd
|
|
@ -183,6 +183,8 @@ body {
|
|||
cursor: pointer;
|
||||
transition: transform 0.3s ease;
|
||||
object-fit: contain; /* Ensures the entire image is visible within its bounds */
|
||||
image-rendering: -webkit-optimize-contrast; /* Optimizes rendering for better performance */
|
||||
image-rendering: crisp-edges; /* Better rendering for scaled images */
|
||||
}
|
||||
|
||||
.lesson-content img.zoomable-img {
|
||||
|
|
|
|||
|
|
@ -355,8 +355,8 @@
|
|||
});
|
||||
}
|
||||
|
||||
// Image zoom functionality for home page
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Dynamic image sizing and zoom functionality for home page
|
||||
function setupHomeImageHandling() {
|
||||
// Select all images inside home content
|
||||
const homeImages = document.querySelectorAll('.home-content img');
|
||||
|
||||
|
|
@ -366,6 +366,19 @@
|
|||
img.classList.add('zoomable-img');
|
||||
}
|
||||
|
||||
// Add load event to handle image sizing
|
||||
if (!img.complete) {
|
||||
img.addEventListener('load', function() {
|
||||
// Image loaded, ensure it fits container
|
||||
this.style.maxWidth = '100%';
|
||||
this.style.height = 'auto';
|
||||
});
|
||||
} else {
|
||||
// Image already loaded
|
||||
img.style.maxWidth = '100%';
|
||||
img.style.height = 'auto';
|
||||
}
|
||||
|
||||
img.addEventListener('click', function() {
|
||||
// Toggle enlarged class to zoom in/out
|
||||
this.classList.toggle('enlarged');
|
||||
|
|
@ -385,7 +398,10 @@
|
|||
enlargedImg.classList.remove('enlarged');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Call the function to set up image handling
|
||||
setupHomeImageHandling();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
|
|
|||
|
|
@ -997,21 +997,39 @@
|
|||
});
|
||||
}
|
||||
|
||||
// Image zoom functionality
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Dynamic image sizing and zoom functionality
|
||||
function setupImageHandling() {
|
||||
// Select all images inside lesson content
|
||||
const lessonImages = document.querySelectorAll('.lesson-content img');
|
||||
|
||||
lessonImages.forEach(img => {
|
||||
// Add zoomable-img class to make them zoomable
|
||||
img.classList.add('zoomable-img');
|
||||
if (!img.classList.contains('zoomable-img')) {
|
||||
img.classList.add('zoomable-img');
|
||||
}
|
||||
|
||||
// Add load event to handle image sizing
|
||||
if (!img.complete) {
|
||||
img.addEventListener('load', function() {
|
||||
// Image loaded, ensure it fits container
|
||||
this.style.maxWidth = '100%';
|
||||
this.style.height = 'auto';
|
||||
});
|
||||
} else {
|
||||
// Image already loaded
|
||||
img.style.maxWidth = '100%';
|
||||
img.style.height = 'auto';
|
||||
}
|
||||
|
||||
img.addEventListener('click', function() {
|
||||
// Toggle enlarged class to zoom in/out
|
||||
this.classList.toggle('enlarged');
|
||||
|
||||
// Prevent default behavior to avoid any conflicts
|
||||
event.preventDefault();
|
||||
if (event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -1022,7 +1040,10 @@
|
|||
enlargedImg.classList.remove('enlarged');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Call the function to set up image handling
|
||||
setupImageHandling();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
|
|
|||
|
|
@ -1,22 +0,0 @@
|
|||
FROM python:3.11-slim
|
||||
|
||||
# Install gcc compiler for C code compilation (needed for communication with LMS)
|
||||
RUN apt-get update && \
|
||||
apt-get install -y gcc build-essential && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /locust
|
||||
|
||||
# Install dependencies
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
# Copy locustfile
|
||||
COPY locustfile.py .
|
||||
|
||||
# Expose port for Locust web interface
|
||||
EXPOSE 8089
|
||||
|
||||
# Command to run Locust
|
||||
CMD ["locust", "-f", "locustfile.py"]
|
||||
|
|
@ -1,105 +1,509 @@
|
|||
from locust import HttpUser, TaskSet, task, between
|
||||
"""
|
||||
Load testing script for C Programming Learning Management System using Locust
|
||||
"""
|
||||
|
||||
import os
|
||||
import random
|
||||
import csv
|
||||
import glob
|
||||
from locust import HttpUser, TaskSet, task, between
|
||||
import json
|
||||
|
||||
class CProgrammingLMSUser(HttpUser):
|
||||
|
||||
def get_number_of_students():
|
||||
"""
|
||||
Load testing for C Programming Learning Management System
|
||||
Get the number of students from tokens_siswa.csv
|
||||
"""
|
||||
wait_time = between(1, 3)
|
||||
|
||||
tokens_file = '/mnt/locust/tokens_siswa.csv'
|
||||
num_students = 1 # Default to 1 if file doesn't exist or is empty
|
||||
|
||||
if os.path.exists(tokens_file):
|
||||
with open(tokens_file, 'r', newline='', encoding='utf-8') as csvfile:
|
||||
reader = csv.DictReader(csvfile, delimiter=';')
|
||||
# Count the number of data rows (excluding header)
|
||||
num_students = sum(1 for row in reader)
|
||||
|
||||
return max(1, num_students) # Ensure at least 1 student
|
||||
|
||||
|
||||
class LMSCUserBehavior(TaskSet):
|
||||
"""
|
||||
Define user behavior for the LMS-C application
|
||||
"""
|
||||
|
||||
def on_start(self):
|
||||
"""
|
||||
Called when a user starts
|
||||
Initialize user session with a token from tokens_siswa.csv
|
||||
"""
|
||||
# Get a list of available lessons to choose from
|
||||
self.lessons = []
|
||||
response = self.client.get("/")
|
||||
if response.status_code == 200:
|
||||
# In a real implementation, we would parse the response to get lesson URLs
|
||||
# For now, we'll use a predefined list of lesson paths
|
||||
self.lessons = [
|
||||
"introduction_to_c.md",
|
||||
"variables_and_data_types.md"
|
||||
]
|
||||
# Read all student data from tokens_siswa.csv
|
||||
tokens_file = '/mnt/locust/tokens_siswa.csv'
|
||||
all_students = []
|
||||
|
||||
if os.path.exists(tokens_file):
|
||||
with open(tokens_file, 'r', newline='', encoding='utf-8') as csvfile:
|
||||
reader = csv.DictReader(csvfile, delimiter=';')
|
||||
all_students = list(reader) # Convert to list to get all rows
|
||||
|
||||
# Select a random student from the CSV
|
||||
if all_students:
|
||||
selected_student = random.choice(all_students)
|
||||
self.student_token = selected_student.get('token', f"STUDENT_TOKEN_{random.randint(1000, 9999)}")
|
||||
self.student_name = selected_student.get('nama_siswa', f"Student_{random.randint(1000, 9999)}")
|
||||
else:
|
||||
# Fallback if no students in CSV
|
||||
self.student_token = f"STUDENT_TOKEN_{random.randint(1000, 9999)}"
|
||||
self.student_name = f"Student_{random.randint(1000, 9999)}"
|
||||
|
||||
# Register the student in the system
|
||||
register_payload = {
|
||||
"token": self.student_token,
|
||||
"student_name": self.student_name
|
||||
}
|
||||
|
||||
# Try to register the student (this might fail if the token already exists, which is fine)
|
||||
try:
|
||||
self.client.post("/validate-token", json=register_payload)
|
||||
except:
|
||||
pass # If validation fails, we'll continue anyway
|
||||
|
||||
# Read lesson files from content directory
|
||||
content_dir = '/mnt/locust/content' # Path relative to where locust runs in container
|
||||
self.lesson_files = []
|
||||
|
||||
if os.path.exists(content_dir):
|
||||
lesson_paths = glob.glob(os.path.join(content_dir, "*.md"))
|
||||
self.lesson_files = [os.path.basename(path) for path in lesson_paths if os.path.isfile(path)]
|
||||
|
||||
# No fallback to example_content since it won't be on the server
|
||||
|
||||
@task(3)
|
||||
def view_home_page(self):
|
||||
def view_homepage(self):
|
||||
"""
|
||||
Task to view the home page
|
||||
Task to view the homepage with lessons list
|
||||
"""
|
||||
self.client.get("/")
|
||||
|
||||
@task(5)
|
||||
@task(2)
|
||||
def view_lesson(self):
|
||||
"""
|
||||
Task to view a random lesson
|
||||
Task to view a specific lesson
|
||||
"""
|
||||
if self.lessons:
|
||||
lesson = random.choice(self.lessons)
|
||||
self.client.get(f"/lesson/{lesson}")
|
||||
if self.lesson_files:
|
||||
# Randomly select a lesson to view from available lessons
|
||||
lesson = random.choice(self.lesson_files)
|
||||
self.client.get(f"/lesson/{lesson}?token={self.student_token}")
|
||||
else:
|
||||
# Visit homepage instead of falling back to example content
|
||||
self.client.get(f"/?token={self.student_token}")
|
||||
|
||||
@task(2)
|
||||
def compile_c_code(self):
|
||||
@task(1)
|
||||
def login_student(self):
|
||||
"""
|
||||
Task to compile C code using the API
|
||||
Task to simulate student login
|
||||
"""
|
||||
# Sample C code for testing
|
||||
c_code = """
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
printf("Hello, World!\\n");
|
||||
return 0;
|
||||
login_payload = {
|
||||
"token": self.student_token
|
||||
}
|
||||
"""
|
||||
|
||||
response = self.client.post(
|
||||
"/compile",
|
||||
data={"code": c_code},
|
||||
headers={"Content-Type": "application/x-www-form-urlencoded"}
|
||||
)
|
||||
response = self.client.post("/login", json=login_payload)
|
||||
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
if data.get("success"):
|
||||
print(f"Successfully logged in student: {data.get('student_name')}")
|
||||
|
||||
@task(4)
|
||||
def compile_code(self):
|
||||
"""
|
||||
Task to compile and run code (universal for any supported language)
|
||||
"""
|
||||
# Determine the programming language from environment or default to 'c'
|
||||
programming_language = os.environ.get('DEFAULT_PROGRAMMING_LANGUAGE', 'c')
|
||||
|
||||
# Get code from lesson content or use default samples
|
||||
code = self.get_code_from_lessons(programming_language)
|
||||
|
||||
if not code:
|
||||
# Fallback to sample codes if no lesson content is available
|
||||
code = self.get_default_sample_code(programming_language)
|
||||
|
||||
# Prepare payload for compilation
|
||||
compile_payload = {
|
||||
"code": code,
|
||||
"language": programming_language
|
||||
}
|
||||
|
||||
# Send POST request to compile endpoint
|
||||
with self.client.post("/compile",
|
||||
json=compile_payload,
|
||||
catch_response=True) as response:
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
if result.get("success"):
|
||||
response.success()
|
||||
else:
|
||||
response.failure(f"Compilation failed: {result.get('error', 'Unknown error')}")
|
||||
else:
|
||||
response.failure(f"HTTP {response.status_code}: Request failed")
|
||||
|
||||
def get_code_from_lessons(self, language):
|
||||
"""
|
||||
Extract code from lesson content using markers
|
||||
"""
|
||||
if not self.lesson_files:
|
||||
return None
|
||||
|
||||
# Select a random lesson to extract code from
|
||||
lesson_file = random.choice(self.lesson_files)
|
||||
lesson_path = f'/mnt/locust/content/{lesson_file}'
|
||||
|
||||
if not os.path.exists(lesson_path):
|
||||
return None
|
||||
|
||||
try:
|
||||
with open(lesson_path, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
|
||||
# Look for INITIAL_CODE and SOLUTION_CODE sections
|
||||
initial_code = self.extract_code_section(content, '---INITIAL_CODE---', '---END_INITIAL_CODE---')
|
||||
solution_code = self.extract_code_section(content, '---SOLUTION_CODE---', '---END_SOLUTION_CODE---')
|
||||
|
||||
# Choose randomly between initial and solution code if both exist
|
||||
available_codes = [code for code in [initial_code, solution_code] if code]
|
||||
|
||||
if available_codes:
|
||||
return random.choice(available_codes)
|
||||
except Exception:
|
||||
pass # If there's an error reading the file, return None
|
||||
|
||||
return None
|
||||
|
||||
def extract_code_section(self, content, start_marker, end_marker):
|
||||
"""
|
||||
Extract code between start and end markers
|
||||
"""
|
||||
start_idx = content.find(start_marker)
|
||||
end_idx = content.find(end_marker)
|
||||
|
||||
if start_idx != -1 and end_idx != -1 and end_idx > start_idx:
|
||||
start_pos = start_idx + len(start_marker)
|
||||
extracted_code = content[start_pos:end_idx].strip()
|
||||
return extracted_code
|
||||
|
||||
return None
|
||||
|
||||
def get_default_sample_code(self, language):
|
||||
"""
|
||||
Get default sample code based on the programming language
|
||||
"""
|
||||
if language.lower() == 'c':
|
||||
sample_codes = [
|
||||
'''#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
printf("Hello, World!\\n");
|
||||
return 0;
|
||||
}''',
|
||||
'''#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
int a = 5, b = 10;
|
||||
int sum = a + b;
|
||||
printf("Sum is: %d\\n", sum);
|
||||
return 0;
|
||||
}''',
|
||||
'''#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
int i;
|
||||
for(i = 0; i < 5; i++) {
|
||||
printf("Count: %d\\n", i);
|
||||
}
|
||||
return 0;
|
||||
}'''
|
||||
]
|
||||
elif language.lower() == 'python':
|
||||
sample_codes = [
|
||||
'''print("Hello, World!")''',
|
||||
'''
|
||||
name = "Locust"
|
||||
print(f"Hello, {name}!")''',
|
||||
'''
|
||||
numbers = [1, 2, 3, 4, 5]
|
||||
for num in numbers:
|
||||
print(f"Number: {num}")
|
||||
'''
|
||||
]
|
||||
else:
|
||||
# Default to C if language is not recognized
|
||||
sample_codes = [
|
||||
'''#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
printf("Hello, World!\\n");
|
||||
return 0;
|
||||
}'''
|
||||
]
|
||||
|
||||
return random.choice(sample_codes)
|
||||
|
||||
@task(1)
|
||||
def validate_token(self):
|
||||
"""
|
||||
Task to validate a student token
|
||||
Task to validate student token
|
||||
"""
|
||||
# Use a random token for testing
|
||||
token = f"token_{random.randint(1000, 9999)}"
|
||||
validate_payload = {
|
||||
"token": self.student_token
|
||||
}
|
||||
|
||||
response = self.client.post(
|
||||
"/validate-token",
|
||||
json={"token": token}
|
||||
)
|
||||
|
||||
@task(1)
|
||||
def login_with_token(self):
|
||||
"""
|
||||
Task to login with a student token
|
||||
"""
|
||||
# Use a random token for testing
|
||||
token = f"token_{random.randint(1000, 9999)}"
|
||||
|
||||
response = self.client.post(
|
||||
"/login",
|
||||
json={"token": token}
|
||||
)
|
||||
self.client.post("/validate-token", json=validate_payload)
|
||||
|
||||
@task(1)
|
||||
def track_progress(self):
|
||||
"""
|
||||
Task to track student progress
|
||||
Task to track student progress for a lesson
|
||||
"""
|
||||
# Use a random token and lesson for testing
|
||||
token = f"token_{random.randint(1000, 9999)}"
|
||||
lesson_name = random.choice(["introduction_to_c", "variables_and_data_types"])
|
||||
|
||||
response = self.client.post(
|
||||
"/track-progress",
|
||||
json={
|
||||
"token": token,
|
||||
"lesson_name": lesson_name,
|
||||
"status": "completed"
|
||||
}
|
||||
)
|
||||
if self.lesson_files:
|
||||
# Select a random lesson from available lessons, removing .md extension for lesson_name
|
||||
lesson_file = random.choice(self.lesson_files)
|
||||
lesson_name = lesson_file.replace('.md', '')
|
||||
else:
|
||||
# Don't track progress if no lessons are available
|
||||
return
|
||||
|
||||
progress_payload = {
|
||||
"token": self.student_token,
|
||||
"lesson_name": lesson_name,
|
||||
"status": "completed"
|
||||
}
|
||||
|
||||
self.client.post("/track-progress", json=progress_payload)
|
||||
|
||||
|
||||
class WebsiteUser(HttpUser):
|
||||
"""
|
||||
Main user class for the LMS-C load test
|
||||
"""
|
||||
tasks = [LMSCUserBehavior]
|
||||
|
||||
# Wait time between tasks (1-3 seconds)
|
||||
wait_time = between(1, 3)
|
||||
|
||||
def on_start(self):
|
||||
"""
|
||||
Setup for each user when they start
|
||||
"""
|
||||
# Perform initial homepage visit
|
||||
self.client.get("/")
|
||||
|
||||
def on_stop(self):
|
||||
"""
|
||||
Cleanup when user stops
|
||||
"""
|
||||
# Logout the user
|
||||
try:
|
||||
self.client.post("/logout", json={})
|
||||
except:
|
||||
pass # Ignore logout errors
|
||||
|
||||
|
||||
# Additional task sets for more complex behaviors
|
||||
|
||||
class LessonNavigationTaskSet(TaskSet):
|
||||
"""
|
||||
Task set focused on navigating through lessons
|
||||
"""
|
||||
|
||||
def on_start(self):
|
||||
"""
|
||||
Initialize lesson navigation with available lessons
|
||||
"""
|
||||
# Read lesson files from content directory
|
||||
content_dir = '/mnt/locust/content' # Path relative to where locust runs in container
|
||||
self.lesson_files = []
|
||||
|
||||
if os.path.exists(content_dir):
|
||||
lesson_paths = glob.glob(os.path.join(content_dir, "*.md"))
|
||||
self.lesson_files = [os.path.basename(path) for path in lesson_paths if os.path.isfile(path)]
|
||||
|
||||
# No fallback to example_content since it won't be on the server
|
||||
|
||||
@task(5)
|
||||
def browse_lessons(self):
|
||||
"""
|
||||
Browse through different lessons
|
||||
"""
|
||||
if self.lesson_files:
|
||||
for lesson in self.lesson_files:
|
||||
self.client.get(f"/lesson/{lesson}")
|
||||
self.wait()
|
||||
else:
|
||||
# No fallback since example_content won't be on the server
|
||||
# Just visit the homepage if no lessons are found
|
||||
self.client.get("/")
|
||||
self.wait()
|
||||
|
||||
@task(2)
|
||||
def go_back_to_home(self):
|
||||
"""
|
||||
Navigate back to home page
|
||||
"""
|
||||
self.client.get("/")
|
||||
|
||||
@task(1)
|
||||
def stop_browsing(self):
|
||||
"""
|
||||
Stop browsing and potentially switch to another task set
|
||||
"""
|
||||
self.interrupt()
|
||||
|
||||
|
||||
class CompilationFocusedTaskSet(TaskSet):
|
||||
"""
|
||||
Task set focused on code compilation activities
|
||||
"""
|
||||
|
||||
def on_start(self):
|
||||
"""
|
||||
Initialize with lesson files for code extraction
|
||||
"""
|
||||
# Read lesson files from content directory
|
||||
content_dir = '/mnt/locust/content' # Path relative to where locust runs in container
|
||||
self.lesson_files = []
|
||||
|
||||
if os.path.exists(content_dir):
|
||||
lesson_paths = glob.glob(os.path.join(content_dir, "*.md"))
|
||||
self.lesson_files = [os.path.basename(path) for path in lesson_paths if os.path.isfile(path)]
|
||||
|
||||
# No fallback to example_content since it won't be on the server
|
||||
|
||||
@task(10)
|
||||
def compile_different_codes(self):
|
||||
"""
|
||||
Compile various programs based on the default language or from lesson content
|
||||
"""
|
||||
# Determine the programming language from environment or default to 'c'
|
||||
programming_language = os.environ.get('DEFAULT_PROGRAMMING_LANGUAGE', 'c')
|
||||
|
||||
# Get code from lesson content or use default samples
|
||||
code = self.get_code_from_lessons(programming_language)
|
||||
|
||||
if not code:
|
||||
# Fallback to sample codes if no lesson content is available
|
||||
code = self.get_default_sample_code(programming_language)
|
||||
|
||||
response = self.client.post("/compile", json={
|
||||
"code": code,
|
||||
"language": programming_language
|
||||
})
|
||||
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
if not result.get("success"):
|
||||
print(f"Compilation error: {result.get('error')}")
|
||||
|
||||
def get_code_from_lessons(self, language):
|
||||
"""
|
||||
Extract code from lesson content using markers
|
||||
"""
|
||||
if not self.lesson_files:
|
||||
return None
|
||||
|
||||
# Select a random lesson to extract code from
|
||||
lesson_file = random.choice(self.lesson_files)
|
||||
lesson_path = f'/mnt/locust/content/{lesson_file}'
|
||||
|
||||
if not os.path.exists(lesson_path):
|
||||
return None
|
||||
|
||||
try:
|
||||
with open(lesson_path, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
|
||||
# Look for INITIAL_CODE and SOLUTION_CODE sections
|
||||
initial_code = self.extract_code_section(content, '---INITIAL_CODE---', '---END_INITIAL_CODE---')
|
||||
solution_code = self.extract_code_section(content, '---SOLUTION_CODE---', '---END_SOLUTION_CODE---')
|
||||
|
||||
# Choose randomly between initial and solution code if both exist
|
||||
available_codes = [code for code in [initial_code, solution_code] if code]
|
||||
|
||||
if available_codes:
|
||||
return random.choice(available_codes)
|
||||
except Exception:
|
||||
pass # If there's an error reading the file, return None
|
||||
|
||||
return None
|
||||
|
||||
def extract_code_section(self, content, start_marker, end_marker):
|
||||
"""
|
||||
Extract code between start and end markers
|
||||
"""
|
||||
start_idx = content.find(start_marker)
|
||||
end_idx = content.find(end_marker)
|
||||
|
||||
if start_idx != -1 and end_idx != -1 and end_idx > start_idx:
|
||||
start_pos = start_idx + len(start_marker)
|
||||
extracted_code = content[start_pos:end_idx].strip()
|
||||
return extracted_code
|
||||
|
||||
return None
|
||||
|
||||
def get_default_sample_code(self, language):
|
||||
"""
|
||||
Get default sample code based on the programming language
|
||||
"""
|
||||
if language.lower() == 'c':
|
||||
sample_codes = [
|
||||
'''#include <stdio.h>
|
||||
int main() { printf("Simple Hello World\\n"); return 0; }''',
|
||||
'''#include <stdio.h>
|
||||
int main() {
|
||||
int arr[] = {1, 2, 3, 4, 5};
|
||||
int i;
|
||||
for(i = 0; i < 5; i++) {
|
||||
printf("%d ", arr[i]);
|
||||
}
|
||||
printf("\\n");
|
||||
return 0;
|
||||
}''',
|
||||
'''#include <stdio.h>
|
||||
int factorial(int n) {
|
||||
if(n <= 1) return 1;
|
||||
return n * factorial(n-1);
|
||||
}
|
||||
int main() {
|
||||
int num = 5;
|
||||
printf("Factorial of %d is %d\\n", num, factorial(num));
|
||||
return 0;
|
||||
}'''
|
||||
]
|
||||
elif language.lower() == 'python':
|
||||
sample_codes = [
|
||||
'''print("Hello, World!")''',
|
||||
'''
|
||||
name = "Locust"
|
||||
print(f"Hello, {name}!")''',
|
||||
'''
|
||||
numbers = [1, 2, 3, 4, 5]
|
||||
for num in numbers:
|
||||
print(f"Number: {num}")
|
||||
'''
|
||||
]
|
||||
else:
|
||||
# Default to C if language is not recognized
|
||||
sample_codes = [
|
||||
'''#include <stdio.h>
|
||||
int main() { printf("Simple Hello World\\n"); return 0; }'''
|
||||
]
|
||||
|
||||
return random.choice(sample_codes)
|
||||
|
||||
|
||||
class AdvancedUser(HttpUser):
|
||||
"""
|
||||
Advanced user that performs more complex behaviors
|
||||
"""
|
||||
weight = 2 # Twice as likely to be chosen as WebsiteUser
|
||||
tasks = {LessonNavigationTaskSet: 2, CompilationFocusedTaskSet: 3, LMSCUserBehavior: 4}
|
||||
|
||||
wait_time = between(0.5, 2)
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
version: '3.8'
|
||||
|
||||
services:
|
||||
locust:
|
||||
build: .
|
||||
ports:
|
||||
- "8089:8089"
|
||||
volumes:
|
||||
- ./:/locust
|
||||
command: ["locust", "-f", "locustfile.py", "--host", "http://<LMS_SERVER_IP>:5000"]
|
||||
|
||||
networks:
|
||||
default:
|
||||
driver: bridge
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
version: '3.8'
|
||||
|
||||
services:
|
||||
master:
|
||||
container_name: locust_master
|
||||
image: locustio/locust
|
||||
ports:
|
||||
- "8089:8089"
|
||||
volumes:
|
||||
- ../../:/mnt/locust
|
||||
environment:
|
||||
# URL target default (bisa diganti saat run)
|
||||
- LOCUST_HOST=${TARGET_URL:-http://example.com}
|
||||
# Number of students to simulate - defaults to 10 but should match number in tokens_siswa.csv
|
||||
- LOCUST_NUM_STUDENTS=${LOCUST_NUM_STUDENTS:-10}
|
||||
command: -f /mnt/locust/elemes/test/locustfile.py --master
|
||||
|
||||
worker:
|
||||
image: locustio/locust
|
||||
volumes:
|
||||
- ../../:/mnt/locust
|
||||
environment:
|
||||
- LOCUST_HOST=${TARGET_URL:-http://example.com}
|
||||
# Number of students to simulate - defaults to 10 but should match number in tokens_siswa.csv
|
||||
- LOCUST_NUM_STUDENTS=${LOCUST_NUM_STUDENTS:-10}
|
||||
# Worker akan otomatis mendeteksi Master lewat nama service 'master'
|
||||
command: -f /mnt/locust/elemes/test/locustfile.py --worker --master-host locust_master
|
||||
|
||||
|
|
@ -1 +0,0 @@
|
|||
locust==2.29.1
|
||||
|
|
@ -1,101 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Test script for LMS-C Production Environment
|
||||
|
||||
echo "Starting LMS-C Production Environment Tests..."
|
||||
|
||||
# Start the application
|
||||
echo "Starting the application..."
|
||||
cd ..
|
||||
./start.sh
|
||||
cd test
|
||||
|
||||
# Wait for the application to start
|
||||
echo "Waiting for application to start..."
|
||||
sleep 10
|
||||
|
||||
# Test 1: Check if the home page loads and displays lessons
|
||||
echo "Test 1: Checking home page and lesson display..."
|
||||
HOME_PAGE_CHECK=$(curl -s http://localhost:5000/ | grep -i "Available Lessons" | wc -l)
|
||||
if [ $HOME_PAGE_CHECK -gt 0 ]; then
|
||||
echo "✓ Test 1 PASSED: Home page displays lessons correctly"
|
||||
else
|
||||
echo "✗ Test 1 FAILED: Home page does not display lessons"
|
||||
fi
|
||||
|
||||
# Test 2: Check if specific lesson pages load
|
||||
echo "Test 2: Checking lesson page loading..."
|
||||
LESSON_PAGE_CHECK=$(curl -s http://localhost:5000/lesson/welcome.md | grep -i "C Programming Learning System" | wc -l)
|
||||
if [ $LESSON_PAGE_CHECK -gt 0 ]; then
|
||||
echo "✓ Test 2 PASSED: Lesson pages load correctly"
|
||||
else
|
||||
echo "✗ Test 2 FAILED: Lesson pages do not load"
|
||||
fi
|
||||
|
||||
# Test 3: Test the code compilation API
|
||||
echo "Test 3: Testing code compilation API..."
|
||||
COMPILATION_TEST=$(curl -s -X POST http://localhost:5000/compile \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"code":"#include <stdio.h>\n\nint main() {\n printf(\"Hello, World!\\n\");\n return 0;\n}"}')
|
||||
|
||||
# Check if the response contains success: true and the expected output
|
||||
if echo "$COMPILATION_TEST" | grep -q '"success":true' && echo "$COMPILATION_TEST" | grep -q "Hello, World!"; then
|
||||
echo "✓ Test 3 PASSED: Code compilation API works correctly"
|
||||
else
|
||||
echo "✗ Test 3 FAILED: Code compilation API not working"
|
||||
echo " Response: $COMPILATION_TEST"
|
||||
fi
|
||||
|
||||
# Test 4: Test with a more complex C program
|
||||
echo "Test 4: Testing complex code compilation..."
|
||||
COMPLEX_CODE='{
|
||||
"code": "#include <stdio.h>\nint main() {\n int i, sum = 0;\n for (i = 1; i <= 5; i++) {\n sum += i;\n }\n printf(\"Sum of 1 to 5 is: %d\\n\", sum);\n return 0;\n}"
|
||||
}'
|
||||
|
||||
COMPLEX_TEST=$(curl -s -X POST http://localhost:5000/compile \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$COMPLEX_CODE")
|
||||
|
||||
if echo "$COMPLEX_TEST" | grep -q '"success":true' && echo "$COMPLEX_TEST" | grep -q "Sum of 1 to 5"; then
|
||||
echo "✓ Test 4 PASSED: Complex code compilation works correctly"
|
||||
else
|
||||
echo "✗ Test 4 FAILED: Complex code compilation not working"
|
||||
echo " Response: $COMPLEX_TEST"
|
||||
fi
|
||||
|
||||
# Test 5: Check if all expected lesson files are accessible
|
||||
echo "Test 5: Checking lesson accessibility..."
|
||||
LESSON_FILES=("welcome.md" "hello_world.md" "introduction_to_c.md" "arrays.md")
|
||||
ALL_LESSONS_ACCESSIBLE=true
|
||||
|
||||
for lesson in "${LESSON_FILES[@]}"; do
|
||||
lesson_check=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:5000/lesson/$lesson")
|
||||
if [ "$lesson_check" -ne 200 ]; then
|
||||
echo "✗ Lesson $lesson returned HTTP $lesson_check"
|
||||
ALL_LESSONS_ACCESSIBLE=false
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$ALL_LESSONS_ACCESSIBLE" = true ]; then
|
||||
echo "✓ Test 5 PASSED: All test lessons are accessible"
|
||||
else
|
||||
echo "✗ Test 5 FAILED: Some lessons are not accessible"
|
||||
fi
|
||||
|
||||
# Test 6: Check for errors in container logs
|
||||
echo "Test 6: Checking container logs for errors..."
|
||||
LOG_ERRORS=$(podman logs elemes_lms-c_1 2>&1 | grep -i error | wc -l)
|
||||
if [ $LOG_ERRORS -eq 0 ]; then
|
||||
echo "✓ Test 6 PASSED: No errors found in container logs"
|
||||
else
|
||||
echo "✗ Test 6 FAILED: Errors found in container logs"
|
||||
podman logs elemes_lms-c_1 | grep -i error
|
||||
fi
|
||||
|
||||
# Stop the application
|
||||
echo "Stopping the application..."
|
||||
cd ..
|
||||
./stop.sh
|
||||
cd test
|
||||
|
||||
echo "All tests completed!"
|
||||
Loading…
Reference in New Issue