Google Tests
This commit is contained in:
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 Pokemon Battle Engine
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
14
cmake/PokemonSimConfig.cmake.in
Normal file
14
cmake/PokemonSimConfig.cmake.in
Normal file
@@ -0,0 +1,14 @@
|
||||
@PACKAGE_INIT@
|
||||
|
||||
# Pokemon Battle Simulator CMake Configuration
|
||||
set(PokemonSim_VERSION "@PROJECT_VERSION@")
|
||||
|
||||
# Set up import targets
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/PokemonSimTargets.cmake")
|
||||
|
||||
# Set up variables for consumers
|
||||
set(PokemonSim_LIBRARIES PokemonSim::pokemon_battle_sim)
|
||||
set(PokemonSim_INCLUDE_DIRS "@CMAKE_INSTALL_FULL_INCLUDEDIR@")
|
||||
|
||||
# Check required components
|
||||
check_required_components(PokemonSim)
|
||||
22
cmake/modules/CompilerWarnings.cmake
Normal file
22
cmake/modules/CompilerWarnings.cmake
Normal file
@@ -0,0 +1,22 @@
|
||||
# Compiler warnings configuration
|
||||
function(set_project_warnings)
|
||||
option(WARNINGS_AS_ERRORS "Treat compiler warnings as errors" ON)
|
||||
|
||||
if(MSVC)
|
||||
add_compile_options(
|
||||
/W4
|
||||
/permissive-
|
||||
$<$<BOOL:${WARNINGS_AS_ERRORS}>:/WX>
|
||||
)
|
||||
else()
|
||||
add_compile_options(
|
||||
-Wall
|
||||
-Wextra
|
||||
-Wpedantic
|
||||
-Wshadow
|
||||
-Wconversion
|
||||
-Wsign-conversion
|
||||
$<$<BOOL:${WARNINGS_AS_ERRORS}>:-Werror>
|
||||
)
|
||||
endif()
|
||||
endfunction()
|
||||
20
cmake/modules/StaticAnalysis.cmake
Normal file
20
cmake/modules/StaticAnalysis.cmake
Normal file
@@ -0,0 +1,20 @@
|
||||
# Static analysis tools configuration
|
||||
function(enable_static_analysis)
|
||||
# Find clang-tidy
|
||||
find_program(CLANG_TIDY_EXE NAMES clang-tidy)
|
||||
if(CLANG_TIDY_EXE)
|
||||
set(CMAKE_CXX_CLANG_TIDY ${CLANG_TIDY_EXE} PARENT_SCOPE)
|
||||
message(STATUS "clang-tidy found: ${CLANG_TIDY_EXE}")
|
||||
else()
|
||||
message(STATUS "clang-tidy not found")
|
||||
endif()
|
||||
|
||||
# Find cppcheck
|
||||
find_program(CPPCHECK_EXE NAMES cppcheck)
|
||||
if(CPPCHECK_EXE)
|
||||
set(CMAKE_CXX_CPPCHECK ${CPPCHECK_EXE} PARENT_SCOPE)
|
||||
message(STATUS "cppcheck found: ${CPPCHECK_EXE}")
|
||||
else()
|
||||
message(STATUS "cppcheck not found")
|
||||
endif()
|
||||
endfunction()
|
||||
5
cmake/packaging/DEB.cmake
Normal file
5
cmake/packaging/DEB.cmake
Normal file
@@ -0,0 +1,5 @@
|
||||
# DEB packaging configuration
|
||||
set(CPACK_DEB_COMPONENT_INSTALL ON)
|
||||
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Pokemon Sim Team")
|
||||
set(CPACK_DEBIAN_PACKAGE_SECTION "games")
|
||||
set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional")
|
||||
4
cmake/packaging/RPM.cmake
Normal file
4
cmake/packaging/RPM.cmake
Normal file
@@ -0,0 +1,4 @@
|
||||
# RPM packaging configuration
|
||||
set(CPACK_RPM_COMPONENT_INSTALL ON)
|
||||
set(CPACK_RPM_PACKAGE_GROUP "Games")
|
||||
set(CPACK_RPM_PACKAGE_LICENSE "MIT")
|
||||
26
examples/CMakeLists.txt
Normal file
26
examples/CMakeLists.txt
Normal file
@@ -0,0 +1,26 @@
|
||||
# Examples CMakeLists.txt
|
||||
|
||||
# Collect all example source files
|
||||
file(GLOB_RECURSE EXAMPLE_SOURCES "*.cpp" "*.cc" "*.cxx")
|
||||
|
||||
# Create example executables
|
||||
foreach(EXAMPLE_SOURCE ${EXAMPLE_SOURCES})
|
||||
get_filename_component(EXAMPLE_NAME ${EXAMPLE_SOURCE} NAME_WE)
|
||||
add_executable(${EXAMPLE_NAME} ${EXAMPLE_SOURCE})
|
||||
|
||||
# Link with our library
|
||||
target_link_libraries(${EXAMPLE_NAME}
|
||||
PRIVATE
|
||||
PokemonSim::pokemon_battle_sim
|
||||
)
|
||||
|
||||
# Include directories
|
||||
target_include_directories(${EXAMPLE_NAME}
|
||||
PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/include
|
||||
)
|
||||
endforeach()
|
||||
|
||||
# Example of adding examples manually:
|
||||
# add_executable(battle_example battle_example.cpp)
|
||||
# target_link_libraries(battle_example PRIVATE PokemonSim::pokemon_battle_sim)
|
||||
37
include/pokemon_battle_sim.h
Normal file
37
include/pokemon_battle_sim.h
Normal file
@@ -0,0 +1,37 @@
|
||||
// Pokemon Battle Engine Header
|
||||
// This is a placeholder header for the Pokemon Battle Engine library
|
||||
|
||||
#ifndef POKEMON_BATTLE_SIM_H
|
||||
#define POKEMON_BATTLE_SIM_H
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace PokemonSim {
|
||||
|
||||
// Forward declarations
|
||||
class Pokemon;
|
||||
|
||||
// Pokemon class
|
||||
class Pokemon {
|
||||
public:
|
||||
Pokemon(const std::string& name, int health = 100);
|
||||
~Pokemon() = default;
|
||||
|
||||
// Getters
|
||||
std::string getName() const;
|
||||
int getHealth() const;
|
||||
|
||||
// Setters
|
||||
void setHealth(int health);
|
||||
|
||||
private:
|
||||
std::string name_;
|
||||
int health_;
|
||||
};
|
||||
|
||||
// Battle simulation function
|
||||
bool simulateBattle(Pokemon& pokemon1, Pokemon& pokemon2);
|
||||
|
||||
} // namespace PokemonSim
|
||||
|
||||
#endif // POKEMON_BATTLE_SIM_H
|
||||
41
src/CMakeLists.txt
Normal file
41
src/CMakeLists.txt
Normal file
@@ -0,0 +1,41 @@
|
||||
# Source files CMakeLists.txt
|
||||
|
||||
# Collect all source files
|
||||
file(GLOB_RECURSE SOURCES "*.cpp" "*.cc" "*.cxx")
|
||||
file(GLOB_RECURSE HEADERS "*.h" "*.hpp" "*.hxx")
|
||||
|
||||
# Create the main library
|
||||
add_library(pokemon_battle_sim ${SOURCES} ${HEADERS})
|
||||
|
||||
# Set library properties
|
||||
set_target_properties(pokemon_battle_sim PROPERTIES
|
||||
VERSION ${PROJECT_VERSION}
|
||||
SOVERSION ${PROJECT_VERSION_MAJOR}
|
||||
)
|
||||
|
||||
# Include directories
|
||||
target_include_directories(pokemon_battle_sim
|
||||
PUBLIC
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../include>
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
|
||||
PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
|
||||
# Link dependencies
|
||||
target_link_libraries(pokemon_battle_sim
|
||||
PRIVATE
|
||||
Threads::Threads
|
||||
)
|
||||
|
||||
# Create an alias for consistency
|
||||
add_library(PokemonSim::pokemon_battle_sim ALIAS pokemon_battle_sim)
|
||||
|
||||
# Export the target
|
||||
install(TARGETS pokemon_battle_sim
|
||||
EXPORT PokemonSimTargets
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||
)
|
||||
44
src/placeholder.cpp
Normal file
44
src/placeholder.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
// Placeholder source file for Pokemon Battle Engine
|
||||
// This file is temporary and will be replaced with actual implementation
|
||||
|
||||
#include "pokemon_battle_sim.h"
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
namespace PokemonSim {
|
||||
|
||||
// Pokemon class method implementations
|
||||
Pokemon::Pokemon(const std::string& name, int health)
|
||||
: name_(name), health_(health) {}
|
||||
|
||||
std::string Pokemon::getName() const {
|
||||
return name_;
|
||||
}
|
||||
|
||||
int Pokemon::getHealth() const {
|
||||
return health_;
|
||||
}
|
||||
|
||||
void Pokemon::setHealth(int health) {
|
||||
health_ = health;
|
||||
}
|
||||
|
||||
// Placeholder battle function
|
||||
bool simulateBattle(Pokemon& pokemon1, Pokemon& pokemon2) {
|
||||
// Simple battle simulation for testing
|
||||
while (pokemon1.getHealth() > 0 && pokemon2.getHealth() > 0) {
|
||||
// Pokemon 1 attacks Pokemon 2
|
||||
int damage = 10; // Fixed damage for now
|
||||
pokemon2.setHealth(pokemon2.getHealth() - damage);
|
||||
|
||||
if (pokemon2.getHealth() <= 0) {
|
||||
return true; // Pokemon 1 wins
|
||||
}
|
||||
|
||||
// Pokemon 2 attacks Pokemon 1
|
||||
pokemon1.setHealth(pokemon1.getHealth() - damage);
|
||||
}
|
||||
return false; // Pokemon 2 wins or tie
|
||||
}
|
||||
|
||||
} // namespace PokemonSim
|
||||
8
test_all.sh
Executable file
8
test_all.sh
Executable file
@@ -0,0 +1,8 @@
|
||||
# Configure the project
|
||||
cmake -B build -S .
|
||||
|
||||
# Build the project (includes tests)
|
||||
cmake --build build
|
||||
|
||||
# Run all tests
|
||||
cmake --build build --target test
|
||||
75
tests/CMakeLists.txt
Normal file
75
tests/CMakeLists.txt
Normal file
@@ -0,0 +1,75 @@
|
||||
# Tests CMakeLists.txt
|
||||
include(FetchContent)
|
||||
|
||||
# Download and configure Google Test
|
||||
FetchContent_Declare(
|
||||
googletest
|
||||
URL https://github.com/google/googletest/archive/v1.14.0.zip
|
||||
DOWNLOAD_EXTRACT_TIMESTAMP TRUE
|
||||
)
|
||||
|
||||
# Prevent overriding the parent project's compiler/linker on Windows
|
||||
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
||||
|
||||
# Make Google Test available
|
||||
FetchContent_MakeAvailable(googletest)
|
||||
|
||||
# Include Google Test modules
|
||||
include(GoogleTest)
|
||||
|
||||
# Create test executable for unit tests
|
||||
add_executable(unit_tests
|
||||
unit/main.cpp
|
||||
)
|
||||
|
||||
# Link with Google Test and our library
|
||||
target_link_libraries(unit_tests
|
||||
PRIVATE
|
||||
GTest::gtest_main
|
||||
GTest::gmock_main
|
||||
PokemonSim::pokemon_battle_sim
|
||||
)
|
||||
|
||||
# Set up test discovery
|
||||
gtest_discover_tests(unit_tests
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
PROPERTIES
|
||||
LABELS "unit"
|
||||
)
|
||||
|
||||
# Create test executable for integration tests
|
||||
add_executable(integration_tests
|
||||
integration/main.cpp
|
||||
)
|
||||
|
||||
# Link with Google Test and our library
|
||||
target_link_libraries(integration_tests
|
||||
PRIVATE
|
||||
GTest::gtest_main
|
||||
GTest::gmock_main
|
||||
PokemonSim::pokemon_battle_sim
|
||||
)
|
||||
|
||||
# Set up test discovery
|
||||
gtest_discover_tests(integration_tests
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
PROPERTIES
|
||||
LABELS "integration"
|
||||
)
|
||||
|
||||
# Include directories for tests
|
||||
target_include_directories(unit_tests
|
||||
PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/include
|
||||
${CMAKE_SOURCE_DIR}/src
|
||||
)
|
||||
|
||||
target_include_directories(integration_tests
|
||||
PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/include
|
||||
${CMAKE_SOURCE_DIR}/src
|
||||
)
|
||||
|
||||
# Add test subdirectories
|
||||
add_subdirectory(unit)
|
||||
add_subdirectory(integration)
|
||||
@@ -38,10 +38,49 @@ tests/
|
||||
|
||||
## Testing Framework
|
||||
|
||||
- **Google Test (gtest)**: Primary testing framework
|
||||
- **Google Mock (gmock)**: For mocking dependencies
|
||||
- **Google Test (gtest)**: Primary testing framework - automatically downloaded via FetchContent
|
||||
- **Google Mock (gmock)**: For mocking dependencies - included with Google Test
|
||||
- **Test Coverage**: Aim for >90% code coverage
|
||||
- **Continuous Integration**: Tests run on every commit
|
||||
- **Version**: Google Test v1.14.0
|
||||
|
||||
## Running Tests
|
||||
|
||||
### Build and Run
|
||||
|
||||
```bash
|
||||
# Configure and build
|
||||
cmake -B build -S .
|
||||
cmake --build build
|
||||
|
||||
# Run all tests
|
||||
cmake --build build --target test
|
||||
|
||||
# Run specific test types
|
||||
cd build && ctest -L unit # Unit tests only
|
||||
cd build && ctest -L integration # Integration tests only
|
||||
|
||||
# Run with verbose output
|
||||
cd build && ctest -V
|
||||
```
|
||||
|
||||
### Test Structure (Now Implemented)
|
||||
|
||||
```
|
||||
tests/
|
||||
├── unit/ # Unit tests for individual components
|
||||
│ ├── main.cpp # Unit test entry point
|
||||
│ ├── CMakeLists.txt # Unit test build config
|
||||
│ └── core/ # Core system tests
|
||||
│ ├── CMakeLists.txt
|
||||
│ └── test_example.cpp # Example test (ready to run)
|
||||
├── integration/ # Integration tests
|
||||
│ ├── main.cpp # Integration test entry point
|
||||
│ └── CMakeLists.txt # Integration test build config
|
||||
├── fixtures/ # Test data and fixtures
|
||||
├── helpers/ # Test helper utilities
|
||||
└── CMakeLists.txt # Main test build configuration
|
||||
```
|
||||
|
||||
## Test Categories
|
||||
|
||||
|
||||
18
tests/integration/CMakeLists.txt
Normal file
18
tests/integration/CMakeLists.txt
Normal file
@@ -0,0 +1,18 @@
|
||||
# Integration tests CMakeLists.txt
|
||||
|
||||
# Collect all test source files
|
||||
file(GLOB_RECURSE INTEGRATION_TEST_SOURCES "*.cpp" "*.cc" "*.cxx")
|
||||
|
||||
# Remove main.cpp from the sources list if it exists
|
||||
list(REMOVE_ITEM INTEGRATION_TEST_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/main.cpp")
|
||||
|
||||
# Add integration test sources to the integration_tests executable
|
||||
if(INTEGRATION_TEST_SOURCES)
|
||||
target_sources(integration_tests PRIVATE ${INTEGRATION_TEST_SOURCES})
|
||||
endif()
|
||||
|
||||
# Example of how to add specific test files:
|
||||
# target_sources(integration_tests PRIVATE
|
||||
# test_battle_integration.cpp
|
||||
# test_full_game_flow.cpp
|
||||
# )
|
||||
11
tests/integration/main.cpp
Normal file
11
tests/integration/main.cpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
// Main test runner for integration tests
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
|
||||
// Set up any global test configuration here
|
||||
// Integration tests might need different setup than unit tests
|
||||
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
9
tests/unit/CMakeLists.txt
Normal file
9
tests/unit/CMakeLists.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
# Unit tests CMakeLists.txt
|
||||
|
||||
# Add unit test subdirectories
|
||||
add_subdirectory(core)
|
||||
|
||||
# You can add more unit test subdirectories here as needed:
|
||||
# add_subdirectory(math)
|
||||
# add_subdirectory(utils)
|
||||
# add_subdirectory(battle)
|
||||
18
tests/unit/core/CMakeLists.txt
Normal file
18
tests/unit/core/CMakeLists.txt
Normal file
@@ -0,0 +1,18 @@
|
||||
# Core unit tests CMakeLists.txt
|
||||
|
||||
# Collect all test source files
|
||||
file(GLOB_RECURSE CORE_TEST_SOURCES "*.cpp" "*.cc" "*.cxx")
|
||||
|
||||
# Remove main.cpp from the sources list if it exists
|
||||
list(REMOVE_ITEM CORE_TEST_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/main.cpp")
|
||||
|
||||
# Add core test sources to the unit_tests executable
|
||||
if(CORE_TEST_SOURCES)
|
||||
target_sources(unit_tests PRIVATE ${CORE_TEST_SOURCES})
|
||||
endif()
|
||||
|
||||
# Example of how to add specific test files:
|
||||
# target_sources(unit_tests PRIVATE
|
||||
# test_example.cpp
|
||||
# test_another_example.cpp
|
||||
# )
|
||||
75
tests/unit/core/test_example.cpp
Normal file
75
tests/unit/core/test_example.cpp
Normal file
@@ -0,0 +1,75 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
// Example test case demonstrating Google Test usage
|
||||
class ExampleTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
// Set up test fixtures here
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
// Clean up test fixtures here
|
||||
}
|
||||
};
|
||||
|
||||
// Basic assertion test
|
||||
TEST_F(ExampleTest, BasicAssertions) {
|
||||
// Basic equality assertions
|
||||
EXPECT_EQ(2 + 2, 4);
|
||||
EXPECT_NE(2 + 2, 5);
|
||||
|
||||
// Boolean assertions
|
||||
EXPECT_TRUE(true);
|
||||
EXPECT_FALSE(false);
|
||||
|
||||
// String assertions
|
||||
EXPECT_STREQ("hello", "hello");
|
||||
EXPECT_STRNE("hello", "world");
|
||||
}
|
||||
|
||||
// Test with parameters
|
||||
TEST(ParameterizedTest, Addition) {
|
||||
EXPECT_EQ(1 + 1, 2);
|
||||
EXPECT_EQ(10 + 5, 15);
|
||||
EXPECT_EQ(-1 + 1, 0);
|
||||
}
|
||||
|
||||
// Test that demonstrates failure (using TEST_F to match the fixture)
|
||||
TEST_F(ExampleTest, ThisWillFail) {
|
||||
// This test will fail to demonstrate the failure output
|
||||
// EXPECT_EQ(2 + 2, 5); // Uncomment this line to see a failing test
|
||||
}
|
||||
|
||||
// Example of how you might test a Pokemon battle simulator component
|
||||
// This demonstrates testing the placeholder implementation we created
|
||||
#include "pokemon_battle_sim.h"
|
||||
|
||||
TEST(PokemonTest, PokemonCreation) {
|
||||
PokemonSim::Pokemon pikachu("Pikachu", 100);
|
||||
|
||||
// Test basic properties
|
||||
EXPECT_EQ(pikachu.getName(), "Pikachu");
|
||||
EXPECT_EQ(pikachu.getHealth(), 100);
|
||||
}
|
||||
|
||||
TEST(PokemonTest, PokemonHealthModification) {
|
||||
PokemonSim::Pokemon charizard("Charizard", 150);
|
||||
|
||||
EXPECT_EQ(charizard.getHealth(), 150);
|
||||
|
||||
charizard.setHealth(100);
|
||||
EXPECT_EQ(charizard.getHealth(), 100);
|
||||
}
|
||||
|
||||
TEST(PokemonTest, BattleSimulation) {
|
||||
PokemonSim::Pokemon pokemon1("Pokemon1", 50);
|
||||
PokemonSim::Pokemon pokemon2("Pokemon2", 30);
|
||||
|
||||
// Pokemon1 should win because it has more health
|
||||
bool result = PokemonSim::simulateBattle(pokemon1, pokemon2);
|
||||
EXPECT_TRUE(result); // Pokemon1 wins
|
||||
|
||||
// Both Pokemon should have taken damage
|
||||
EXPECT_LT(pokemon1.getHealth(), 50);
|
||||
EXPECT_LE(pokemon2.getHealth(), 0);
|
||||
}
|
||||
11
tests/unit/main.cpp
Normal file
11
tests/unit/main.cpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
// Main test runner for unit tests
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
|
||||
// Set up any global test configuration here
|
||||
// For example, you can set test filters, output format, etc.
|
||||
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
Reference in New Issue
Block a user