ready to test

This commit is contained in:
2025-07-30 12:56:32 +01:00
parent 88fa2fa2a3
commit e87c1f74f4
6 changed files with 202 additions and 48 deletions

View File

@@ -0,0 +1,83 @@
import socket
import time
def print_bytes(prefix, data):
"""Print raw bytes with both hex and ASCII representation"""
hex_data = ' '.join(f'{b:02x}' for b in data)
ascii_data = ''.join(chr(b) if 32 <= b <= 126 else '.' for b in data)
print(f"{prefix} (hex): {hex_data}")
print(f"{prefix} (ascii): {ascii_data}")
def create_client(name):
"""Create and connect a client, handle initial connection sequence"""
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('localhost', 40000))
# Receive welcome message
welcome = sock.recv(1024)
print(f"\n=== {name} receiving welcome message ===")
print_bytes("Received", welcome)
# Send username
username = f"{name}\n".encode('ascii')
print(f"\n=== {name} sending username ===")
print_bytes("Sending", username)
sock.send(username)
# Receive room info
room_info = sock.recv(1024)
print(f"\n=== {name} receiving room info ===")
print_bytes("Received", room_info)
return sock
def main():
# Create first client
print("\nConnecting client 1...")
client1 = create_client("Alice")
time.sleep(1) # Small delay between connections
# Create second client
print("\nConnecting client 2...")
client2 = create_client("Bob")
# Client 1 should receive notification about client 2
join_notification = client1.recv(1024)
print("\n=== Alice receiving Bob's join notification ===")
print_bytes("Received", join_notification)
# Client 1 sends a message
message1 = "Hello Bob!\n".encode('ascii')
print("\n=== Alice sending message ===")
print_bytes("Sending", message1)
client1.send(message1)
# Client 2 receives the message
received1 = client2.recv(1024)
print("\n=== Bob receiving message ===")
print_bytes("Received", received1)
# Client 2 sends a reply
message2 = "Hi Alice!\n".encode('ascii')
print("\n=== Bob sending message ===")
print_bytes("Sending", message2)
client2.send(message2)
# Client 1 receives the reply
received2 = client1.recv(1024)
print("\n=== Alice receiving message ===")
print_bytes("Received", received2)
# Close connections
print("\nClosing connections...")
client1.close()
received1 = client2.recv(1024)
print("\n=== Bob receiving message ===")
print_bytes("Received", received1)
client2.close()
if __name__ == "__main__":
main()

5
data.c
View File

@@ -99,6 +99,11 @@ char *char_array_get_until_char(char_array_t *array, char c) {
return ret; return ret;
}; };
void char_array_put_until_char(char *arr, char_array_t *array, char c) {
char *data = char_array_get_until_char(array, c);
memcpy(arr, data, strlen(data));
}
char *char_array_get_bytes(char_array_t *array, size_t length) { char *char_array_get_bytes(char_array_t *array, size_t length) {
if (length > array->size) { if (length > array->size) {
return NULL; return NULL;

1
data.h
View File

@@ -22,6 +22,7 @@ void char_array_print_hex(const char_array_t *array);
void char_array_wipe(char_array_t *array); void char_array_wipe(char_array_t *array);
bool char_array_has_char(char_array_t *array, char c); bool char_array_has_char(char_array_t *array, char c);
char *char_array_get_until_char(char_array_t *array, char c); char *char_array_get_until_char(char_array_t *array, char c);
void char_array_put_until_char(char *arr, char_array_t *array, char c);
char *char_array_get_bytes(char_array_t *array, size_t length); char *char_array_get_bytes(char_array_t *array, size_t length);
void char_array_shift_bytes(char_array_t *array, size_t length); void char_array_shift_bytes(char_array_t *array, size_t length);

View File

@@ -30,7 +30,7 @@ int main() {
handle_args_t *args = malloc(sizeof(handle_args_t)); handle_args_t *args = malloc(sizeof(handle_args_t));
args->client = client; args->client = client;
args->connection = connection_number++; args->connection = connection_number++;
args->connections = *connections; args->connections = connections;
pthread_t thread; pthread_t thread;
pthread_create(&thread, nullptr, handle_connection, args); pthread_create(&thread, nullptr, handle_connection, args);
} }
@@ -46,50 +46,67 @@ void *handle_connection(void *args) {
char *message = malloc(sizeof(char)*1024); char *message = malloc(sizeof(char)*1024);
char_array_t *data = char_array_create(1024); char_array_t *data = char_array_create(1024);
send(handleArgs->client, WELCOME, sizeof(WELCOME), 0); send(handleArgs->client, WELCOME, sizeof(WELCOME), 0);
char *username = malloc(sizeof(char)*1024);
bytesReceived = recv(handleArgs->client, buffer, sizeof(buffer), 0);
char_array_append(data, buffer, bytesReceived);
char *username = char_array_get_until_char(data, '\n');
if (username == NULL) {
printf("{%d} Failed to parse username\n", handleArgs->connection);
connections_remove(&handleArgs->connections, handleArgs->client);
free(handleArgs);
pthread_exit(NULL);
exit(3);
}
username = trim(username);
if (!username_is_valid(username)) {
printf("{%d} Invalid username\n", handleArgs->connection);
connections_remove(&handleArgs->connections, handleArgs->client);
free(handleArgs);
pthread_exit(NULL);
exit(3);
}
pthread_mutex_lock(&mutex);
sprintf(message, "* The room contains: ");
for (int i = 0; i < handleArgs->connections.len; i++) {
strcat(message, handleArgs->connections.clients[i].username);
strcat(message, " ");
}
strcat(message, "\n");
send(handleArgs->client, message, strlen(message), 0);
pthread_mutex_unlock(&mutex);
connections_append(&handleArgs->connections, handleArgs->client, username);
sprintf(message, "* %s has entered the room\n", username);
broadcast(&handleArgs->connections, &handleArgs->client, message);
printf("{%d} Username: %s\n", handleArgs->connection, username);
while ((bytesReceived = recv(handleArgs->client, buffer, sizeof(buffer), 0)) > 0) { while ((bytesReceived = recv(handleArgs->client, buffer, sizeof(buffer), 0)) > 0) {
printf("{%d} Client sent: |%d| \n", handleArgs->connection, bytesReceived); printf("{%d} Client sent: |%d| \n", handleArgs->connection, bytesReceived);
char_array_append(data, buffer, bytesReceived); char_array_append(data, buffer, bytesReceived);
char *request; switch (status) {
while ((request = char_array_get_until_char(data, '\n')) != NULL) { case CONNECTED: {
sprintf(message, "[%s] %s\n", username, request); if ((username = char_array_get_until_char(data, '\n')) != NULL) {
printf("{%d} Username raw: %s\n", handleArgs->connection, username);
if (username == NULL) {
printf("{%d} Failed to parse username\n", handleArgs->connection);
closesocket(handleArgs->client);
free(handleArgs);
pthread_exit(NULL);
exit(3);
}
username = trim(username);
printf("{%d} Username after trim: %s\n", handleArgs->connection, username);
if (!username_is_valid(username)) {
printf("{%d} Invalid username\n", handleArgs->connection);
closesocket(handleArgs->client);
free(handleArgs);
pthread_exit(NULL);
exit(3);
}
pthread_mutex_lock(&mutex);
sprintf(message, "* The room contains: ");
for (int i = 0; i < handleArgs->connections->len; i++) {
strcat(message, handleArgs->connections->clients[i].username);
strcat(message, " ");
}
strcat(message, "\n");
send(handleArgs->client, message, strlen(message), 0);
pthread_mutex_unlock(&mutex);
connections_append(handleArgs->connections, handleArgs->client, username);
sprintf(message, "* %s has entered the room\n", username);
broadcast(handleArgs->connections, &handleArgs->client, message);
printf("{%d} Username: %s\n", handleArgs->connection, username);
status = IDENTIFIED;
}
break;
}
case IDENTIFIED: {
char *request;
while ((request = char_array_get_until_char(data, '\n')) != NULL) {
sprintf(message, "[%s] %s\n", username, request);
broadcast(handleArgs->connections, &handleArgs->client, message);
}
break;
}
} }
memset(buffer, 0, sizeof(buffer)); memset(buffer, 0, sizeof(buffer));
} }
sprintf(message, "* %s has left the room\n", username);
broadcast(handleArgs->connections, &handleArgs->client, message);
connections_remove(handleArgs->connections, handleArgs->client);
printf("{%d} Client disconnected\n", handleArgs->connection);
free(message); free(message);
free(args); free(args);
return NULL; return NULL;
@@ -120,8 +137,8 @@ void connections_append(connections_t *connections, SOCKET client, char *usernam
printf("Failed to allocate memory for array\n"); printf("Failed to allocate memory for array\n");
} }
} }
connections->clients[connections->size].client = client; connections->clients[connections->len].client = client;
connections->clients[connections->size].username = username; connections->clients[connections->len].username = username;
connections->len = new_len; connections->len = new_len;
printf("{%llu} Connection added\n", connections->size); printf("{%llu} Connection added\n", connections->size);
pthread_mutex_unlock(&mutex); pthread_mutex_unlock(&mutex);
@@ -129,7 +146,7 @@ void connections_append(connections_t *connections, SOCKET client, char *usernam
}; };
void connections_remove(connections_t *connections, SOCKET client) { void connections_remove(connections_t *connections, SOCKET client) {
if (connections == NULL) { if (connections == NULL || connections->clients == NULL) {
exit(1); exit(1);
} }
pthread_mutex_lock(&mutex); pthread_mutex_lock(&mutex);
@@ -141,15 +158,20 @@ void connections_remove(connections_t *connections, SOCKET client) {
for (int j = i; j < connections->size; j++) { for (int j = i; j < connections->size; j++) {
connections->clients[j] = connections->clients[j+1]; connections->clients[j] = connections->clients[j+1];
} }
connections->size--; connections->len--;
printf("{%llu} Connection removed\n", connections->size); printf("{%llu} Connection removed\n", connections->size);
pthread_mutex_unlock(&mutex); pthread_mutex_unlock(&mutex);
return; return;
} }
} }
pthread_mutex_unlock(&mutex);
printf("{%llu} Connection not found\n", connections->size);
} }
void broadcast(const connections_t *connections, const SOCKET *source, const char *message) { void broadcast(const connections_t *connections, const SOCKET *source, const char *message) {
if (connections == NULL || connections->clients == NULL) {
exit(1);
}
pthread_mutex_lock(&mutex); pthread_mutex_lock(&mutex);
for (size_t i = 0; i < connections->size; i++) { for (size_t i = 0; i < connections->size; i++) {
if (connections->clients[i].client != *source) { if (connections->clients[i].client != *source) {

View File

@@ -24,7 +24,7 @@ typedef struct Connections {
typedef struct handleArgs { typedef struct handleArgs {
int connection; int connection;
SOCKET client; SOCKET client;
connections_t connections; connections_t *connections;
} handle_args_t; } handle_args_t;
typedef enum Status { typedef enum Status {

View File

@@ -2,6 +2,7 @@ import socket
import threading import threading
import time import time
import unittest import unittest
import sys
class TestBudgetChat(unittest.TestCase): class TestBudgetChat(unittest.TestCase):
SERVER_HOST = 'localhost' SERVER_HOST = 'localhost'
@@ -39,9 +40,11 @@ class TestBudgetChat(unittest.TestCase):
_ = self.receive_message(client) # Welcome message _ = self.receive_message(client) # Welcome message
client.send(f"{name}\n".encode('ascii')) client.send(f"{name}\n".encode('ascii'))
# Server should disconnect us # Server should disconnect us
response = self.receive_message(client)
with self.assertRaises((ConnectionResetError, ConnectionAbortedError, socket.error)): with self.assertRaises((ConnectionResetError, ConnectionAbortedError, socket.error)):
client.send("test\n".encode('ascii')) client.sendall("test\n".encode('ascii'))
response = self.receive_message(client)
response = self.receive_message(client)
response = self.receive_message(client)
client.close() client.close()
def test_valid_username(self): def test_valid_username(self):
@@ -95,7 +98,7 @@ class TestBudgetChat(unittest.TestCase):
client.send(f"{username}\n".encode('ascii')) client.send(f"{username}\n".encode('ascii'))
_ = self.receive_message(client) # Room info _ = self.receive_message(client) # Room info
clients.append(client) clients.append(client)
time.sleep(0.1) # Small delay to prevent race conditions # time.sleep(0.1) # Small delay to prevent race conditions
# Send a message from the last client # Send a message from the last client
test_message = "Hello from last user!" test_message = "Hello from last user!"
@@ -105,6 +108,7 @@ class TestBudgetChat(unittest.TestCase):
expected = f"[{usernames[-1]}] {test_message}" expected = f"[{usernames[-1]}] {test_message}"
for i in range(len(clients)-1): for i in range(len(clients)-1):
received = self.receive_message(clients[i]) received = self.receive_message(clients[i])
print(f"Received: {received}")
self.assertEqual(expected, received) self.assertEqual(expected, received)
# Cleanup # Cleanup
@@ -126,10 +130,49 @@ class TestBudgetChat(unittest.TestCase):
# Close client2 and check if client1 receives departure message # Close client2 and check if client1 receives departure message
client2.close() client2.close()
departure_msg = self.receive_message(client1) departure_msg = self.receive_message(client1)
print(departure_msg)
self.assertTrue("user2" in departure_msg) self.assertTrue("user2" in departure_msg)
self.assertTrue(departure_msg.startswith("*")) self.assertTrue(departure_msg.startswith("*"))
client1.close() client1.close()
def list_tests():
"""List all available tests"""
print("\nAvailable tests:")
test_methods = [method for method in dir(TestBudgetChat) if method.startswith('test_')]
for i, test in enumerate(test_methods, 1):
print(f"{i}. {test}")
print("\n0. Run all tests")
def run_selected_test(test_number):
"""Run a specific test based on user selection"""
suite = unittest.TestSuite()
test_methods = [method for method in dir(TestBudgetChat) if method.startswith('test_')]
if test_number == 0:
# Run all tests
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestBudgetChat))
else:
try:
selected_test = test_methods[test_number - 1]
suite.addTest(TestBudgetChat(selected_test))
except IndexError:
print("Invalid test number!")
return
runner = unittest.TextTestRunner(verbosity=2)
runner.run(suite)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() while True:
list_tests()
try:
choice = input("\nEnter test number (or 'q' to quit): ")
if choice.lower() == 'q':
break
test_number = int(choice)
run_selected_test(test_number)
except ValueError:
print("Please enter a valid number!")
input("\nPress Enter to continue...")