Pārlūkot izejas kodu

Update tests

Update input tests.
feature/data-structs
TheoryOfNekomata pirms 2 gadiem
vecāks
revīzija
e4d4441b07
10 mainītis faili ar 299 papildinājumiem un 293 dzēšanām
  1. +14
    -0
      CMakeLists.txt
  2. +4
    -0
      __mocks__/SDL_joystick.mock.h
  3. +11
    -0
      __mocks__/SDL_stdinc.mock.h
  4. +2
    -1
      dependencies.txt
  5. +1
    -0
      src/packages/game/IZ_app.c
  6. +5
    -0
      src/packages/game/IZ_app.h
  7. +1
    -0
      src/packages/game/input/IZ_joystick.h
  8. +1
    -0
      src/packages/game/input/IZ_keyboard.h
  9. +260
    -292
      src/packages/game/input/input.test.c
  10. +0
    -0
      src/packages/game/memory/memory.test.c

+ 14
- 0
CMakeLists.txt Parādīt failu

@@ -11,6 +11,7 @@ include_directories(
"${PROJECT_SOURCE_DIR}/dependencies/bdd-for-c"
"${PROJECT_SOURCE_DIR}/dependencies/portmidi/pm_common"
"${PROJECT_SOURCE_DIR}/dependencies/spine-runtimes/spine-c/spine-c/include"
"${PROJECT_SOURCE_DIR}/dependencies/getopt-for-windows"
)

if (WIN32)
@@ -30,6 +31,8 @@ add_executable(
game
dependencies/minIni/dev/minIni.h
dependencies/minIni/dev/minIni.c
dependencies/getopt-for-windows/getopt.h
dependencies/getopt-for-windows/getopt.c
src/packages/game/output/IZ_video.h
src/packages/game/output/IZ_video.c
src/packages/game/IZ_common.h
@@ -76,6 +79,7 @@ add_executable(
__mocks__/SDL_keyboard.mock.h
__mocks__/SDL_events.mock.h
__mocks__/SDL_joystick.mock.h
__mocks__/SDL_stdinc.mock.h

src/packages/game/IZ_config.h

@@ -104,6 +108,16 @@ add_executable(
src/packages/game/output/output.test.c
)

add_executable(
game-test-memory
dependencies/bdd-for-c/bdd-for-c.h
src/packages/test/IZ_mock.h
src/packages/test/IZ_test.h

src/packages/game/memory/IZ_pool.h
src/packages/game/memory/IZ_pool.c
src/packages/game/memory/memory.test.c)

if (WIN32)
add_custom_command(TARGET game POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different # which executes "cmake - E copy_if_different..."


+ 4
- 0
__mocks__/SDL_joystick.mock.h Parādīt failu

@@ -18,4 +18,8 @@ mock(SDL_JoystickInstanceID) int SDL_JoystickInstanceID(SDL_Joystick* joystick)
mock_return(SDL_JoystickInstanceID) 0;
}

mock(SDL_JoystickClose) void SDL_JoystickClose(SDL_Joystick* _joystick) {
mock_return(SDL_JoystickClose);
}

#endif

+ 11
- 0
__mocks__/SDL_stdinc.mock.h Parādīt failu

@@ -0,0 +1,11 @@
#ifndef SDL_STDINC_MOCK_H
#define SDL_STDINC_MOCK_H

#include "../src/packages/test/IZ_test.h"
#include <string.h>

mock(SDL_memcpy) void* SDL_memcpy(void* dst, const void* src, size_t len) {
mock_return(SDL_memcpy) memcpy(dst, src, len);
}

#endif

+ 2
- 1
dependencies.txt Parādīt failu

@@ -1,4 +1,5 @@
https://github.com/compuphase/minIni
https://github.com/grassator/bdd-for-c
https://github.com/PortMidi/portmidi
https://github.com/EsotericSoftware/spine-runtimes
https://github.com/EsotericSoftware/spine-runtimes
https://github.com/Chunde/getopt-for-windows

+ 1
- 0
src/packages/game/IZ_app.c Parādīt failu

@@ -86,6 +86,7 @@ IZ_ProcedureResult IZ_RunApp(IZ_App* app, u8 arg_count, char* arg_values[]) {
printf_s(" %s\n", arg_values[arg_index]);
}


IZ_ProcedureResult init_result = IZ_InitializeApp(app);
if (init_result) {
return init_result;


+ 5
- 0
src/packages/game/IZ_app.h Parādīt failu

@@ -3,6 +3,11 @@

#include <SDL.h>
#include <stdbool.h>
#ifdef __WIN32__
#include <getopt.h>
#else
#include <unistd.h>
#endif
#include "input/IZ_input.h"
#include "output/IZ_video.h"
#include "memory/IZ_pool.h"


+ 1
- 0
src/packages/game/input/IZ_joystick.h Parādīt failu

@@ -1,6 +1,7 @@
#ifndef IZ_JOYSTICK_H
#define IZ_JOYSTICK_H

#include <SDL_stdinc.h>
#include <SDL_joystick.h>
#include <SDL_events.h>
#include <minIni.h>


+ 1
- 0
src/packages/game/input/IZ_keyboard.h Parādīt failu

@@ -1,6 +1,7 @@
#ifndef IZ_KEYBOARD_H
#define IZ_KEYBOARD_H

#include <SDL_stdinc.h>
#include <SDL_keyboard.h>
#include <SDL_events.h>
#include <minIni.h>


+ 260
- 292
src/packages/game/input/input.test.c Parādīt failu

@@ -1,5 +1,6 @@
#include "../../../__mocks__/SDL_keyboard.mock.h"
#include "../../../__mocks__/SDL_joystick.mock.h"
#include "../../../__mocks__/SDL_stdinc.mock.h"
#include "../../../__mocks__/minIni.mock.h"
#include "IZ_keyboard.h"
#include "IZ_joystick.h"
@@ -16,283 +17,267 @@ spec("input") {
describe("joystick") {
describe("HandleJoystickEvents") {
static SDL_Event e;
static IZ_JoystickState state;
static IZ_Action action;
static IZ_JoystickState state[PLAYERS];
static IZ_Action action[PLAYERS];

describe("on axis motion events") {
before_each() {
e.type = SDL_JOYAXISMOTION;
state.config.axis_threshold = 8000u;
}

describe("on primary horizontal direction") {
u8 p;
for (p = 0; p < PLAYERS; p += 1) {
describe("on axis motion events") {
before_each() {
e.jaxis.axis = IZ_JOY_AXIS_DIRECTION_HORIZONTAL1;
e.type = SDL_JOYAXISMOTION;
state[0].config.axis_threshold = 8000u;
}

it("handles positive motion") {
e.jaxis.value = GenerateAxisValueOutsideThreshold(state.config.axis_threshold);
action = 0;

printf("(axis value: %d) ", e.jaxis.value);
IZ_HandleJoystickEvents(e, &state, &action);
check(
action == (0x1 << IZ_ACTION_INDEX_RIGHT),
"Action not set."
);
describe("on primary horizontal direction") {
before_each() {
e.jaxis.axis = IZ_JOY_AXIS_DIRECTION_HORIZONTAL1;
}

it("handles positive motion") {
e.jaxis.value = GenerateAxisValueOutsideThreshold(state[p].config.axis_threshold);
action[p] = 0;

printf("(axis value: %d) ", e.jaxis.value);
IZ_HandleJoystickEvents(e, &state, &action);
check(
action[p] == (0x1 << IZ_ACTION_INDEX_RIGHT),
"Action not set."
);
}

it("handles negative motion") {
e.jaxis.value = -GenerateAxisValueOutsideThreshold(state[p].config.axis_threshold);
action[p] = 0;

printf("(axis value: %d) ", e.jaxis.value);
IZ_HandleJoystickEvents(e, &state, &action);
check(
action[p] == (0x1 << IZ_ACTION_INDEX_LEFT),
"Action not set."
);
}

it("handles neutral motion") {
e.jaxis.value = GenerateAxisValueWithinThreshold(state[p].config.axis_threshold);
action[p] = 0;

printf("(axis value: %d) ", e.jaxis.value);
IZ_HandleJoystickEvents(e, &state, &action);
check(
action[p] == 0,
"Action not set."
);
}
}

it("handles negative motion") {
e.jaxis.value = -GenerateAxisValueOutsideThreshold(state.config.axis_threshold);
action = 0;

printf("(axis value: %d) ", e.jaxis.value);
IZ_HandleJoystickEvents(e, &state, &action);
check(
action == (0x1 << IZ_ACTION_INDEX_LEFT),
"Action not set."
);
describe("on secondary horizontal direction") {
before_each() {
e.jaxis.axis = IZ_JOY_AXIS_DIRECTION_HORIZONTAL2;
}

it("handles positive motion") {
e.jaxis.value = GenerateAxisValueOutsideThreshold(state[p].config.axis_threshold);
action[p] = 0;

printf("(axis value: %d) ", e.jaxis.value);
IZ_HandleJoystickEvents(e, &state, &action);
check(
action[p] == (0x1 << IZ_ACTION_INDEX_RIGHT),
"Action not set."
);
}

it("handles negative motion") {
e.jaxis.value = -GenerateAxisValueOutsideThreshold(state[p].config.axis_threshold);
action[p] = 0;

printf("(axis value: %d) ", e.jaxis.value);
IZ_HandleJoystickEvents(e, &state, &action);
check(
action[p] == (0x1 << IZ_ACTION_INDEX_LEFT),
"Action not set."
);
}

it("handles neutral motion") {
e.jaxis.value = GenerateAxisValueWithinThreshold(state[p].config.axis_threshold);;
action[p] = 0;

printf("(axis value: %d) ", e.jaxis.value);
IZ_HandleJoystickEvents(e, &state, &action);
check(
action[p] == 0,
"Action not set."
);
}
}

it("handles neutral motion") {
e.jaxis.value = GenerateAxisValueWithinThreshold(state.config.axis_threshold);
action = 0;

printf("(axis value: %d) ", e.jaxis.value);
IZ_HandleJoystickEvents(e, &state, &action);
check(
action == 0,
"Action not set."
);
describe("on primary vertical direction") {
before_each() {
e.jaxis.axis = IZ_JOY_AXIS_DIRECTION_VERTICAL1;
}

it("handles positive motion") {
e.jaxis.value = GenerateAxisValueOutsideThreshold(state[p].config.axis_threshold);
action[p] = 0;

printf("(axis value: %d) ", e.jaxis.value);
IZ_HandleJoystickEvents(e, &state, &action);
check(
action[p] == (0x1 << IZ_ACTION_INDEX_DOWN),
"Action not set."
);
}

it("handles negative motion") {
e.jaxis.value = -GenerateAxisValueOutsideThreshold(state[p].config.axis_threshold);
action[p] = 0;

printf("(axis value: %d) ", e.jaxis.value);
IZ_HandleJoystickEvents(e, &state, &action);
check(
action[p] == (0x1 << IZ_ACTION_INDEX_UP),
"Action not set."
);
}

it("handles neutral motion") {
e.jaxis.value = GenerateAxisValueWithinThreshold(state[p].config.axis_threshold);;
action[p] = 0;

printf("(axis value: %d) ", e.jaxis.value);
IZ_HandleJoystickEvents(e, &state, &action);
check(
action[p] == 0,
"Action not set."
);
}
}
}

describe("on secondary horizontal direction") {
before_each() {
e.jaxis.axis = IZ_JOY_AXIS_DIRECTION_HORIZONTAL2;
}

it("handles positive motion") {
e.jaxis.value = GenerateAxisValueOutsideThreshold(state.config.axis_threshold);
action = 0;

printf("(axis value: %d) ", e.jaxis.value);
IZ_HandleJoystickEvents(e, &state, &action);
check(
action == (0x1 << IZ_ACTION_INDEX_RIGHT),
"Action not set."
);
}

it("handles negative motion") {
e.jaxis.value = -GenerateAxisValueOutsideThreshold(state.config.axis_threshold);
action = 0;

printf("(axis value: %d) ", e.jaxis.value);
IZ_HandleJoystickEvents(e, &state, &action);
check(
action == (0x1 << IZ_ACTION_INDEX_LEFT),
"Action not set."
);
}

it("handles neutral motion") {
e.jaxis.value = GenerateAxisValueWithinThreshold(state.config.axis_threshold);;
action = 0;

printf("(axis value: %d) ", e.jaxis.value);
IZ_HandleJoystickEvents(e, &state, &action);
check(
action == 0,
"Action not set."
);
describe("on secondary vertical direction") {
before_each() {
e.jaxis.axis = IZ_JOY_AXIS_DIRECTION_VERTICAL2;
}

it("handles positive motion") {
e.jaxis.value = GenerateAxisValueOutsideThreshold(state[p].config.axis_threshold);
action[p] = 0;

printf("(axis value: %d) ", e.jaxis.value);
IZ_HandleJoystickEvents(e, &state, &action);
check(
action[p] == (0x1 << IZ_ACTION_INDEX_DOWN),
"Action not set."
);
}

it("handles negative motion") {
e.jaxis.value = -GenerateAxisValueOutsideThreshold(state[p].config.axis_threshold);
action[p] = 0;

printf("(axis value: %d) ", e.jaxis.value);
IZ_HandleJoystickEvents(e, &state, &action);
check(
action[p] == (0x1 << IZ_ACTION_INDEX_UP),
"Action not set."
);
}

it("handles neutral motion") {
e.jaxis.value = GenerateAxisValueWithinThreshold(state[p].config.axis_threshold);;
action[p] = 0;

printf("(axis value: %d) ", e.jaxis.value);
IZ_HandleJoystickEvents(e, &state, &action);
check(
action[p] == 0,
"Action not set."
);
}
}
}

describe("on primary vertical direction") {
describe("on hat motion events") {
before_each() {
e.jaxis.axis = IZ_JOY_AXIS_DIRECTION_VERTICAL1;
}

it("handles positive motion") {
e.jaxis.value = GenerateAxisValueOutsideThreshold(state.config.axis_threshold);
action = 0;

printf("(axis value: %d) ", e.jaxis.value);
IZ_HandleJoystickEvents(e, &state, &action);
check(
action == (0x1 << IZ_ACTION_INDEX_DOWN),
"Action not set."
);
}

it("handles negative motion") {
e.jaxis.value = -GenerateAxisValueOutsideThreshold(state.config.axis_threshold);
action = 0;

printf("(axis value: %d) ", e.jaxis.value);
IZ_HandleJoystickEvents(e, &state, &action);
check(
action == (0x1 << IZ_ACTION_INDEX_UP),
"Action not set."
);
}

it("handles neutral motion") {
e.jaxis.value = GenerateAxisValueWithinThreshold(state.config.axis_threshold);;
action = 0;

printf("(axis value: %d) ", e.jaxis.value);
IZ_HandleJoystickEvents(e, &state, &action);
check(
action == 0,
"Action not set."
);
}
}

describe("on secondary vertical direction") {
before_each() {
e.jaxis.axis = IZ_JOY_AXIS_DIRECTION_VERTICAL2;
}

it("handles positive motion") {
e.jaxis.value = GenerateAxisValueOutsideThreshold(state.config.axis_threshold);
action = 0;

printf("(axis value: %d) ", e.jaxis.value);
IZ_HandleJoystickEvents(e, &state, &action);
check(
action == (0x1 << IZ_ACTION_INDEX_DOWN),
"Action not set."
);
}

it("handles negative motion") {
e.jaxis.value = -GenerateAxisValueOutsideThreshold(state.config.axis_threshold);
action = 0;

printf("(axis value: %d) ", e.jaxis.value);
IZ_HandleJoystickEvents(e, &state, &action);
check(
action == (0x1 << IZ_ACTION_INDEX_UP),
"Action not set."
);
}

it("handles neutral motion") {
e.jaxis.value = GenerateAxisValueWithinThreshold(state.config.axis_threshold);;
action = 0;

printf("(axis value: %d) ", e.jaxis.value);
IZ_HandleJoystickEvents(e, &state, &action);
check(
action == 0,
"Action not set."
);
e.type = SDL_JOYHATMOTION;
}
}
}

describe("on hat motion events") {
before_each() {
e.type = SDL_JOYHATMOTION;
}

for (u8 i = 0; i < 4; i += 1) {
it("handles motion for %s action", ACTION_NAMES[i]) {
e.jhat.value = (0x1u << i);
action = 0;

IZ_HandleJoystickEvents(e, &state, &action);
check(
action == (0x1u << i),
"Action not set."
);
}

it("handles motion for %s deactivation", ACTION_NAMES[i]) {
e.jhat.value = 0;
action = ~0;

IZ_HandleJoystickEvents(e, &state, &action);
check(
!(action & (0x1 << i)),
"Action not unset."
);
for (u8 i = 0; i < 4; i += 1) {
it("handles motion for %s action", ACTION_NAMES[i]) {
e.jhat.value = (0x1u << i);
action[p] = 0;

IZ_HandleJoystickEvents(e, &state, &action);
check(
action[p] == (0x1u << i),
"Action not set."
);
}

it("handles motion for %s deactivation", ACTION_NAMES[i]) {
e.jhat.value = 0;
action[p] = ~0;

IZ_HandleJoystickEvents(e, &state, &action);
check(
!(action[p] & (0x1 << i)),
"Action not unset."
);
}
}
}
}

describe("on button events") {
for (u8 i = 4; i < CONTROLS; i += 1) {
it("handles %s action activation", ACTION_NAMES[i]) {
e.type = SDL_JOYBUTTONDOWN;
e.jbutton.button = IZ_DEFAULT_JOYSTICK_STATE[0][i];
state.config.control_mapping[i] = IZ_DEFAULT_JOYSTICK_STATE[0][i];
action = 0;

IZ_HandleJoystickEvents(e, &state, &action);
check(
action == (0x1u << i),
"Action not set."
);
}

it("handles %s action deactivation", ACTION_NAMES[i]) {
e.type = SDL_JOYBUTTONUP;
e.jbutton.button = IZ_DEFAULT_JOYSTICK_STATE[0][i];
state.config.control_mapping[i] = IZ_DEFAULT_JOYSTICK_STATE[0][i];
action = ~0;

IZ_HandleJoystickEvents(e, &state, &action);
check(
!(action & (0x1 << i)),
"Action not unset."
);
describe("on button events") {
for (u8 i = 4; i < CONTROLS; i += 1) {
it("handles %s action activation", ACTION_NAMES[i]) {
e.type = SDL_JOYBUTTONDOWN;
e.jbutton.button = IZ_DEFAULT_JOYSTICK_STATE[p].config.control_mapping[i];
state[p].config.control_mapping[i] = IZ_DEFAULT_JOYSTICK_STATE[p].config.control_mapping[i];
action[p] = 0;

IZ_HandleJoystickEvents(e, &state, &action);
check(
action[p] == (0x1u << i),
"Action not set."
);
}

it("handles %s action deactivation", ACTION_NAMES[i]) {
e.type = SDL_JOYBUTTONUP;
e.jbutton.button = IZ_DEFAULT_JOYSTICK_STATE[p].config.control_mapping[i];
state[p].config.control_mapping[i] = IZ_DEFAULT_JOYSTICK_STATE[p].config.control_mapping[i];
action[p] = ~0;

IZ_HandleJoystickEvents(e, &state, &action);
check(
!(action[p] & (0x1 << i)),
"Action not unset."
);
}
}
}
}
}

describe("LoadJoystickConfig") {
static IZ_JoystickConfig config;

after_each() {
mock_reset(ini_getl);
}

it("calls load method") {
mock_set_expected_calls(ini_getl, CONTROLS - 4 + 1);

IZ_LoadJoystickConfig("config.ini", &config, 0);

check(
mock_get_expected_calls(ini_getl) == mock_get_actual_calls(ini_getl),
"Call count mismatch for ini_getl() (expected %u, received %u).",
mock_get_expected_calls(ini_getl),
mock_get_actual_calls(ini_getl)
);
}
}

describe("SaveJoystickConfig") {
static IZ_JoystickConfig config;
static IZ_JoystickState state[PLAYERS];

after_each() {
mock_reset(ini_putl);
}

before_each() {
for (u8 i = 0; i < CONTROLS; i += 1) {
config.control_mapping[i] = IZ_DEFAULT_JOYSTICK_STATE[0][i];
for (u8 p = 0; p < PLAYERS; p += 1) {
for (u8 i = 0; i < CONTROLS; i += 1) {
state[p].config.control_mapping[i] = IZ_DEFAULT_JOYSTICK_STATE[p].config.control_mapping[i];
}
}
}

it("calls save method") {
mock_set_expected_calls(ini_putl, CONTROLS - 4 + 1);
mock_set_expected_calls(ini_putl, ((CONTROLS - 4) + 2) * PLAYERS);

IZ_SaveJoystickConfig("config.ini", &config, 0);
IZ_SaveJoystickConfig("config.ini", &state);

check(
mock_get_expected_calls(ini_putl) == mock_get_actual_calls(ini_putl),
@@ -307,76 +292,59 @@ spec("input") {
describe("keyboard") {
describe("HandleKeyboardEvents") {
static SDL_Event e;
static IZ_KeyboardState state;
static IZ_Action action;

for (u8 i = 0; i < CONTROLS; i += 1) {
it("handles %s action activation", ACTION_NAMES[i]) {
e.type = SDL_KEYDOWN;
e.key.keysym.sym = IZ_DEFAULT_KEYBOARD_STATE[0][i];
state.config.control_mapping[i] = IZ_DEFAULT_KEYBOARD_STATE[0][i];
action = 0;

IZ_HandleKeyboardEvents(e, &state, &action);
check(
action == (0x1 << i),
"Action not set."
);
}

it("handles %s action deactivation", ACTION_NAMES[i]) {
e.type = SDL_KEYUP;
e.key.keysym.sym = IZ_DEFAULT_KEYBOARD_STATE[0][i];
state.config.control_mapping[i] = IZ_DEFAULT_KEYBOARD_STATE[0][i];
action = ~0;

IZ_HandleKeyboardEvents(e, &state, &action);
check(
!(action & (0x1 << i)),
"Action not unset."
);
}
}
}
static IZ_KeyboardState state[PLAYERS];
static IZ_Action action[PLAYERS];

describe("LoadKeyboardConfig") {
static IZ_KeyboardConfig config;

after_each() {
mock_reset(ini_gets);
}
for (u8 p = 0; p < PLAYERS; p += 1) {
for (u8 i = 0; i < CONTROLS; i += 1) {
it("handles %s action activation", ACTION_NAMES[i]) {
e.type = SDL_KEYDOWN;
e.key.keysym.sym = IZ_DEFAULT_KEYBOARD_STATE[p].config.control_mapping[i];
state[p].config.control_mapping[i] = IZ_DEFAULT_KEYBOARD_STATE[p].config.control_mapping[i];
action[p] = 0;

it("calls load method") {
mock_set_expected_calls(ini_gets, CONTROLS);
IZ_HandleKeyboardEvents(e, &state, &action);
check(
action[p] == (0x1 << i),
"Action not set."
);
}

IZ_LoadKeyboardConfig("config.ini", &config, 0);
it("handles %s action deactivation", ACTION_NAMES[i]) {
e.type = SDL_KEYUP;
e.key.keysym.sym = IZ_DEFAULT_KEYBOARD_STATE[p].config.control_mapping[i];
state[p].config.control_mapping[i] = IZ_DEFAULT_KEYBOARD_STATE[p].config.control_mapping[i];
action[p] = ~0;

check(
mock_get_expected_calls(ini_gets) == mock_get_actual_calls(ini_gets),
"Call count mismatch for ini_gets() (expected %u, received %u).",
mock_get_expected_calls(ini_gets),
mock_get_actual_calls(ini_gets)
);
IZ_HandleKeyboardEvents(e, &state, &action);
check(
!(action[p] & (0x1 << i)),
"Action not unset."
);
}
}
}
}

describe("SaveKeyboardConfig") {
static IZ_KeyboardConfig config;
static IZ_KeyboardState state[PLAYERS];

after_each() {
mock_reset(ini_puts);
}

before_each() {
for (u8 i = 0; i < CONTROLS; i += 1) {
config.control_mapping[i] = IZ_DEFAULT_KEYBOARD_STATE[0][i];
for (u8 p = 0; p < PLAYERS; p += 1) {
for (u8 i = 0; i < CONTROLS; i += 1) {
state[p].config.control_mapping[i] = IZ_DEFAULT_KEYBOARD_STATE[p].config.control_mapping[i];
}
}
}

it("calls save method") {
mock_set_expected_calls(ini_puts, CONTROLS);
mock_set_expected_calls(ini_puts, CONTROLS * PLAYERS);

IZ_SaveKeyboardConfig("config.ini", &config, 0);
IZ_SaveKeyboardConfig("config.ini", &state);

check(
mock_get_expected_calls(ini_puts) == mock_get_actual_calls(ini_puts),


+ 0
- 0
src/packages/game/memory/memory.test.c Parādīt failu


Notiek ielāde…
Atcelt
Saglabāt