|
|
|
|
@@ -16,20 +16,15 @@
|
|
|
|
|
|
|
|
|
|
package org.springframework.cloud.contract.verifier.builder
|
|
|
|
|
|
|
|
|
|
import java.nio.file.Paths
|
|
|
|
|
|
|
|
|
|
import org.junit.Rule
|
|
|
|
|
import org.junit.rules.TemporaryFolder
|
|
|
|
|
import spock.lang.Issue
|
|
|
|
|
import spock.lang.Specification
|
|
|
|
|
import wiremock.com.google.common.collect.ArrayListMultimap
|
|
|
|
|
|
|
|
|
|
import org.springframework.cloud.contract.spec.Contract
|
|
|
|
|
import org.springframework.cloud.contract.verifier.FileSaver
|
|
|
|
|
import org.springframework.cloud.contract.verifier.TestGenerator
|
|
|
|
|
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
|
|
|
|
|
import org.springframework.cloud.contract.verifier.config.TestFramework
|
|
|
|
|
import org.springframework.cloud.contract.verifier.file.ContractFileScanner
|
|
|
|
|
import org.springframework.cloud.contract.verifier.file.ContractMetadata
|
|
|
|
|
import org.springframework.cloud.contract.verifier.util.SyntaxChecker
|
|
|
|
|
import org.springframework.util.FileSystemUtils
|
|
|
|
|
@@ -51,24 +46,12 @@ class SingleTestGeneratorSpec extends Specification {
|
|
|
|
|
File file
|
|
|
|
|
File tmp
|
|
|
|
|
|
|
|
|
|
private static final List<String> mockMvcJUnitRestAssured2ClassStrings = ['import com.jayway.jsonpath.DocumentContext;', 'import com.jayway.jsonpath.JsonPath;',
|
|
|
|
|
'import org.junit.FixMethodOrder;', 'import org.junit.Ignore;', 'import org.junit.Test;', 'import org.junit.runners.MethodSorters;',
|
|
|
|
|
'import static com.toomuchcoding.jsonassert.JsonAssertion.assertThatJson;', 'import static com.jayway.restassured.module.mockmvc.RestAssuredMockMvc.*;',
|
|
|
|
|
'@FixMethodOrder(MethodSorters.NAME_ASCENDING)', '@Test', '@Ignore', 'import com.jayway.restassured.module.mockmvc.specification.MockMvcRequestSpecification;',
|
|
|
|
|
'import com.jayway.restassured.response.ResponseOptions;', 'import static org.springframework.cloud.contract.verifier.assertion.SpringCloudContractAssertions.assertThat']
|
|
|
|
|
|
|
|
|
|
private static final List<String> mockMvcJUnitRestAssured3ClassStrings = ['import com.jayway.jsonpath.DocumentContext;', 'import com.jayway.jsonpath.JsonPath;',
|
|
|
|
|
'import org.junit.FixMethodOrder;', 'import org.junit.Ignore;', 'import org.junit.Test;', 'import org.junit.runners.MethodSorters;',
|
|
|
|
|
'import static com.toomuchcoding.jsonassert.JsonAssertion.assertThatJson;', 'import static io.restassured.module.mockmvc.RestAssuredMockMvc.*;',
|
|
|
|
|
'@FixMethodOrder(MethodSorters.NAME_ASCENDING)', '@Test', '@Ignore', 'import io.restassured.module.mockmvc.specification.MockMvcRequestSpecification;',
|
|
|
|
|
'import io.restassured.response.ResponseOptions;', 'import static org.springframework.cloud.contract.verifier.assertion.SpringCloudContractAssertions.assertThat']
|
|
|
|
|
|
|
|
|
|
private static final List<String> explicitJUnitRestAssured2ClassStrings = ['import com.jayway.jsonpath.DocumentContext;', 'import com.jayway.jsonpath.JsonPath;',
|
|
|
|
|
'import org.junit.FixMethodOrder;', 'import org.junit.Ignore;', 'import org.junit.Test;', 'import org.junit.runners.MethodSorters;',
|
|
|
|
|
'import static com.toomuchcoding.jsonassert.JsonAssertion.assertThatJson;', 'import static com.jayway.restassured.RestAssured.*;',
|
|
|
|
|
'@FixMethodOrder(MethodSorters.NAME_ASCENDING)', '@Test', '@Ignore', 'import com.jayway.restassured.specification.RequestSpecification;',
|
|
|
|
|
'import com.jayway.restassured.response.Response;', 'import static org.springframework.cloud.contract.verifier.assertion.SpringCloudContractAssertions.assertThat']
|
|
|
|
|
|
|
|
|
|
private static final List<String> explicitJUnitRestAssured3ClassStrings = ['import com.jayway.jsonpath.DocumentContext;', 'import com.jayway.jsonpath.JsonPath;',
|
|
|
|
|
'import org.junit.FixMethodOrder;', 'import org.junit.Ignore;', 'import org.junit.Test;', 'import org.junit.runners.MethodSorters;',
|
|
|
|
|
'import static com.toomuchcoding.jsonassert.JsonAssertion.assertThatJson;', 'import static io.restassured.RestAssured.*;',
|
|
|
|
|
@@ -76,37 +59,21 @@ class SingleTestGeneratorSpec extends Specification {
|
|
|
|
|
'import io.restassured.response.Response;', 'import static org.springframework.cloud.contract.verifier.assertion.SpringCloudContractAssertions.assertThat']
|
|
|
|
|
|
|
|
|
|
private static
|
|
|
|
|
final List<String> mockMvcJUnit5RestAssured2ClassStrings = ['import com.jayway.jsonpath.DocumentContext;', 'import com.jayway.jsonpath.JsonPath;',
|
|
|
|
|
'import org.junit.jupiter.api.Disabled;', 'import org.junit.jupiter.api.Test;',
|
|
|
|
|
'import static com.toomuchcoding.jsonassert.JsonAssertion.assertThatJson;', 'import static com.jayway.restassured.module.mockmvc.RestAssuredMockMvc.*;',
|
|
|
|
|
'@Test', '@Disabled', 'import com.jayway.restassured.module.mockmvc.specification.MockMvcRequestSpecification;',
|
|
|
|
|
'import com.jayway.restassured.response.ResponseOptions;', 'import static org.springframework.cloud.contract.verifier.assertion.SpringCloudContractAssertions.assertThat']
|
|
|
|
|
private static
|
|
|
|
|
final List<String> mockMvcJUnit5RestAssured3ClassStrings = ['import com.jayway.jsonpath.DocumentContext;', 'import com.jayway.jsonpath.JsonPath;',
|
|
|
|
|
'import org.junit.jupiter.api.Disabled;', 'import org.junit.jupiter.api.Test;',
|
|
|
|
|
'import static com.toomuchcoding.jsonassert.JsonAssertion.assertThatJson;', 'import static io.restassured.module.mockmvc.RestAssuredMockMvc.*;',
|
|
|
|
|
'@Test', '@Disabled', 'import io.restassured.module.mockmvc.specification.MockMvcRequestSpecification;',
|
|
|
|
|
'import io.restassured.response.ResponseOptions;', 'import static org.springframework.cloud.contract.verifier.assertion.SpringCloudContractAssertions.assertThat']
|
|
|
|
|
|
|
|
|
|
private static
|
|
|
|
|
final List<String> explicitJUnit5RestAssured2ClassStrings = ['import com.jayway.jsonpath.DocumentContext;', 'import com.jayway.jsonpath.JsonPath;',
|
|
|
|
|
'import org.junit.jupiter.api.Disabled;', 'import org.junit.jupiter.api.Test;',
|
|
|
|
|
'import static com.toomuchcoding.jsonassert.JsonAssertion.assertThatJson;', 'import static com.jayway.restassured.RestAssured.*;',
|
|
|
|
|
'@Test', '@Disabled', 'import com.jayway.restassured.specification.RequestSpecification;',
|
|
|
|
|
'import com.jayway.restassured.response.Response;', 'import static org.springframework.cloud.contract.verifier.assertion.SpringCloudContractAssertions.assertThat']
|
|
|
|
|
'import io.restassured.response.ResponseOptions;', 'import static org.springframework.cloud.contract.verifier.assertion.SpringCloudContractAssertions.assertThat',
|
|
|
|
|
'import org.junit.jupiter.api.TestMethodOrder', 'import org.junit.jupiter.api.MethodOrderer', '@TestMethodOrder(MethodOrderer.Alphanumeric.class)']
|
|
|
|
|
|
|
|
|
|
private static
|
|
|
|
|
final List<String> explicitJUnit5RestAssured3ClassStrings = ['import com.jayway.jsonpath.DocumentContext;', 'import com.jayway.jsonpath.JsonPath;',
|
|
|
|
|
'import org.junit.jupiter.api.Disabled;', 'import org.junit.jupiter.api.Test;',
|
|
|
|
|
'import static com.toomuchcoding.jsonassert.JsonAssertion.assertThatJson;', 'import static io.restassured.RestAssured.*;',
|
|
|
|
|
'@Test', '@Disabled', 'import io.restassured.specification.RequestSpecification;',
|
|
|
|
|
'import io.restassured.response.Response;', 'import static org.springframework.cloud.contract.verifier.assertion.SpringCloudContractAssertions.assertThat']
|
|
|
|
|
private static
|
|
|
|
|
final List<String> mockMvcTestNGRestAssured2ClassStrings = ['import com.jayway.jsonpath.DocumentContext;', 'import com.jayway.jsonpath.JsonPath;',
|
|
|
|
|
'import org.testng.annotations.Test;',
|
|
|
|
|
'import static com.toomuchcoding.jsonassert.JsonAssertion.assertThatJson;', 'import static com.jayway.restassured.module.mockmvc.RestAssuredMockMvc.*;',
|
|
|
|
|
'@Test', 'import com.jayway.restassured.module.mockmvc.specification.MockMvcRequestSpecification;',
|
|
|
|
|
'import com.jayway.restassured.response.ResponseOptions;', 'import static org.springframework.cloud.contract.verifier.assertion.SpringCloudContractAssertions.assertThat']
|
|
|
|
|
'import io.restassured.response.Response;', 'import static org.springframework.cloud.contract.verifier.assertion.SpringCloudContractAssertions.assertThat',
|
|
|
|
|
'org.junit.jupiter.api.TestMethodOrder', 'org.junit.jupiter.api.MethodOrderer', '@TestMethodOrder(MethodOrderer.Alphanumeric.class)']
|
|
|
|
|
|
|
|
|
|
private static
|
|
|
|
|
final List<String> mockMvcTestNGRestAssured3ClassStrings = ['import com.jayway.jsonpath.DocumentContext;', 'import com.jayway.jsonpath.JsonPath;',
|
|
|
|
|
'import org.testng.annotations.Test;',
|
|
|
|
|
@@ -114,13 +81,6 @@ class SingleTestGeneratorSpec extends Specification {
|
|
|
|
|
'@Test', 'import io.restassured.module.mockmvc.specification.MockMvcRequestSpecification;',
|
|
|
|
|
'import io.restassured.response.ResponseOptions;', 'import static org.springframework.cloud.contract.verifier.assertion.SpringCloudContractAssertions.assertThat']
|
|
|
|
|
|
|
|
|
|
private static
|
|
|
|
|
final List<String> explicitTestNGRestAssured2ClassStrings = ['import com.jayway.jsonpath.DocumentContext;', 'import com.jayway.jsonpath.JsonPath;',
|
|
|
|
|
'org.testng.annotations.Test;',
|
|
|
|
|
'import static com.toomuchcoding.jsonassert.JsonAssertion.assertThatJson;', 'import static com.jayway.restassured.RestAssured.*;',
|
|
|
|
|
'@Test', 'import com.jayway.restassured.specification.RequestSpecification;',
|
|
|
|
|
'import com.jayway.restassured.response.Response;', 'import static org.springframework.cloud.contract.verifier.assertion.SpringCloudContractAssertions.assertThat']
|
|
|
|
|
|
|
|
|
|
private static
|
|
|
|
|
final List<String> explicitTestNGRestAssured3ClassStrings = ['import com.jayway.jsonpath.DocumentContext;', 'import com.jayway.jsonpath.JsonPath;',
|
|
|
|
|
'import org.testng.annotations.Test;',
|
|
|
|
|
@@ -128,12 +88,6 @@ class SingleTestGeneratorSpec extends Specification {
|
|
|
|
|
'@Test', 'import io.restassured.specification.RequestSpecification;',
|
|
|
|
|
'import io.restassured.response.Response;', 'import static org.springframework.cloud.contract.verifier.assertion.SpringCloudContractAssertions.assertThat']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static final List<String> spockClassRestAssured2Strings = ['import com.jayway.jsonpath.DocumentContext', 'import com.jayway.jsonpath.JsonPath',
|
|
|
|
|
'import spock.lang.Ignore', 'import spock.lang.Specification', 'import spock.lang.Stepwise',
|
|
|
|
|
'import static com.toomuchcoding.jsonassert.JsonAssertion.assertThatJson', 'import static com.jayway.restassured.module.mockmvc.RestAssuredMockMvc.*',
|
|
|
|
|
'@Stepwise', '@Ignore', 'org.springframework.cloud.contract.verifier.assertion.SpringCloudContractAssertions.assertThat']
|
|
|
|
|
|
|
|
|
|
private static final List<String> spockClassRestAssured3Strings = ['import com.jayway.jsonpath.DocumentContext', 'import com.jayway.jsonpath.JsonPath',
|
|
|
|
|
'import spock.lang.Ignore', 'import spock.lang.Specification', 'import spock.lang.Stepwise',
|
|
|
|
|
'import static com.toomuchcoding.jsonassert.JsonAssertion.assertThatJson', 'import static io.restassured.module.mockmvc.RestAssuredMockMvc.*',
|
|
|
|
|
@@ -185,7 +139,7 @@ class SingleTestGeneratorSpec extends Specification {
|
|
|
|
|
FileSystemUtils.copyRecursively(classpath, tmp)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private writeContract(File file) {
|
|
|
|
|
private static writeContract(File file) {
|
|
|
|
|
file.write('''
|
|
|
|
|
org.springframework.cloud.contract.spec.Contract.make {
|
|
|
|
|
request {
|
|
|
|
|
@@ -205,7 +159,8 @@ class SingleTestGeneratorSpec extends Specification {
|
|
|
|
|
ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties()
|
|
|
|
|
properties.testFramework = testFramework
|
|
|
|
|
properties.testMode = mode
|
|
|
|
|
ContractMetadata contract = new ContractMetadata(file.toPath(), true, 1, order, convertAsCollection(new File('/'), file))
|
|
|
|
|
ContractMetadata contract = new ContractMetadata(file.toPath(), true, 1, 2,
|
|
|
|
|
convertAsCollection(new File('/'), file))
|
|
|
|
|
JavaTestGenerator testGenerator = new JavaTestGenerator()
|
|
|
|
|
|
|
|
|
|
when:
|
|
|
|
|
@@ -216,15 +171,15 @@ class SingleTestGeneratorSpec extends Specification {
|
|
|
|
|
and:
|
|
|
|
|
asserter(clazz)
|
|
|
|
|
where:
|
|
|
|
|
testFramework | order | mode | classStrings | asserter
|
|
|
|
|
JUNIT | 2 | MOCKMVC | mockMvcJUnitRestAssured3ClassStrings | JAVA_ASSERTER
|
|
|
|
|
JUNIT | 2 | EXPLICIT | explicitJUnitRestAssured3ClassStrings | JAVA_ASSERTER
|
|
|
|
|
JUNIT5 | null | MOCKMVC | mockMvcJUnit5RestAssured3ClassStrings | JAVA_ASSERTER
|
|
|
|
|
JUNIT5 | null | EXPLICIT | explicitJUnit5RestAssured3ClassStrings | JAVA_ASSERTER
|
|
|
|
|
TESTNG | null | MOCKMVC | mockMvcTestNGRestAssured3ClassStrings | JAVA_ASSERTER
|
|
|
|
|
TESTNG | null | EXPLICIT | explicitTestNGRestAssured3ClassStrings | JAVA_ASSERTER
|
|
|
|
|
SPOCK | 2 | MOCKMVC | spockClassRestAssured3Strings | GROOVY_ASSERTER
|
|
|
|
|
SPOCK | 2 | EXPLICIT | explicitSpockRestAssured3ClassStrings | GROOVY_ASSERTER
|
|
|
|
|
testFramework | mode | classStrings | asserter
|
|
|
|
|
JUNIT | MOCKMVC | mockMvcJUnitRestAssured3ClassStrings | JAVA_ASSERTER
|
|
|
|
|
JUNIT | EXPLICIT | explicitJUnitRestAssured3ClassStrings | JAVA_ASSERTER
|
|
|
|
|
JUNIT5 | MOCKMVC | mockMvcJUnit5RestAssured3ClassStrings | JAVA_ASSERTER
|
|
|
|
|
JUNIT5 | EXPLICIT | explicitJUnit5RestAssured3ClassStrings | JAVA_ASSERTER
|
|
|
|
|
TESTNG | MOCKMVC | mockMvcTestNGRestAssured3ClassStrings | JAVA_ASSERTER
|
|
|
|
|
TESTNG | EXPLICIT | explicitTestNGRestAssured3ClassStrings | JAVA_ASSERTER
|
|
|
|
|
SPOCK | MOCKMVC | spockClassRestAssured3Strings | GROOVY_ASSERTER
|
|
|
|
|
SPOCK | EXPLICIT | explicitSpockRestAssured3ClassStrings | GROOVY_ASSERTER
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def 'should build test class for #testFramework when the path contains bizarre signs'() {
|
|
|
|
|
@@ -382,10 +337,12 @@ class SingleTestGeneratorSpec extends Specification {
|
|
|
|
|
and:
|
|
|
|
|
ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties()
|
|
|
|
|
properties.testFramework = testFramework
|
|
|
|
|
ContractMetadata contract = new ContractMetadata(file.toPath(), true, 1, order, convertAsCollection(new File('/'), file))
|
|
|
|
|
ContractMetadata contract = new ContractMetadata(file.toPath(), true, 1, 2,
|
|
|
|
|
convertAsCollection(new File('/'), file))
|
|
|
|
|
contract.ignored >> true
|
|
|
|
|
and:
|
|
|
|
|
ContractMetadata contract2 = new ContractMetadata(secondFile.toPath(), true, 1, order, convertAsCollection(new File('/'), secondFile))
|
|
|
|
|
ContractMetadata contract2 = new ContractMetadata(secondFile.
|
|
|
|
|
toPath(), true, 1, 2, convertAsCollection(new File('/'), secondFile))
|
|
|
|
|
contract2.ignored >> true
|
|
|
|
|
and:
|
|
|
|
|
JavaTestGenerator testGenerator = new JavaTestGenerator()
|
|
|
|
|
@@ -401,15 +358,15 @@ class SingleTestGeneratorSpec extends Specification {
|
|
|
|
|
asserter(clazz)
|
|
|
|
|
|
|
|
|
|
where:
|
|
|
|
|
testFramework | order | classStrings | asserter
|
|
|
|
|
JUNIT | 2 | mockMvcJUnitRestAssured3ClassStrings | JAVA_ASSERTER
|
|
|
|
|
JUNIT5 | null | mockMvcJUnit5RestAssured3ClassStrings | JAVA_ASSERTER
|
|
|
|
|
TESTNG | null | mockMvcTestNGRestAssured3ClassStrings | JAVA_ASSERTER
|
|
|
|
|
SPOCK | 2 | spockClassRestAssured3Strings | GROOVY_ASSERTER
|
|
|
|
|
testFramework | classStrings | asserter
|
|
|
|
|
JUNIT | mockMvcJUnitRestAssured3ClassStrings | JAVA_ASSERTER
|
|
|
|
|
JUNIT5 | mockMvcJUnit5RestAssured3ClassStrings | JAVA_ASSERTER
|
|
|
|
|
TESTNG | mockMvcTestNGRestAssured3ClassStrings | JAVA_ASSERTER
|
|
|
|
|
SPOCK | spockClassRestAssured3Strings | GROOVY_ASSERTER
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Issue('#30')
|
|
|
|
|
def 'should ignore a test if the contract is ignored in the dsl with #testFramework and ignore annotation #ignoreAnnotation'() {
|
|
|
|
|
def 'should ignore a test if the contract is ignored in the dsl'() {
|
|
|
|
|
given:
|
|
|
|
|
File secondFile = tmpFolder.newFile()
|
|
|
|
|
secondFile.write('''
|
|
|
|
|
@@ -428,7 +385,8 @@ class SingleTestGeneratorSpec extends Specification {
|
|
|
|
|
ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties()
|
|
|
|
|
properties.testFramework = testFramework
|
|
|
|
|
and:
|
|
|
|
|
ContractMetadata contract2 = new ContractMetadata(secondFile.toPath(), true, 1, order, convertAsCollection(new File('/'), file))
|
|
|
|
|
ContractMetadata contract2 = new ContractMetadata(secondFile.
|
|
|
|
|
toPath(), true, 1, 2, convertAsCollection(new File('/'), file))
|
|
|
|
|
contract2.ignored >> false
|
|
|
|
|
and:
|
|
|
|
|
JavaTestGenerator testGenerator = new JavaTestGenerator()
|
|
|
|
|
@@ -444,11 +402,11 @@ class SingleTestGeneratorSpec extends Specification {
|
|
|
|
|
asserter(clazz)
|
|
|
|
|
|
|
|
|
|
where:
|
|
|
|
|
testFramework | order | classStrings | ignoreAnnotation | asserter
|
|
|
|
|
JUNIT | 2 | mockMvcJUnitRestAssured3ClassStrings | '@Ignore' | JAVA_ASSERTER
|
|
|
|
|
JUNIT5 | null | mockMvcJUnit5RestAssured3ClassStrings | '@Disabled' | JAVA_ASSERTER
|
|
|
|
|
TESTNG | null | mockMvcTestNGRestAssured3ClassStrings | '@Test(enabled = false)' | JAVA_ASSERTER
|
|
|
|
|
SPOCK | 2 | spockClassRestAssured3Strings | '@Ignore' | GROOVY_ASSERTER
|
|
|
|
|
testFramework | classStrings | ignoreAnnotation | asserter
|
|
|
|
|
JUNIT | mockMvcJUnitRestAssured3ClassStrings | '@Ignore' | JAVA_ASSERTER
|
|
|
|
|
JUNIT5 | mockMvcJUnit5RestAssured3ClassStrings | '@Disabled' | JAVA_ASSERTER
|
|
|
|
|
TESTNG | mockMvcTestNGRestAssured3ClassStrings | '@Test(enabled = false)' | JAVA_ASSERTER
|
|
|
|
|
SPOCK | spockClassRestAssured3Strings | '@Ignore' | GROOVY_ASSERTER
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def 'should not allow the usage of ignore annotations for TestNG '() {
|
|
|
|
|
@@ -740,21 +698,6 @@ class SingleTestGeneratorSpec extends Specification {
|
|
|
|
|
testFramework << [JUNIT, JUNIT5, TESTNG, SPOCK]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def 'should throw exception in JUnit5 when contract belongs to scenario'() {
|
|
|
|
|
given:
|
|
|
|
|
ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties()
|
|
|
|
|
properties.testFramework = JUNIT5
|
|
|
|
|
properties.testMode = mode
|
|
|
|
|
ContractMetadata contract = new ContractMetadata(file.toPath(), true, 1, 1, convertAsCollection(new File('/'), file))
|
|
|
|
|
JavaTestGenerator testGenerator = new JavaTestGenerator()
|
|
|
|
|
when:
|
|
|
|
|
testGenerator.buildClass(properties, [contract], 'com/foo', new SingleTestGenerator.GeneratedClassData('test', 'test', file.toPath()))
|
|
|
|
|
then:
|
|
|
|
|
thrown(UnsupportedOperationException)
|
|
|
|
|
where:
|
|
|
|
|
mode << [MOCKMVC, EXPLICIT, JAXRSCLIENT]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static String getTestName(TestFramework testFramework) {
|
|
|
|
|
testFramework == SPOCK ? 'Spec.groovy' : 'Test.java'
|
|
|
|
|
}
|
|
|
|
|
|