Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
df7c7e2b9f | ||
|
|
28cd218bf7 | ||
|
|
ff09435c04 | ||
|
|
6d76646d16 | ||
|
|
458091a7e2 | ||
|
|
696037c2d3 | ||
|
|
189ba44c1d | ||
|
|
bcdd11c5a3 | ||
|
|
19f638e091 | ||
|
|
38f4cf3464 | ||
|
|
a0880372f8 | ||
|
|
db0c3da413 |
@@ -12,12 +12,18 @@ java {
|
||||
}
|
||||
}
|
||||
|
||||
compileJava {
|
||||
options.encoding = 'UTF-8'
|
||||
}
|
||||
|
||||
repositories {
|
||||
// Spock releases are available from Maven Central
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.guava:guava:32.1.2-jre'
|
||||
implementation 'org.apache.commons:commons-lang3:3.13.0'
|
||||
// mandatory dependencies for using Spock
|
||||
implementation platform('org.apache.groovy:groovy-bom:4.0.15')
|
||||
implementation 'org.apache.groovy:groovy'
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<groovy.version>4.0.15</groovy.version>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
@@ -77,6 +79,16 @@
|
||||
</dependencyManagement>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>32.1.2-jre</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.13.0</version>
|
||||
</dependency>
|
||||
<!-- Mandatory dependencies for using Spock -->
|
||||
<dependency>
|
||||
<groupId>org.spockframework</groupId>
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package space.kklochko.simple_jbdd.tests.factories.meta;
|
||||
|
||||
import java.util.AbstractMap;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public abstract class AbstractTestCommandMetaDataFactory {
|
||||
abstract public ArrayList<AbstractMap.SimpleEntry<String, String>> create();
|
||||
}
|
||||
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package space.kklochko.simple_jbdd.tests.factories.meta;
|
||||
|
||||
import space.kklochko.simple_jbdd.tests.commands.AbstractTestCommand;
|
||||
import space.kklochko.simple_jbdd.tests.commands.decorators.AbstractDecorator;
|
||||
|
||||
import java.util.AbstractMap;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class TestCommandMetaDataFactory extends AbstractTestCommandMetaDataFactory {
|
||||
private AbstractTestCommand command;
|
||||
|
||||
public TestCommandMetaDataFactory(AbstractTestCommand command) {
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
public ArrayList<AbstractMap.SimpleEntry<String, String>> create() {
|
||||
ArrayList<AbstractMap.SimpleEntry<String, String>> blocks = new ArrayList<>();
|
||||
AbstractTestCommand currentCommand = command;
|
||||
Class<?> aClass = command.getClass();
|
||||
|
||||
while(AbstractDecorator.class.isAssignableFrom(aClass)) {
|
||||
AbstractDecorator block = (AbstractDecorator) currentCommand;
|
||||
blocks.add(new AbstractMap.SimpleEntry<>(block.getType(), block.getLabel()));
|
||||
|
||||
currentCommand = block.getCommand();
|
||||
aClass = currentCommand.getClass();
|
||||
}
|
||||
|
||||
blocks.add(0, (new AbstractMap.SimpleEntry<>(currentCommand.getType(), currentCommand.getLabel())));
|
||||
|
||||
return blocks;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package space.kklochko.simple_jbdd.tests.loaders;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public abstract class AbstractNameClassLoader {
|
||||
public abstract Set<Class<?>> load();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package space.kklochko.simple_jbdd.tests.loaders;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Set;
|
||||
|
||||
public class ClassLoader<T> {
|
||||
|
||||
AbstractNameClassLoader nameLoader;
|
||||
|
||||
public ClassLoader(AbstractNameClassLoader nameLoader) {
|
||||
this.nameLoader = nameLoader;
|
||||
}
|
||||
|
||||
public ArrayList<T> load() {
|
||||
Set<Class<?>> names = nameLoader.load();
|
||||
ArrayList<T> classes = new ArrayList<>();
|
||||
|
||||
for(Class<?> name : names) {
|
||||
SingleClassLoader loader = new SingleClassLoader(name.getName());
|
||||
classes.add((T) loader.load());
|
||||
}
|
||||
|
||||
return classes;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package space.kklochko.simple_jbdd.tests.loaders;
|
||||
|
||||
import com.google.common.reflect.ClassPath;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.Set;
|
||||
|
||||
public class NameClassLoader extends AbstractNameClassLoader {
|
||||
public Set<Class<?>> load() {
|
||||
try {
|
||||
return ClassPath.from(java.lang.ClassLoader.getSystemClassLoader())
|
||||
.getAllClasses()
|
||||
.stream()
|
||||
// exclude the Test class, which indicate a Test class
|
||||
.filter(aClass -> !aClass.getName().endsWith(".Test"))
|
||||
.filter(aClass -> aClass.getName().endsWith("Test"))
|
||||
// include classes which extends the Test class
|
||||
.filter(aClass -> space.kklochko.simple_jbdd.tests.Test.class.isAssignableFrom(aClass.load()))
|
||||
.filter(aClass -> aClass.getName().startsWith("space"))
|
||||
.map(aClass -> aClass.load())
|
||||
.collect(Collectors.toSet());
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package space.kklochko.simple_jbdd.tests.loaders;
|
||||
|
||||
public class SingleClassLoader {
|
||||
String className;
|
||||
|
||||
public SingleClassLoader(String className) {
|
||||
setClassName(className);
|
||||
}
|
||||
|
||||
public Class<?> load() {
|
||||
try {
|
||||
return Class.forName(getClassName());
|
||||
} catch (ClassNotFoundException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public String getClassName() {
|
||||
return className;
|
||||
}
|
||||
|
||||
public void setClassName(String className) {
|
||||
this.className = className;
|
||||
}
|
||||
}
|
||||
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package space.kklochko.simple_jbdd.tests.reports.fmt;
|
||||
|
||||
import java.util.AbstractMap;
|
||||
import java.util.ArrayList;
|
||||
|
||||
abstract public class AbstractTestReportFormatter {
|
||||
private ArrayList<AbstractMap.SimpleEntry<String, String>> testMetaData;
|
||||
private boolean isPassed;
|
||||
|
||||
public AbstractTestReportFormatter(ArrayList<AbstractMap.SimpleEntry<String, String>> testMetaData, boolean isPassed) {
|
||||
setTestMetaData(testMetaData);
|
||||
setPassed(isPassed);
|
||||
}
|
||||
|
||||
public abstract String format();
|
||||
|
||||
public ArrayList<AbstractMap.SimpleEntry<String, String>> getTestMetaData() {
|
||||
return testMetaData;
|
||||
}
|
||||
|
||||
public void setTestMetaData(ArrayList<AbstractMap.SimpleEntry<String, String>> testMetaData) {
|
||||
this.testMetaData = testMetaData;
|
||||
}
|
||||
|
||||
public boolean isPassed() {
|
||||
return isPassed;
|
||||
}
|
||||
|
||||
public void setPassed(boolean passed) {
|
||||
isPassed = passed;
|
||||
}
|
||||
}
|
||||
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
package space.kklochko.simple_jbdd.tests.reports.fmt;
|
||||
|
||||
import space.kklochko.simple_jbdd.tests.Test;
|
||||
|
||||
import java.util.AbstractMap;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class SimpleTestReportFormatter extends AbstractTestReportFormatter {
|
||||
final private String passedIcon = "✓";
|
||||
final private String failedIcon = "⨯";
|
||||
|
||||
public SimpleTestReportFormatter() {
|
||||
super(null, false);
|
||||
}
|
||||
|
||||
public SimpleTestReportFormatter(ArrayList<AbstractMap.SimpleEntry<String, String>> testMetaData, boolean isPassed) {
|
||||
super(testMetaData, isPassed);
|
||||
}
|
||||
|
||||
public String format() {
|
||||
String status = (isPassed()) ? getPassedIcon() : getFailedIcon();
|
||||
String title = getTestMetaData().get(0).getValue();
|
||||
|
||||
String result = String.format("%s %s\n", status, title);
|
||||
|
||||
for(int i=1; i<getTestMetaData().size(); ++i) {
|
||||
AbstractMap.SimpleEntry<String, String> pair = getTestMetaData().get(i);
|
||||
result += String.format("\t%s %s\n", pair.getKey(), pair.getValue());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public String getPassedIcon() {
|
||||
return passedIcon;
|
||||
}
|
||||
|
||||
public String getFailedIcon() {
|
||||
return failedIcon;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ import spock.lang.Title
|
||||
@Narrative("""The factory must generate a test command, so
|
||||
those tests check if factory generate them right.
|
||||
""")
|
||||
@Title("Unit test for TestCommandFactory")
|
||||
@Title("Unit tests for TestCommandFactory")
|
||||
class TestCommandFactorySpec extends Specification {
|
||||
def "If the map of methods have one test block."() {
|
||||
given: "I have a TestCommandFactory object"
|
||||
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
package space.kklochko.simple_jbdd.tests.factories.meta
|
||||
|
||||
import space.kklochko.simple_jbdd.test_examples.tests.SimpleFailedThenTest
|
||||
import space.kklochko.simple_jbdd.test_examples.tests.SimpleGivenWhenThenTest
|
||||
import space.kklochko.simple_jbdd.test_examples.tests.SimpleThenTest
|
||||
import space.kklochko.simple_jbdd.test_examples.tests.SimpleThenTestWithoutTitle
|
||||
import space.kklochko.simple_jbdd.tests.commands.AbstractTestCommand
|
||||
import space.kklochko.simple_jbdd.tests.commands.decorators.BlockDecorator
|
||||
import space.kklochko.simple_jbdd.tests.factories.TestCommandFactory
|
||||
import space.kklochko.simple_jbdd.tests.runners.SimpleTestRunner
|
||||
import spock.lang.Narrative
|
||||
import spock.lang.Specification
|
||||
import spock.lang.Subject
|
||||
import spock.lang.Title
|
||||
|
||||
@Narrative("""The factory must generate a test command metadata, so
|
||||
those tests check if the factory generate it right.
|
||||
""")
|
||||
@Title("Integrated tests for TestCommandMetaDataFactory")
|
||||
class IntegratedTestCommandMetaDataFactorySpec extends Specification {
|
||||
def "Metadata have the map of methods have one test block."() {
|
||||
given: "I have a command factory"
|
||||
def commandFactory = new TestCommandFactory();
|
||||
|
||||
and: "I have a command of a test"
|
||||
def command = commandFactory.create(testObject)
|
||||
|
||||
and: "I have a TestCommandMetaDataFactory object"
|
||||
@Subject
|
||||
def factory = new TestCommandMetaDataFactory(command)
|
||||
|
||||
when: "Parse the command to get metadata"
|
||||
def metadata = factory.create()
|
||||
|
||||
then: "the metadata have only two labels for Title and Then"
|
||||
expectedCount == metadata.size()
|
||||
|
||||
where: "Possible variants of tests"
|
||||
testObject || expectedCount
|
||||
new SimpleThenTest() || 2
|
||||
new SimpleGivenWhenThenTest() || 4
|
||||
new SimpleThenTestWithoutTitle() || 2
|
||||
new SimpleFailedThenTest() || 2
|
||||
}
|
||||
}
|
||||
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
package space.kklochko.simple_jbdd.tests.factories.meta
|
||||
|
||||
import space.kklochko.simple_jbdd.test_examples.tests.SimpleGivenWhenThenTest
|
||||
import space.kklochko.simple_jbdd.test_examples.tests.SimpleThenTest
|
||||
import space.kklochko.simple_jbdd.tests.commands.AbstractTestCommand
|
||||
import space.kklochko.simple_jbdd.tests.commands.decorators.BlockDecorator
|
||||
import space.kklochko.simple_jbdd.tests.factories.TestCommandFactory
|
||||
import spock.lang.Narrative;
|
||||
import spock.lang.Specification
|
||||
import spock.lang.Subject
|
||||
import spock.lang.Title
|
||||
|
||||
@Narrative("""The factory must generate a test command metadata, so
|
||||
those tests check if the factory generate it right.
|
||||
""")
|
||||
@Title("Unit tests for TestCommandMetaDataFactory")
|
||||
class TestCommandMetaDataFactorySpec extends Specification {
|
||||
def "If the map of methods have one test block."() {
|
||||
given: "I have a simple test with only Then block and its command."
|
||||
def test = Mock(AbstractTestCommand) {
|
||||
getType() >> "Title"
|
||||
getLabel() >> "A simple test"
|
||||
}
|
||||
|
||||
def command = GroovyMock(BlockDecorator) {
|
||||
getType() >> "Then"
|
||||
getLabel() >> "Then block"
|
||||
getCommand() >> test
|
||||
}
|
||||
|
||||
and: "I have a TestCommandMetaDataFactory object"
|
||||
@Subject
|
||||
def factory = new TestCommandMetaDataFactory(command)
|
||||
|
||||
when: "Parse the command to get metadata"
|
||||
def metadata = factory.create()
|
||||
|
||||
then: "The metadata have only two labels for Title and Then"
|
||||
2 == metadata.size()
|
||||
|
||||
and: "The format and the order must be the same"
|
||||
metadata == [
|
||||
new AbstractMap.SimpleEntry<>("Title", "A simple test"),
|
||||
new AbstractMap.SimpleEntry<>("Then", "Then block")
|
||||
]
|
||||
}
|
||||
|
||||
def "If the map of methods have GivenWhenThen blocks."() {
|
||||
given: "I have a simple test with only Then block and its command."
|
||||
def test = Mock(AbstractTestCommand) {
|
||||
getType() >> "Title"
|
||||
getLabel() >> "A simple test"
|
||||
}
|
||||
|
||||
and: "Bootstrapping the test command"
|
||||
def then = GroovyMock(BlockDecorator) {
|
||||
getType() >> "Then"
|
||||
getLabel() >> "Then block"
|
||||
getCommand() >> test
|
||||
}
|
||||
|
||||
def when = GroovyMock(BlockDecorator) {
|
||||
getType() >> "When"
|
||||
getLabel() >> "When block"
|
||||
getCommand() >> then
|
||||
}
|
||||
|
||||
def command = GroovyMock(BlockDecorator) {
|
||||
getType() >> "Given"
|
||||
getLabel() >> "Given block"
|
||||
getCommand() >> when
|
||||
}
|
||||
|
||||
and: "I have a TestCommandMetaDataFactory object"
|
||||
@Subject
|
||||
def factory = new TestCommandMetaDataFactory(command)
|
||||
|
||||
when: "Parse the command to get metadata"
|
||||
def metadata = factory.create()
|
||||
|
||||
then: "The metadata have only two labels for Title and Then"
|
||||
4 == metadata.size()
|
||||
|
||||
and: "The format and the order must be the same"
|
||||
metadata == [
|
||||
new AbstractMap.SimpleEntry<>("Title", "A simple test"),
|
||||
new AbstractMap.SimpleEntry<>("Given", "Given block"),
|
||||
new AbstractMap.SimpleEntry<>("When", "When block"),
|
||||
new AbstractMap.SimpleEntry<>("Then", "Then block")
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package space.kklochko.simple_jbdd.tests.loaders
|
||||
|
||||
import spock.lang.Narrative
|
||||
import spock.lang.Specification
|
||||
import spock.lang.Subject
|
||||
import spock.lang.Title
|
||||
|
||||
@Narrative("""The loader must return a test classes or null, so
|
||||
those tests check if responses are right.
|
||||
""")
|
||||
@Title("Integrated tests for ClassLoader.")
|
||||
class IntegratedClassLoader extends Specification {
|
||||
def "The classes have been loaded."() {
|
||||
given: "I have a name class loader"
|
||||
def nameLoader = new NameClassLoader()
|
||||
|
||||
and: "I have a loader for classes"
|
||||
@Subject
|
||||
def loader = new ClassLoader(nameLoader)
|
||||
|
||||
when: "The classes were loaded"
|
||||
def classes = loader.load()
|
||||
|
||||
then: "Checking that the test result is expected"
|
||||
3 == classes.size()
|
||||
}
|
||||
|
||||
def "The classes must be represent the Test interface."() {
|
||||
given: "I have a name class loader"
|
||||
def nameLoader = new NameClassLoader()
|
||||
|
||||
and: "I have a loader for classes"
|
||||
@Subject
|
||||
def loader = new ClassLoader(nameLoader)
|
||||
|
||||
when: "The classes were loaded"
|
||||
def classes = loader.load()
|
||||
|
||||
then: "Checking that the test result is expected"
|
||||
classes.every({ space.kklochko.simple_jbdd.tests.Test.class.isAssignableFrom(it) })
|
||||
}
|
||||
}
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package space.kklochko.simple_jbdd.tests.loaders
|
||||
|
||||
import spock.lang.Narrative
|
||||
import spock.lang.Specification
|
||||
import spock.lang.Subject
|
||||
import spock.lang.Title
|
||||
|
||||
@Narrative("""The loader must return a test class names or null, so
|
||||
those tests check if responses are right.
|
||||
""")
|
||||
@Title("Integrated tests for NameClassLoader.")
|
||||
class IntegratedNameClassLoader extends Specification {
|
||||
def "The class names have been loaded."() {
|
||||
given: "I have a class loader"
|
||||
@Subject
|
||||
def loader = new NameClassLoader()
|
||||
|
||||
when: "The class names were loaded"
|
||||
def classNames = loader.load()
|
||||
|
||||
then: "Checking that the test result is expected"
|
||||
3 == classNames.size()
|
||||
}
|
||||
}
|
||||
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package space.kklochko.simple_jbdd.tests.loaders
|
||||
|
||||
import space.kklochko.simple_jbdd.test_examples.tests.SimpleThenTest
|
||||
import spock.lang.Narrative
|
||||
import spock.lang.Specification
|
||||
import spock.lang.Subject
|
||||
import spock.lang.Title
|
||||
|
||||
@Narrative("""The loader must return a class or null, so
|
||||
those tests check if responses are right.
|
||||
""")
|
||||
@Title("Integrated tests for ClassLoader.")
|
||||
class IntegratedSingleClassLoader extends Specification {
|
||||
def "The class has been loaded."() {
|
||||
given: "I have a class loader"
|
||||
@Subject
|
||||
def loader = new SingleClassLoader(className)
|
||||
|
||||
when: "The class was loaded"
|
||||
def aClass = loader.load()
|
||||
|
||||
then: "Checking that the test result is expected"
|
||||
aClass == expectedClass
|
||||
|
||||
where: "Possible variants of tests"
|
||||
className || expectedClass
|
||||
"fourHundredFour" || null
|
||||
"space.kklochko.simple_jbdd.test_examples.tests.SimpleThenTest" || SimpleThenTest
|
||||
}
|
||||
}
|
||||
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
package space.kklochko.simple_jbdd.tests.reports.fmt
|
||||
|
||||
import space.kklochko.simple_jbdd.test_examples.tests.SimpleFailedThenTest
|
||||
import space.kklochko.simple_jbdd.test_examples.tests.SimpleGivenWhenThenTest
|
||||
import space.kklochko.simple_jbdd.test_examples.tests.SimpleThenTest
|
||||
import space.kklochko.simple_jbdd.test_examples.tests.SimpleThenTestWithoutTitle
|
||||
import space.kklochko.simple_jbdd.tests.commands.AbstractTestCommand
|
||||
import space.kklochko.simple_jbdd.tests.commands.decorators.BlockDecorator
|
||||
import space.kklochko.simple_jbdd.tests.factories.meta.TestCommandMetaDataFactory
|
||||
import spock.lang.Narrative
|
||||
import spock.lang.Specification
|
||||
import spock.lang.Subject
|
||||
import spock.lang.Title
|
||||
|
||||
@Narrative("""The formatter must generate a format for the test command metadata, so
|
||||
those tests check if the formatter generate it right.
|
||||
""")
|
||||
@Title("Unit tests for SimpleTestReportFormatter")
|
||||
class SimpleTestReportFormatterSpec extends Specification {
|
||||
def "If the map of methods have one test block."() {
|
||||
given: "I have a metaData"
|
||||
def metaData = [
|
||||
new AbstractMap.SimpleEntry<>("Title", "A simple test"),
|
||||
new AbstractMap.SimpleEntry<>("Then", "Then block")
|
||||
]
|
||||
|
||||
and: "I have the formatter object"
|
||||
@Subject
|
||||
def formatter = new SimpleTestReportFormatter(metaData, isPassed)
|
||||
|
||||
when: "Generate the format"
|
||||
def format = formatter.format()
|
||||
|
||||
then: "The format must be the same"
|
||||
expectedFormat == format
|
||||
|
||||
where: "Possible variants of tests"
|
||||
isPassed || expectedFormat
|
||||
true || "✓ A simple test\n\tThen Then block\n"
|
||||
false || "⨯ A simple test\n\tThen Then block\n"
|
||||
}
|
||||
|
||||
def "If the map of methods have GivenWhenThen blocks."() {
|
||||
given: "I have a metaData"
|
||||
def metaData = [
|
||||
new AbstractMap.SimpleEntry<>("Title", "A simple test"),
|
||||
new AbstractMap.SimpleEntry<>("Given", "Given block"),
|
||||
new AbstractMap.SimpleEntry<>("When", "When block"),
|
||||
new AbstractMap.SimpleEntry<>("Then", "Then block")
|
||||
]
|
||||
|
||||
and: "I have the formatter object"
|
||||
@Subject
|
||||
def formatter = new SimpleTestReportFormatter(metaData, isPassed)
|
||||
|
||||
when: "Generate the format"
|
||||
def format = formatter.format()
|
||||
|
||||
then: "The format must be the same"
|
||||
expectedFormat == format
|
||||
|
||||
where: "Possible variants of tests"
|
||||
isPassed || expectedFormat
|
||||
true || "✓ A simple test\n\tGiven Given block\n\tWhen When block\n\tThen Then block\n"
|
||||
false || "⨯ A simple test\n\tGiven Given block\n\tWhen When block\n\tThen Then block\n"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user