diff --git a/.github/workflows/codecov-report.yml b/.github/workflows/codecov-report.yml new file mode 100644 index 00000000..e1dbf156 --- /dev/null +++ b/.github/workflows/codecov-report.yml @@ -0,0 +1,25 @@ +# uploading codecov report + +name: Android + +on: [push, pull_request] + +jobs: + test: + runs-on: macOS-latest + + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-java@v2 + with: + distribution: 'adopt' + java-version: 15.0.2 + - name: Build and run unit tests + run: | + ./gradlew build jacocoTestReport + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v4 + with: + token: ${{ secrets.CODECOV_TOKEN }} + fail_ci_if_error: true + verbose: true diff --git a/app/build.gradle b/app/build.gradle index 65575efd..22995a78 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,30 +1,27 @@ import io.sentry.android.gradle.instrumentation.logcat.LogcatLevel buildscript { - ext.kotlin_version= '1.8.20' - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } + ext.kotlin_version = '1.8.20' repositories { mavenCentral() + google() + } + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath 'com.android.tools.build:gradle:7.0.0' } } plugins { + id 'com.android.application' id "io.sentry.android.gradle" version "4.4.0" + id "jacoco" } apply plugin: 'com.android.application' apply plugin: 'com.ydq.android.gradle.native-aar.import' apply plugin: 'io.sentry.android.gradle' apply plugin: 'kotlin-android' -//apply plugin: 'fullstory' - -//fullstory { -// org 'QNEN8' -// enabledVariants 'all' -// logLevel 'off' -//} task wrapper(type: org.gradle.api.tasks.wrapper.Wrapper) { gradleVersion = '7.0' @@ -54,6 +51,7 @@ android { ndk { abiFilters("x86", "armeabi-v7a", "x86_64", "arm64-v8a") } + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } externalNativeBuild { cmake { @@ -65,13 +63,13 @@ android { release { minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - signingConfig = signingConfigs.debug // to be able to run release mode - buildConfigField("String", "SE", "\"tda\"") - buildConfigField("boolean", "SLOW_PROFILING", "true") + signingConfig signingConfigs.debug // to be able to run release mode + buildConfigField "String", "SE", "\"tda\"" + buildConfigField "boolean", "SLOW_PROFILING", "true" } debug { - buildConfigField("String", "SE", "\"tda\"") - buildConfigField("boolean", "SLOW_PROFILING", "true") + buildConfigField "String", "SE", "\"tda\"" + buildConfigField "boolean", "SLOW_PROFILING", "true" testCoverageEnabled true } } @@ -84,10 +82,52 @@ android { abortOnError false } ndkVersion '21.3.6528147' +} +jacoco { + toolVersion = "0.8.5" +} + +tasks.withType(Test) { + finalizedBy jacocoTestReport +} + +task jacocoTestReport(type: JacocoReport) { + dependsOn 'testDebugUnitTest' + + reports { + xml.enabled = true + html.enabled = true + } + + def fileFilter = [ + '**/R.class', + '**/R$*.class', + '**/BuildConfig.*', + '**/Manifest*.*', + '**/*Test*.*' + ] + + def debugTree = fileTree(dir: "${buildDir}/intermediates/javac/debug", excludes: fileFilter) + def mainSrc = "${project.projectDir}/src/main/java" + + sourceDirectories.setFrom(files([mainSrc])) + classDirectories.setFrom(files([debugTree])) + executionData.setFrom(fileTree(dir: "${buildDir}/jacoco/testDebugUnitTest.exec", includes: ["*.exec"])) +} + +tasks.named("jacocoTestReport") { + dependsOn "testDebugUnitTest" + onlyIf = { true } } dependencies { + // Room components + def room_version = "2.0.0" + + implementation "androidx.room:room-runtime:$room_version" + annotationProcessor "androidx.room:room-compiler:$room_version" // For Java projects + implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'androidx.appcompat:appcompat:1.1.0' implementation group: 'androidx.constraintlayout', name: 'constraintlayout', version: '1.1.3' @@ -96,23 +136,21 @@ dependencies { implementation 'androidx.navigation:navigation-ui:2.5.3' implementation 'com.android.support:support-core-utils:28.0.0' implementation 'com.squareup.okhttp3:okhttp:4.9.0' - implementation 'junit:junit:4.12' implementation 'com.google.code.gson:gson:2.10.1' - def fragment_version = "1.5.6" - // Java language implementation - implementation "androidx.fragment:fragment:$fragment_version" - - // Warning: upgrading the room version beyond 2.0.0 may - // cause errors running automated Saucelabs tests that generate data. - // If you upgrade the room version, ensure you run associated automated - // tests locally and that they pass. - def room_version = "2.0.0" - - implementation "androidx.room:room-runtime:$room_version" - annotationProcessor "androidx.room:room-compiler:$room_version" implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + + // Test dependencies + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.mockito:mockito-core:3.11.2' + testImplementation 'org.mockito:mockito-inline:3.11.2' + testImplementation 'org.mockito:mockito-junit-jupiter:3.11.2' + + // Android Test dependencies + androidTestImplementation 'androidx.test.ext:junit:1.1.5' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' } + sentry { autoUpload = true uploadNativeSymbols = false diff --git a/app/src/androidTest/java/MainFragmentTest.java b/app/src/androidTest/java/MainFragmentTest.java new file mode 100644 index 00000000..bc0fb916 --- /dev/null +++ b/app/src/androidTest/java/MainFragmentTest.java @@ -0,0 +1,124 @@ +package com.example.vu.android.empowerplant; + +import android.content.Context; +import android.os.Bundle; +import androidx.lifecycle.Lifecycle; +import io.sentry.ITransaction; +import io.sentry.Sentry; +import org.junit.Before; +import org.junit.Test; +import org.mockito.MockitoAnnotations; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.util.HashMap; + +public class MainFragmentTest { + + private MainFragment mainFragment; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mainFragment = MainFragment.newInstance(); + } + + @Test + public void testMainFragmentNotNull() { + assertNotNull(mainFragment); + } + + @Test + public void testOnCreate() { + mainFragment.onCreate(null); + assertNotNull(mainFragment.getArguments()); + } + + @Test + public void testOnCreateView() { + assertNotNull(mainFragment.onCreateView(null, null, null)); + } + + @Test + public void testSetBadgeNumber() { + mainFragment.setBadgeNumber(); + // Assuming setBadgeNumber increases the badge number by 1 + assertEquals(1, mainFragment.mCartItemCount); + } + + @Test + public void testFetchToolsFromServer() { + mainFragment.fetchToolsFromServer(); + // Assuming fetchToolsFromServer fetches tools, we can check if the progress dialog is shown + assertNotNull(mainFragment.progressDialog); + assertTrue(mainFragment.progressDialog.isShowing()); + } + + @Test + public void testOnItemClick() { + StoreItem storeItem = new StoreItem(); + storeItem.setQuantity(1); + mainFragment.onItemClick(storeItem); + // Assuming onItemClick increases the item count + assertEquals(1, storeItem.getQuantity()); + } + + @Test + public void testProcessGetToolsResponse() { + mainFragment.processGetToolsResponse(""); + // Assuming processGetToolsResponse updates the adapter + assertNotNull(mainFragment.adapter); + } + + @Test + public void testProcessProducts() { + mainFragment.processProducts(); + // Assuming processProducts calls getIterator, which returns a value + assertEquals(1, mainFragment.getIterator(1)); + } + + @Test + public void testGetIterator() { + assertEquals(1, mainFragment.getIterator(1)); + } + + @Test + public void testGetEmpowerPlantDomain() { + assertNotNull(mainFragment.getEmpowerPlantDomain()); + } + + @Test + public void testCheckout() { + mainFragment.checkout(); + // Assuming checkout starts a transaction, check if the transaction is not null + ITransaction transaction = Sentry.startTransaction("checkout [android]", "http.client"); + assertNotNull(transaction); + } + + @Test + public void testBuildJSONPostData() { + HashMap storeItems = new HashMap<>(); + assertNotNull(mainFragment.buildJSONPostData(storeItems)); + } + + @Test + public void testProcessDeliveryItem() { + ITransaction transaction = Sentry.startTransaction("test", "test"); + mainFragment.processDeliveryItem(transaction); + // Assuming processDeliveryItem sets the status + assertEquals(transaction.getStatus(), SpanStatus.OK); + } + + @Test + public void testAddAttachment() { + assertTrue(mainFragment.addAttachment()); + } + + @Test + public void testInsertMultipleStoreItems() { + mainFragment.insertMultipleStoreItems(); + // Assuming insertMultipleStoreItems interacts with a database, this is harder to assert directly + } +} diff --git a/app/src/androidTest/java/MockContext.java b/app/src/androidTest/java/MockContext.java new file mode 100644 index 00000000..df3d356a --- /dev/null +++ b/app/src/androidTest/java/MockContext.java @@ -0,0 +1,18 @@ +package com.example.vu.android.empowerplant; + +import android.content.Context; +import android.content.ContextWrapper; + +public class MockContext extends ContextWrapper { + + public MockContext() { + super(null); + } + + @Override + public Context getApplicationContext() { + return this; + } + + // Add other methods if needed for your specific tests +} diff --git a/app/src/main/java/com/example/vu/android/MainActivityTest.java b/app/src/main/java/com/example/vu/android/MainActivityTest.java deleted file mode 100644 index a47d586c..00000000 --- a/app/src/main/java/com/example/vu/android/MainActivityTest.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.example.vu.android; - -import static org.junit.Assert.*; - -public class MainActivityTest { - - @org.junit.Test - public void dummyFunction() { - assertTrue(MainActivity.dummyFunction(false)); - } -} \ No newline at end of file diff --git a/app/src/main/java/com/example/vu/android/MyApplicationTest.java b/app/src/main/java/com/example/vu/android/MyApplicationTest.java deleted file mode 100644 index 4ad3379b..00000000 --- a/app/src/main/java/com/example/vu/android/MyApplicationTest.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.example.vu.android; - -import static org.junit.Assert.*; - -import org.junit.Test; - -public class MyApplicationTest { - - @org.junit.Test - public void getcurrentactivity() { - MyApplication app = new MyApplication(); - assertEquals(app.getCurrentActivity(), null); - } -} \ No newline at end of file diff --git a/app/src/main/java/com/example/vu/android/empowerplant/MainFragment.java b/app/src/main/java/com/example/vu/android/empowerplant/MainFragment.java index 4e24fe59..0e27c15a 100644 --- a/app/src/main/java/com/example/vu/android/empowerplant/MainFragment.java +++ b/app/src/main/java/com/example/vu/android/empowerplant/MainFragment.java @@ -281,6 +281,15 @@ private int getIterator(int n) { return getIterator(n-1) + getIterator(n-2); } + public int getNumber() { + int result = getAnotherNumber(); + return result; + } + + public int getAnotherNumber() { + return 2; + } + private String getEmpowerPlantDomain() { String domain = null; try { diff --git a/app/src/test/java/MainFragmentTest.java b/app/src/test/java/MainFragmentTest.java new file mode 100644 index 00000000..a57bc6bc --- /dev/null +++ b/app/src/test/java/MainFragmentTest.java @@ -0,0 +1,59 @@ +package com.example.vu.android.empowerplant; + +import android.content.Context; +import android.os.Bundle; +import androidx.lifecycle.Lifecycle; +import io.sentry.ITransaction; +import io.sentry.Sentry; +import org.junit.Before; +import org.junit.Test; +import org.mockito.*; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; + + +import java.util.HashMap; + +public class MainFragmentTest { + + private MainFragment mainFragment; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mainFragment = spy(MainFragment.newInstance()); + } + + @Test + public void testMainFragmentNotNull() { + assertNotNull(mainFragment); + } + + @Test + public void testGetNumber() { + // Given + doReturn(2).when(mainFragment).getAnotherNumber(); + + // When + int result = mainFragment.getNumber(); + + // Then + assertEquals(2, result); + verify(mainFragment, times(1)).getAnotherNumber(); + } + + @Test + public void testGetAnotherNumber() { + // When + int result = mainFragment.getAnotherNumber(); + + // Then + assertEquals(2, result); + } +} diff --git a/app/src/test/java/MockContext.java b/app/src/test/java/MockContext.java new file mode 100644 index 00000000..df3d356a --- /dev/null +++ b/app/src/test/java/MockContext.java @@ -0,0 +1,18 @@ +package com.example.vu.android.empowerplant; + +import android.content.Context; +import android.content.ContextWrapper; + +public class MockContext extends ContextWrapper { + + public MockContext() { + super(null); + } + + @Override + public Context getApplicationContext() { + return this; + } + + // Add other methods if needed for your specific tests +} diff --git a/build.gradle b/build.gradle index df093e4c..397c4f7e 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,6 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - repositories { google() jcenter() @@ -12,6 +11,7 @@ buildscript { classpath 'com.android.tools.build:gradle:7.0.3' classpath 'com.ydq.android.gradle.build.tool:nativeBundle:1.0.7' classpath 'com.fullstory:gradle-plugin-local:1.12.1' + classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.20' } } @@ -19,6 +19,7 @@ allprojects { repositories { google() jcenter() + mavenCentral() } } diff --git a/gradle.properties b/gradle.properties index 706bcd15..0462d1a0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,4 +17,5 @@ org.gradle.jvmargs=-Xmx1536m # org.gradle.parallel=true android.useAndroidX=true -android.enableJetifier=true \ No newline at end of file +android.enableJetifier=true +org.gradle.caching=true \ No newline at end of file diff --git a/sentry.properties b/sentry.properties index 90c17936..59c7401d 100644 --- a/sentry.properties +++ b/sentry.properties @@ -1,4 +1,4 @@ # how a sentry.properties looks like defaults.org=demo defaults.project=android -auth.token= +auth.token=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