“Trouble Implementing Test Cases for Minesweeper Game in C”

The problem I’m encountering involves implementing test cases for a Minesweeper game written in C. I’ve written a function called run_tests to execute the test cases provided, but I’m having difficulty getting them to run correctly.

The test cases include both input and expected output scenarios, covering various game actions such as starting a game, opening cells, flagging cells, and quitting the game. Each test case provides an expected output grid after certain actions are performed.

My main challenge is figuring out how to modify my run_tests function to properly execute these test cases and compare the actual output with the expected output. I’m also unsure about the best approach to structure my test cases within the function and how to handle any errors or discrepancies between the actual and expected outputs.

Overall, I’m seeking guidance on how to effectively implement these test cases in my Minesweeper game code and ensure that they run smoothly to validate the game’s functionality.

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <time.h>

// Constants
    #define DEFAULT_SIZE 9
    #define DEFAULT_MINES 10
    #define DEFAULT_SEED 0

//     Color codes for output
     #define COLOR_RED "33[31m"
     #define COLOR_YELLOW "33[33m"
     #define COLOR_BG_RED "33[41m"
#define COLOR_RESET "33[0m"

// Field characters
#define CHAR_CLOSED '░'
#define CHAR_OPEN '·'
#define CHAR_FLAG '¶'
#define CHAR_MINE '@'

// Structs
typedef struct {
    int bomb;
    int opened;
    int flagged;
    int adjacent_mines;
} Cell;

typedef struct {
    Cell **cells;
    int height;
    int width;
    int mines;
    int remaining_flags;
} Board;

// Function Prototypes
void parse_arguments(int argc, char *argv[], int *height, int *width, int *mines, int *seed);
void initialize_board(Board *board, int height, int width, int mines);
void generate_map(Board *board, int start_row, int start_col, int seed);
void open_cell(Board *board, int row, int col);
void flag_cell(Board *board, int row, int col);
void dump_board(Board *board);
void save_game(Board *board, const char *filename);
void load_game(Board *board, const char *filename);
void print_board(Board *board);
void free_board(Board *board);
void game_loop(Board *board);
void error_message(const char *message, int exit_code);
int check_win(Board *board);
void run_tests(const char *test_file);

int main(int argc, char *argv[]) {
    if (argc > 1 && strcmp(argv[1], "--test") == 0) {
        run_tests(argv[2]);
    } else {
        int height = DEFAULT_SIZE, width = DEFAULT_SIZE, mines = DEFAULT_MINES, seed = DEFAULT_SEED;
        Board board;

        parse_arguments(argc, argv, &height, &width, &mines, &seed);
        srand(seed);
        initialize_board(&board, height, width, mines);

        printf("Welcome to ESP Minesweeper!n");
        printf("Chosen field size: %d x %d.n", height, width);
        printf("After map generation %d mines will be hidden in the playing field.n", mines);
        printf(" > ");

        game_loop(&board);
        free_board(&board);
    }
    return 0;
}

void error_message(const char *message, int exit_code) {
    fprintf(stderr, "%sn", message);
    exit(exit_code);
}

void parse_arguments(int argc, char *argv[], int *height, int *width, int *mines, int *seed) {
    for (int i = 1; i < argc; i++) {
        if (strcmp(argv[i], "--size") == 0 && i + 2 < argc) {
            *height = atoi(argv[++i]);
            *width = atoi(argv[++i]);
            if (*height <= 0 || *width <= 0) {
                error_message("Error: Invalid arguments given!", 1);
            }
        } else if (strcmp(argv[i], "--mines") == 0 && i + 1 < argc) {
            *mines = atoi(argv[++i]);
            if (*mines <= 0) {
                error_message("Error: Invalid arguments given!", 1);
            }
        } else if (strcmp(argv[i], "--seed") == 0 && i + 1 < argc) {
            *seed = atoi(argv[++i]);
        } else {
            error_message("Error: Unknown command!", 1);
        }
    }
}

void initialize_board(Board *board, int height, int width, int mines) {
    board->height = height;
    board->width = width;
    board->mines = mines;
    board->remaining_flags = mines;
    board->cells = malloc(height * sizeof(Cell *));
    for (int i = 0; i < height; i++) {
        board->cells[i] = calloc(width, sizeof(Cell));
    }
}

void generate_map(Board *board, int start_row, int start_col, int seed) {
    int fields_left = board->height * board->width - 1;
    int mines_left = board->mines;
    srand(seed);

    for (int row = 0; row < board->height; row++) {
        for (int col = 0; col < board->width; col++) {
            if (row == start_row && col == start_col) {
                board->cells[row][col].bomb = 0;
                continue;
            }
            unsigned long long random_number = ((unsigned long long)rand() << 32) | rand();
            if (random_number % fields_left < mines_left) {
                board->cells[row][col].bomb = 1;
                mines_left--;
            } else {
                board->cells[row][col].bomb = 0;
            }
            fields_left--;
        }
    }
}

void open_cell(Board *board, int row, int col) {
    if (row < 0 || col < 0 || row >= board->height || col >= board->width || board->cells[row][col].opened) {
        return;
    }

    board->cells[row][col].opened = 1;

    if (board->cells[row][col].bomb) {
        printf(COLOR_BG_RED "Game Over!n" COLOR_RESET);
        print_board(board);
        exit(0);
    }

    int mine_count = 0;
    for (int r = row - 1; r <= row + 1; r++) {
        for (int c = col - 1; c <= col + 1; c++) {
            if (r >= 0 && c >= 0 && r < board->height && c < board->width && board->cells[r][c].bomb) {
                mine_count++;
            }
        }
    }

    board->cells[row][col].adjacent_mines = mine_count;
    if (mine_count == 0) {
        for (int r = row - 1; r <= row + 1; r++) {
            for (int c = col - 1; c <= col + 1; c++) {
                if (r >= 0 && c >= 0 && r < board->height && c < board->width && !board->cells[r][c].opened) {
                    open_cell(board, r, c);
                }
            }
        }
    }

    if (check_win(board)) {
        printf("Congratulations! You've won!n");
        print_board(board);
        exit(0);
    }
}

void flag_cell(Board *board, int row, int col) {
    if (row < 0 || col < 0 || row >= board->height || col >= board->width || board->cells[row][col].opened) {
        return;
    }

    board->cells[row][col].flagged = !board->cells[row][col].flagged;
    board->remaining_flags += board->cells[row][col].flagged ? -1 : 1;
}

void save_game(Board *board, const char *filename) {
    FILE *file = fopen(filename, "wb");
    if (!file) {
        error_message("Error: Failed to open file!", 1);
    }
    fwrite(&board->height, sizeof(int), 1, file);
    fwrite(&board->width, sizeof(int), 1, file);
    fwrite(&board->mines, sizeof(int), 1, file);
    fwrite(&board->remaining_flags, sizeof(int), 1, file);
    for (int i = 0; i < board->height; i++) {
        fwrite(board->cells[i], sizeof(Cell), board->width, file);
    }
    fclose(file);
}

void load_game(Board *board, const char *filename) {
    FILE *file = fopen(filename, "rb");
    if (!file) {
        error_message("Error: Failed to open file!", 1);
    }
    fread(&board->height, sizeof(int), 1, file);
    fread(&board->width, sizeof(int), 1, file);
    fread(&board->mines, sizeof(int), 1, file);
    fread(&board->remaining_flags, sizeof(int), 1, file);
    board->cells = malloc(board->height * sizeof(Cell *));
    for (int i = 0; i < board->height; i++) {
        board->cells[i] = calloc(board->width, sizeof(Cell));
        fread(board->cells[i], sizeof(Cell), board->width, file);
    }
    fclose(file);
}

void print_board(Board *board) {
    printf("  %dn", board->remaining_flags);
    printf(" ");
    for (int col = 0; col < board->width; col++) {
        printf("=");
    }
    printf("n");
    for (int row = 0; row < board->height; row++) {
        printf("|");
        for (int col = 0; col < board->width; col++) {
            if (board->cells[row][col].flagged) {
                printf(COLOR_RED "¶" COLOR_RESET);
            } else if (board->cells[row][col].opened) {
                if (board->cells[row][col].bomb) {
                    printf(COLOR_YELLOW "@");
                } else if (board->cells[row][col].adjacent_mines > 0) {
                    printf("%d", board->cells[row][col].adjacent_mines);
                } else {
                    printf("·");
                }
            } else {
                printf("░");
            }
        }
        printf("|n");
    }
    printf(" ");
    for (int col = 0; col < board->width; col++) {
        printf("=");
    }
    printf("n");
}

void free_board(Board *board) {
    for (int i = 0; i < board->height; i++) {
        free(board->cells[i]);
    }
    free(board->cells);
}

void game_loop(Board *board) {
    char command[256];
    while (1) {
        printf("Enter a command: ");
        if (!fgets(command, sizeof(command), stdin)) {
            error_message("Error: Failed to read input!", 1);
        }
        char *cmd = strtok(command, " ");
        if (strcmp(cmd, "start") == 0) {
            int row = atoi(strtok(NULL, " "));
            int col = atoi(strtok(NULL, " "));
            printf("Game started at cell (%d, %d)!n", row, col);
            free_board(board);
            initialize_board(board, board->height, board->width, board->mines);
            generate_map(board, row, col, time(NULL));
            open_cell(board, row, col);
            print_board(board);
        } else if (strcmp(cmd, "open") == 0) {
            int row = atoi(strtok(NULL, " "));
            int col = atoi(strtok(NULL, " "));
            open_cell(board, row, col);
            print_board(board);
        } else if (strcmp(cmd, "flag") == 0) {
            int row = atoi(strtok(NULL, " "));
            int col = atoi(strtok(NULL, " "));
            flag_cell(board, row, col);
            print_board(board);
        } else if (strcmp(cmd, "save") == 0) {
            char *filename = strtok(NULL, "n");
            save_game(board, filename);
            printf("Game saved to %s.n", filename);
        } else if (strcmp(cmd, "load") == 0) {
            char *filename = strtok(NULL, "n");
            free_board(board);
            load_game(board, filename);
            printf("Game loaded from %s.n", filename);
            print_board(board);
        } else {
            printf("Unknown command. Try again.n");
        }
    }
}

int check_win(Board *board) {
    for (int row = 0; row < board->height; row++) {
        for (int col = 0; col < board->width; col++) {
            if (!board->cells[row][col].opened && !board->cells[row][col].bomb) {
                return 0;
            }
        }
    }
    return 1;
}

void run_tests(const char *test_file) {
    FILE *file = fopen(test_file, "r");
    if (!file) {
        perror("Failed to open test file");
        exit(1);
    }

    Board board;
    int height = DEFAULT_SIZE, width = DEFAULT_SIZE, mines = DEFAULT_MINES, seed = DEFAULT_SEED;
    initialize_board(&board, height, width, mines);

    char command[256];
    while (fgets(command, sizeof(command), file)) {
        char *cmd = strtok(command, " ");
        if (strcmp(cmd, "start") == 0) {
            int row = atoi(strtok(NULL, " "));
            int col = atoi(strtok(NULL, " "));
            printf("Game started at cell (%d, %d)!n", row, col);
            free_board(&board);
            initialize_board(&board, board.height, board.width, board.mines);
            generate_map(&board, row, col, time(NULL));
            open_cell(&board, row, col);
            print_board(&board);
        } else if (strcmp(cmd, "open") == 0) {
            int row = atoi(strtok(NULL, " "));
            int col = atoi(strtok(NULL, " "));
            open_cell(&board, row, col);
            print_board(&board);
        } else if (strcmp(cmd, "flag") == 0) {
            int row = atoi(strtok(NULL, " "));
            int col = atoi(strtok(NULL, " "));
            flag_cell(&board, row, col);
            print_board(&board);
        } else if (strcmp(cmd, "save") == 0) {
            char *filename = strtok(NULL, "n");
            save_game(&board, filename);
            printf("Game saved to %s.n", filename);
        } else if (strcmp(cmd, "load") == 0) {
            char *filename = strtok(NULL, "n");
            free_board(&board);
            load_game(&board, filename);
            printf("Game loaded from %s.n", filename);
            print_board(&board);
        } else {
            printf("Unknown command. Try again.n");
        }
    }

    fclose(file);
    free_board(&board);
}


here are the following test cases that i want to perform all they include both input and output;
> Welcome to ESP Minesweeper!
> Chosen field size: 9 x 9.
> After map generation 10 mines will be hidden in the playing field.
?  > 
< quit
> 
>   [31m¶[0m: 0
>   ========= 
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>   ========= 

test case 2;
> Welcome to ESP Minesweeper!
> Chosen field size: 9 x 9.
> After map generation 10 mines will be hidden in the playing field.
?  > 
< start 5 5
> 
>   [31m¶[0m: 10
>   ========= 
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░2░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>   ========= 
?  > 
< open 0 2
> 
>   [31m¶[0m: 10
>   ========= 
>  |░░1░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░2░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>   ========= 
?  > 
< open 0 4
> 
>   [31m¶[0m: 10
>   ========= 
>  |░░1░1░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░2░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>   ========= 
?  > 
< open 8 8
> 
>   [31m¶[0m: 10
>   ========= 
>  |░░1░1░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░21122░░|
>  |111···111|
>  |·········|
>  |·········|
>   ========= 
?  > 
< quit
> 
>   [31m¶[0m: 10
>   ========= 
>  |░░1░1░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░21122░░|
>  |111···111|
>  |·········|
>  |·········|
>   ========= 
test case3;
> Welcome to ESP Minesweeper!
> Chosen field size: 9 x 9.
> After map generation 10 mines will be hidden in the playing field.
?  > 
< start 3 3
> 
>   [31m¶[0m: 10
>   ========= 
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░3░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>   ========= 
?  > 
< dump
> 
>   [31m¶[0m: 10
>   ========= 
>  |··111····|
>  |111[33m@[0m2211·|
>  |[33m@[0m113[33m@[0m3[33m@[0m1·|
>  |1213[33m@[0m4321|
>  |·2[33m@[0m32[33m@[0m3[33m@[0m2|
>  |·2[33m@[0m2113[33m@[0m2|
>  |·111··111|
>  |·········|
>  |·········|
>   ========= 
> 
>   [31m¶[0m: 10
>   ========= 
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░3░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>   ========= 
?  > 
< quit
> 
>   [31m¶[0m: 10
>   ========= 
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░3░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>   ========= 
test case 4;
> Welcome to ESP Minesweeper!
> Chosen field size: 4 x 4.
> After map generation 10 mines will be hidden in the playing field.
?  > 
< start 1 3
> 
>   [31m¶[0m: 10
>   ==== 
>  |░░░░|
>  |░░░4|
>  |░░░░|
>  |░░░░|
>   ==== 
?  > 
< open 2 1
> 
>   [31m¶[0m: 10
>   ==== 
>  |░░░░|
>  |░░░4|
>  |░7░░|
>  |░░░░|
>   ==== 
?  > 
< open 1 1
> 
>   [31m¶[0m: 10
>   ==== 
>  |░░░░|
>  |░4░4|
>  |░7░░|
>  |░░░░|
>   ==== 
?  > 
< open 0 1
> 
>   [31m¶[0m: 10
>   ==== 
>  |░2░░|
>  |░4░4|
>  |░7░░|
>  |░░░░|
>   ==== 
?  > 
< open 0 2
> 
>   [31m¶[0m: 10
>   ==== 
>  |░22░|
>  |░4░4|
>  |░7░░|
>  |░░░░|
>   ==== 
?  > 
< quit
> 
>   [31m¶[0m: 10
>   ==== 
>  |░22░|
>  |░4░4|
>  |░7░░|
>  |░░░░|
>   ==== 
test case 5;
> Welcome to ESP Minesweeper!
> Chosen field size: 9 x 9.
> After map generation 79 mines will be hidden in the playing field.
?  > 
< start 4 4
> 
>   [31m¶[0m: 79
>   ========= 
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░8░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>   ========= 
?  > 
< quit
> 
>   [31m¶[0m: 79
>   ========= 
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░8░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>   ========= 
test case 6;
> Welcome to ESP Minesweeper!
> Chosen field size: 9 x 9.
> After map generation 10 mines will be hidden in the playing field.
?  > 
< start 2 2
> 
>   [31m¶[0m: 10
>   ========= 
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░2░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>   ========= 
?  > 
< dump
> 
>   [31m¶[0m: 10
>   ========= 
>  |····11111|
>  |111·1[33m@[0m11[33m@[0m|
>  |2[33m@[0m2112232|
>  |[33m@[0m3[33m@[0m212[33m@[0m2[33m@[0m|
>  |1212[33m@[0m2121|
>  |···111···|
>  |·····111·|
>  |···112[33m@[0m1·|
>  |···1[33m@[0m211·|
>   ========= 
> 
>   [31m¶[0m: 10
>   ========= 
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░2░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>   ========= 
?  > 
< open 8 1
> 
>   [31m¶[0m: 10
>   ========= 
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░2░░░░░░|
>  |░░░░░░░░░|
>  |1212░░░░░|
>  |···111░░░|
>  |·····1░░░|
>  |···112░░░|
>  |···1░░░░░|
>   ========= 
?  > 
< open 8 8
> 
>   [31m¶[0m: 10
>   ========= 
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░2░░░░░░|
>  |░░░░░░░░░|
>  |1212░2121|
>  |···111···|
>  |·····111·|
>  |···112░1·|
>  |···1░░░1·|
>   ========= 
?  > 
< quit
> 
>   [31m¶[0m: 10
>   ========= 
>  |░░░░░░░░░|
>  |░░░░░░░░░|
>  |░░2░░░░░░|
>  |░░░░░░░░░|
>  |1212░2121|
>  |···111···|
>  |·····111·|
>  |···112░1·|
>  |···1░░░1·|
>   ========= 

lastly i wanted to tell you that if you want i can send you my test.toml file


New contributor

Usama Ali is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật