diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e6a47a0bd7..872c05f068 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,11 +12,9 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Set up JDK 1.8 + - name: Set up JDK 12 uses: actions/setup-java@v1 with: - java-version: 1.8 - - name: Grant execute permission for gradlew - run: chmod +x gradlew + java-version: 12 - name: Build run: ./gradlew build \ No newline at end of file diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 2b28ab4ffe..69cf263062 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -10,12 +10,10 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Set up JDK 1.8 + - name: Set up JDK 12 uses: actions/setup-java@v1 with: - java-version: 1.8 - - name: Grant execute permission for gradlew - run: chmod +x gradlew + java-version: 12 - name: Build run: ./gradlew build - name: Publish to Maven Repository diff --git a/core/build.gradle b/core/build.gradle index 8a9b8f6e48..a0bb0a17c4 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -40,6 +40,12 @@ android { freeCompilerArgs = ["-Xjvm-default=all"] jvmTarget = "1.8" } + + testOptions { + unitTests { + includeAndroidResources = true + } + } } dependencies { @@ -47,7 +53,7 @@ dependencies { implementation project(':utils') - implementation 'androidx.appcompat:appcompat:1.2.0' + implementation 'androidx.appcompat:appcompat:1.3.0' implementation 'commons-io:commons-io:2.5' implementation 'org.apache.commons:commons-lang3:3.8.1' implementation 'org.iban4j:iban4j:3.2.1' @@ -58,7 +64,9 @@ dependencies { api "com.squareup.okhttp3:okhttp:${project.okhttpVersion}" api 'com.google.code.gson:gson:2.8.6' - testImplementation 'junit:junit:4.13' + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.5.1' + testImplementation "com.squareup.okhttp3:mockwebserver:${project.okhttpVersion}" androidTestImplementation 'androidx.test:rules:1.3.0' androidTestImplementation 'androidx.test:runner:1.3.0' diff --git a/core/src/androidTest/java/io/snabble/sdk/CodeTemplateTest.java b/core/src/test/java/io/snabble/sdk/CodeTemplateTest.java similarity index 98% rename from core/src/androidTest/java/io/snabble/sdk/CodeTemplateTest.java rename to core/src/test/java/io/snabble/sdk/CodeTemplateTest.java index e443b64297..c9cdb0f634 100644 --- a/core/src/androidTest/java/io/snabble/sdk/CodeTemplateTest.java +++ b/core/src/test/java/io/snabble/sdk/CodeTemplateTest.java @@ -2,12 +2,15 @@ import org.junit.Assert; import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; import java.math.BigDecimal; import io.snabble.sdk.codes.ScannedCode; import io.snabble.sdk.codes.templates.CodeTemplate; +@RunWith(RobolectricTestRunner.class) public class CodeTemplateTest { @Test public void testTemplateParser() { diff --git a/core/src/androidTest/java/io/snabble/sdk/EAN13Test.java b/core/src/test/java/io/snabble/sdk/EAN13Test.java similarity index 100% rename from core/src/androidTest/java/io/snabble/sdk/EAN13Test.java rename to core/src/test/java/io/snabble/sdk/EAN13Test.java diff --git a/core/src/androidTest/java/io/snabble/sdk/EAN8Test.java b/core/src/test/java/io/snabble/sdk/EAN8Test.java similarity index 100% rename from core/src/androidTest/java/io/snabble/sdk/EAN8Test.java rename to core/src/test/java/io/snabble/sdk/EAN8Test.java diff --git a/core/src/androidTest/java/io/snabble/sdk/EncodedCodesGeneratorTest.java b/core/src/test/java/io/snabble/sdk/EncodedCodesGeneratorTest.java similarity index 96% rename from core/src/androidTest/java/io/snabble/sdk/EncodedCodesGeneratorTest.java rename to core/src/test/java/io/snabble/sdk/EncodedCodesGeneratorTest.java index ce378ba969..1841360dc3 100644 --- a/core/src/androidTest/java/io/snabble/sdk/EncodedCodesGeneratorTest.java +++ b/core/src/test/java/io/snabble/sdk/EncodedCodesGeneratorTest.java @@ -1,14 +1,11 @@ package io.snabble.sdk; -import androidx.test.annotation.UiThreadTest; -import androidx.test.filters.LargeTest; -import androidx.test.runner.AndroidJUnit4; - import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; import java.io.IOException; import java.util.ArrayList; @@ -19,11 +16,9 @@ import io.snabble.sdk.encodedcodes.EncodedCodesGenerator; import io.snabble.sdk.encodedcodes.EncodedCodesOptions; -@RunWith(AndroidJUnit4.class) -@LargeTest +@RunWith(RobolectricTestRunner.class) public class EncodedCodesGeneratorTest extends SnabbleSdkTest { @Test - @UiThreadTest public void testGeneration() { EncodedCodesOptions options = new EncodedCodesOptions.Builder(project) .prefix("+") @@ -43,7 +38,6 @@ public void testGeneration() { } @Test - @UiThreadTest public void testSplitAtMaxChars() { EncodedCodesOptions options = new EncodedCodesOptions.Builder(project) .prefix("+") @@ -66,7 +60,6 @@ public void testSplitAtMaxChars() { } @Test - @UiThreadTest public void testSplitAtMaxCodes() { EncodedCodesOptions options = new EncodedCodesOptions.Builder(project) .prefix("+") @@ -89,7 +82,6 @@ public void testSplitAtMaxCodes() { } @Test - @UiThreadTest public void testSplitAgeRestrictedProducts() { EncodedCodesOptions options = new EncodedCodesOptions.Builder(project) .prefix("+") @@ -117,7 +109,6 @@ public void testSplitAgeRestrictedProducts() { } @Test - @UiThreadTest public void testSplitAgeRestrictedProductsInMultipleCodes() { EncodedCodesOptions options = new EncodedCodesOptions.Builder(project) .prefix("+") @@ -146,7 +137,6 @@ public void testSplitAgeRestrictedProductsInMultipleCodes() { } @Test - @UiThreadTest public void testSplitAgeRestrictedProductsInMultipleCodes2() { EncodedCodesOptions options = new EncodedCodesOptions.Builder(project) .prefix("+") @@ -178,7 +168,6 @@ public void testSplitAgeRestrictedProductsInMultipleCodes2() { } @Test - @UiThreadTest public void testSplitWithOnlyOneRestrictedProduct() { EncodedCodesOptions options = new EncodedCodesOptions.Builder(project) .prefix("+") @@ -204,7 +193,6 @@ public void testSplitWithOnlyOneRestrictedProduct() { } @Test - @UiThreadTest public void testKnauber() { EncodedCodesOptions options = new EncodedCodesOptions.Builder(project) .prefix("") @@ -234,7 +222,6 @@ public void testKnauber() { } @Test - @UiThreadTest public void testCoupons() { EncodedCodesOptions options = new EncodedCodesOptions.Builder(project) .prefix("") @@ -263,7 +250,6 @@ public void testCoupons() { } @Test - @UiThreadTest public void testEdeka() { EncodedCodesOptions options = new EncodedCodesOptions.Builder(project) .prefix("XE") @@ -288,7 +274,6 @@ public void testEdeka() { } @Test - @UiThreadTest public void testEdekaWithOverflow() { EncodedCodesOptions options = new EncodedCodesOptions.Builder(project) .prefix("XE") @@ -314,7 +299,6 @@ public void testEdekaWithOverflow() { } @Test - @UiThreadTest public void testEdekaWithRestrictedProduct() { EncodedCodesOptions options = new EncodedCodesOptions.Builder(project) .prefix("XE") @@ -343,7 +327,6 @@ public void testEdekaWithRestrictedProduct() { } @Test - @UiThreadTest public void testExpensiveItemsSortedToBottom() { EncodedCodesOptions options = new EncodedCodesOptions.Builder(project) .prefix("") @@ -367,7 +350,6 @@ public void testExpensiveItemsSortedToBottom() { } @Test - @UiThreadTest public void testCSVFormat() { EncodedCodesOptions options = new EncodedCodesOptions.Builder(project) .prefix("snabble;\n") @@ -394,7 +376,6 @@ public void testCSVFormat() { } @Test - @UiThreadTest public void testCSVv2Format() { EncodedCodesOptions options = new EncodedCodesOptions.Builder(project) .prefix("snabble;{qrCodeIndex};{qrCodeCount}\n") @@ -422,9 +403,8 @@ public void testCSVv2Format() { } @Test - @UiThreadTest public void testTransmissionTemplates() throws IOException, Snabble.SnabbleException { - String[] sql = IOUtils.readLines(context.getAssets().open("transmission_template.sql")).toArray(new String[0]); + String[] sql = loadSql("transmission_template").split("\n"); withDb("test_1_25.sqlite3", false, sql); EncodedCodesOptions options = new EncodedCodesOptions.Builder(project) diff --git a/core/src/androidTest/java/io/snabble/sdk/GS1CodeTest.kt b/core/src/test/java/io/snabble/sdk/GS1CodeTest.kt similarity index 100% rename from core/src/androidTest/java/io/snabble/sdk/GS1CodeTest.kt rename to core/src/test/java/io/snabble/sdk/GS1CodeTest.kt diff --git a/core/src/androidTest/java/io/snabble/sdk/NetworkTest.java b/core/src/test/java/io/snabble/sdk/NetworkTest.java similarity index 86% rename from core/src/androidTest/java/io/snabble/sdk/NetworkTest.java rename to core/src/test/java/io/snabble/sdk/NetworkTest.java index 239a6005c4..a6759fe5bd 100644 --- a/core/src/androidTest/java/io/snabble/sdk/NetworkTest.java +++ b/core/src/test/java/io/snabble/sdk/NetworkTest.java @@ -1,11 +1,9 @@ package io.snabble.sdk; -import androidx.test.filters.LargeTest; -import androidx.test.runner.AndroidJUnit4; - import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; import java.io.IOException; @@ -13,8 +11,7 @@ import okhttp3.Request; import okhttp3.Response; -@RunWith(AndroidJUnit4.class) -@LargeTest +@RunWith(RobolectricTestRunner.class) public class NetworkTest extends SnabbleSdkTest { @Test public void testLetsEncrypt() throws IOException { diff --git a/core/src/androidTest/java/io/snabble/sdk/ProductDatabaseTest.java b/core/src/test/java/io/snabble/sdk/ProductDatabaseTest.java similarity index 84% rename from core/src/androidTest/java/io/snabble/sdk/ProductDatabaseTest.java rename to core/src/test/java/io/snabble/sdk/ProductDatabaseTest.java index 355834daf3..a15504961a 100644 --- a/core/src/androidTest/java/io/snabble/sdk/ProductDatabaseTest.java +++ b/core/src/test/java/io/snabble/sdk/ProductDatabaseTest.java @@ -4,23 +4,21 @@ import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; -import androidx.test.filters.LargeTest; -import androidx.test.runner.AndroidJUnit4; - import org.apache.commons.io.IOUtils; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; -import java.io.FileReader; import java.io.IOException; import java.io.InputStream; +import java.nio.charset.StandardCharsets; import java.util.HashSet; -import java.util.List; import java.util.Set; import java.util.concurrent.CountDownLatch; @@ -31,8 +29,7 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -@RunWith(AndroidJUnit4.class) -@LargeTest +@RunWith(RobolectricTestRunner.class) public class ProductDatabaseTest extends SnabbleSdkTest { @Test @@ -129,32 +126,61 @@ public void testSaleStop() { } @Test - public void testFindBySkuOnline() { - ProductDatabase productDatabase = project.getProductDatabase(); - final Product product = findBySkuBlocking(productDatabase, "1"); - assertEquals(product.getSku(), "1"); - containsCode(product, "4008258510001"); - assertEquals(product.getTransmissionCode(project, "default", "0", 0), null); - - final Product product2 = findBySkuBlocking(productDatabase, "2"); - assertEquals(product2.getSku(), "2"); - containsCode(product2, "asdf123"); - assertEquals(product2.getTransmissionCode(project, "default", "asdf123", 0), "trans123"); + public void testFindBySkuOnline() throws Throwable { + runAsyncHandlerCode(() -> { + ProductDatabase productDatabase = project.getProductDatabase(); + final Product product = findBySkuBlocking(productDatabase, "1"); + assertEquals(product.getSku(), "1"); + containsCode(product, "4008258510001"); + assertEquals(product.getTransmissionCode(project, "default", "0", 0), null); + + final Product product2 = findBySkuBlocking(productDatabase, "2"); + assertEquals(product2.getSku(), "2"); + containsCode(product2, "asdf123"); + assertEquals(product2.getTransmissionCode(project, "default", "asdf123", 0), "trans123"); + + assertNull(findBySkuBlocking(productDatabase, "unknownCode")); + }); + } - assertNull(findBySkuBlocking(productDatabase, "unknownCode")); + private boolean working = false; + private Throwable error = null; + private void runAsyncHandlerCode(Runnable runnable) throws Throwable { + assertFalse("There must be no other running test", working); + working = true; + error = null; + new Thread(() -> { + try { + runnable.run(); + } catch (Throwable throwable) { + error = throwable; + } finally { + working = false; + } + }).start(); + while (working) { + try { + Thread.sleep(100); + } catch (InterruptedException e) { + } + Robolectric.flushForegroundThreadScheduler(); + } + if(error != null) throw error; } @Test - public void testFindBySkuOnlineWithShopSpecificPrice() { - ProductDatabase productDatabase = project.getProductDatabase(); - final Product product = findBySkuBlocking(productDatabase, "1"); - assertEquals(product.getSku(), "1"); - assertEquals(product.getListPrice(), 399); - - project.setCheckedInShop(project.getShops()[1]); - final Product product2 = findBySkuBlocking(productDatabase, "1"); - assertEquals(product2.getSku(), "1"); - assertEquals(product2.getListPrice(), 299); + public void testFindBySkuOnlineWithShopSpecificPrice() throws Throwable { + runAsyncHandlerCode(() -> { + ProductDatabase productDatabase = project.getProductDatabase(); + final Product product = findBySkuBlocking(productDatabase, "1"); + assertEquals(product.getSku(), "1"); + assertEquals(product.getListPrice(), 399); + + project.setCheckedInShop(project.getShops()[1]); + final Product product2 = findBySkuBlocking(productDatabase, "1"); + assertEquals(product2.getSku(), "1"); + assertEquals(product2.getListPrice(), 299); + }); } @Test @@ -278,7 +304,7 @@ public void testApplyChangeSetInvalidDoesNotModifyDb() throws IOException { @Test public void testFullUpdate() throws IOException { ProductDatabase productDatabase = project.getProductDatabase(); - productDatabase.applyFullUpdate(context.getAssets().open("update_1_25.sqlite3")); + productDatabase.applyFullUpdate(getInputStream("update_1_25.sqlite3")); Product product = productDatabase.findBySku("1337"); @@ -292,7 +318,7 @@ public void testFullUpdate() throws IOException { @Test public void testFullUpdateDoesNotModifyOnCorruptedFile() throws IOException { ProductDatabase productDatabase = project.getProductDatabase(); - byte[] bytes = IOUtils.toByteArray(context.getAssets().open("update_1_25.sqlite3")); + byte[] bytes = IOUtils.toByteArray(getInputStream("update_1_25.sqlite3")); for (int i = 0; i < bytes.length; i++) { if (i % 4 == 0) { bytes[i] = 42; @@ -311,7 +337,7 @@ public void testFullUpdateDoesNotModifyOnCorruptedFile() throws IOException { @Test public void testFullUpdateDoesNotModifyOnWrongMajorVersion() throws IOException { ProductDatabase productDatabase = project.getProductDatabase(); - InputStream is = context.getResources().getAssets().open("update_1_25.sqlite3"); + InputStream is = getInputStream("update_1_25.sqlite3"); File outputFile = context.getDatabasePath("update_1_25.sqlite3"); FileOutputStream fos = new FileOutputStream(outputFile); IOUtils.copy(is, fos); @@ -418,7 +444,7 @@ public void testMultiplePricingCategories() throws IOException, Snabble.SnabbleE @Test public void testTransmissionTemplates() throws IOException, Snabble.SnabbleException { - String[] sql = IOUtils.readLines(context.getAssets().open("transmission_template.sql")).toArray(new String[0]); + String[] sql = loadSql("transmission_template").split("\n"); withDb("test_1_25.sqlite3", false, sql); ProductDatabase productDatabase = project.getProductDatabase(); diff --git a/core/src/androidTest/java/io/snabble/sdk/ShoppingCartTest.java b/core/src/test/java/io/snabble/sdk/ShoppingCartTest.java similarity index 100% rename from core/src/androidTest/java/io/snabble/sdk/ShoppingCartTest.java rename to core/src/test/java/io/snabble/sdk/ShoppingCartTest.java diff --git a/core/src/androidTest/java/io/snabble/sdk/SnabbleSdkTest.java b/core/src/test/java/io/snabble/sdk/SnabbleSdkTest.java similarity index 86% rename from core/src/androidTest/java/io/snabble/sdk/SnabbleSdkTest.java rename to core/src/test/java/io/snabble/sdk/SnabbleSdkTest.java index 587512c8a0..35e599509f 100644 --- a/core/src/androidTest/java/io/snabble/sdk/SnabbleSdkTest.java +++ b/core/src/test/java/io/snabble/sdk/SnabbleSdkTest.java @@ -4,12 +4,9 @@ import android.content.Context; import android.os.StrictMode; -import androidx.test.filters.LargeTest; import androidx.test.platform.app.InstrumentationRegistry; -import androidx.test.runner.AndroidJUnit4; import org.apache.commons.io.FileUtils; -import org.apache.commons.io.IOUtils; import org.junit.After; import org.junit.AfterClass; import org.junit.Assert; @@ -17,10 +14,12 @@ import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; import java.io.File; import java.io.IOException; -import java.nio.charset.Charset; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -34,8 +33,7 @@ import okhttp3.mockwebserver.RecordedRequest; import okio.Buffer; -@RunWith(AndroidJUnit4.class) -@LargeTest +@RunWith(RobolectricTestRunner.class) public class SnabbleSdkTest { protected Project project; protected Context context; @@ -43,23 +41,31 @@ public class SnabbleSdkTest { protected static Buffer productDbBuffer; + protected static Buffer loadBuffer(String name) throws IOException { + final Buffer buffer = new Buffer(); + try(InputStream stream = buffer.getClass().getClassLoader().getResourceAsStream(name)) { + buffer.readFrom(stream); + } + return buffer; + } + + protected static String loadSql(String name) throws IOException { + return loadBuffer(name + ".sql").readString(StandardCharsets.UTF_8); + } + + protected InputStream getInputStream(String name) { + return getClass().getClassLoader().getResourceAsStream(name); + } + @BeforeClass public static void setupMockWebServer() throws Exception { - Context context = InstrumentationRegistry.getInstrumentation().getContext(); - mockWebServer = new MockWebServer(); mockWebServer.start(); - final String metadataJson = IOUtils.toString(context.getAssets().open("metadata.json"), Charset.forName("UTF-8")); - - final Buffer product1Buffer = new Buffer(); - product1Buffer.readFrom(context.getAssets().open("product.json")); - - final Buffer product1OtherPriceBuffer = new Buffer(); - product1OtherPriceBuffer.readFrom(context.getAssets().open("product_otherprice.json")); - - final Buffer product2Buffer = new Buffer(); - product2Buffer.readFrom(context.getAssets().open("product2.json")); + final Buffer metadataJson = loadBuffer("metadata.json"); + final Buffer product1Buffer = loadBuffer("product.json"); + final Buffer product1OtherPriceBuffer = loadBuffer("product_otherprice.json"); + final Buffer product2Buffer = loadBuffer("product2.json"); final Dispatcher dispatcher = new Dispatcher() { @Override @@ -128,6 +134,9 @@ public static void closeMockWebServer() throws IOException { @Before public void setupSdk() throws Snabble.SnabbleException, IOException { + // Disable close guard warnings + StrictMode.enableDefaults(); + context = InstrumentationRegistry.getInstrumentation().getTargetContext(); withDb("test_1_25.sqlite3"); } @@ -182,8 +191,7 @@ public void error() { } public void prepareUpdateDb(String assetPath) throws IOException { - productDbBuffer = new Buffer(); - productDbBuffer.readFrom(context.getAssets().open(assetPath)); + productDbBuffer = loadBuffer(assetPath); } @After diff --git a/core/src/androidTest/java/io/snabble/sdk/TotpTest.java b/core/src/test/java/io/snabble/sdk/TotpTest.java similarity index 100% rename from core/src/androidTest/java/io/snabble/sdk/TotpTest.java rename to core/src/test/java/io/snabble/sdk/TotpTest.java diff --git a/core/src/androidTest/java/io/snabble/sdk/UnitTest.java b/core/src/test/java/io/snabble/sdk/UnitTest.java similarity index 100% rename from core/src/androidTest/java/io/snabble/sdk/UnitTest.java rename to core/src/test/java/io/snabble/sdk/UnitTest.java diff --git a/core/src/androidTest/assets/corrupt.sqlite3 b/core/src/test/resources/corrupt.sqlite3 similarity index 100% rename from core/src/androidTest/assets/corrupt.sqlite3 rename to core/src/test/resources/corrupt.sqlite3 diff --git a/core/src/androidTest/assets/metadata.json b/core/src/test/resources/metadata.json similarity index 100% rename from core/src/androidTest/assets/metadata.json rename to core/src/test/resources/metadata.json diff --git a/core/src/androidTest/assets/product.json b/core/src/test/resources/product.json similarity index 100% rename from core/src/androidTest/assets/product.json rename to core/src/test/resources/product.json diff --git a/core/src/androidTest/assets/product2.json b/core/src/test/resources/product2.json similarity index 100% rename from core/src/androidTest/assets/product2.json rename to core/src/test/resources/product2.json diff --git a/core/src/androidTest/assets/product_otherprice.json b/core/src/test/resources/product_otherprice.json similarity index 100% rename from core/src/androidTest/assets/product_otherprice.json rename to core/src/test/resources/product_otherprice.json diff --git a/core/src/test/resources/robolectric.properties b/core/src/test/resources/robolectric.properties new file mode 100644 index 0000000000..d27d2bdc6e --- /dev/null +++ b/core/src/test/resources/robolectric.properties @@ -0,0 +1 @@ +sdk=29 \ No newline at end of file diff --git a/core/src/androidTest/assets/test_1_25.sqlite3 b/core/src/test/resources/test_1_25.sqlite3 similarity index 100% rename from core/src/androidTest/assets/test_1_25.sqlite3 rename to core/src/test/resources/test_1_25.sqlite3 diff --git a/core/src/androidTest/assets/transmission_template.sql b/core/src/test/resources/transmission_template.sql similarity index 100% rename from core/src/androidTest/assets/transmission_template.sql rename to core/src/test/resources/transmission_template.sql diff --git a/core/src/androidTest/assets/update_1_25.sqlite3 b/core/src/test/resources/update_1_25.sqlite3 similarity index 100% rename from core/src/androidTest/assets/update_1_25.sqlite3 rename to core/src/test/resources/update_1_25.sqlite3