diff --git a/.circleci/config.yml b/.circleci/config.yml index 02d75bf9..aca01a1e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,59 +1,54 @@ version: 2.1 +orbs: + codecov: codecov/codecov@3.2.4 -jobs: - android-test: - macos: - xcode: "11.2.0" - working_directory: ~/repo/App +commands: + restore_gradle_cache: steps: - - checkout: - path: ~/repo - - - run: - name: set ANDROID_SDK_ROOT - command: | - echo 'export ANDROID_SDK_ROOT=$HOME/android-tools' >> $BASH_ENV - - restore_cache: - key: android=tools-v1-{{ checksum "scripts/install-android-tools.sh" }}-{{ arch }} - - - run: - name: install android tools - command: | - sh scripts/install-android-tools.sh - echo 'export PATH=$ANDROID_SDK_ROOT/tools/bin:$PATH' >> $BASH_ENV - echo 'export PATH=$ANDROID_SDK_ROOT/tools:$PATH' >> $BASH_ENV - echo 'export PATH=$ANDROID_SDK_ROOT/platform-tools:$PATH' >> $BASH_ENV - echo 'export PATH=$ANDROID_SDK_ROOT/emulator:$PATH' >> $BASH_ENV - source $BASH_ENV - sdkmanager --list + key: v1-gradle-wrapper-{{ arch }}-{{ checksum "gradle/wrapper/gradle-wrapper.properties" }} + - restore_cache: + key: v1-gradle-cache-{{ arch }}-{{ checksum "build.gradle" }} + save_gradle_cache: + steps: - save_cache: - key: android=tools-v1-{{ checksum "scripts/install-android-tools.sh" }}-{{ arch }} paths: - - /Users/distiller/android-tools - - - run: - name: create AVD - command: make create-avd - - - run: - name: start AVD - command: emulator-headless -avd android-tablet - background: true - - - run: - name: wait for emulator - command: adb wait-for-device shell 'while [[ -z $(getprop dev.bootcomplete) ]]; do sleep 1; done;' - - - run: adb shell screencap -p > screenshots/before.png - - # (insert testing here) + - ~/.gradle/wrapper + key: v1-gradle-wrapper-{{ arch }}-{{ checksum "gradle/wrapper/gradle-wrapper.properties" }} + - save_cache: + paths: + - ~/.gradle/caches + key: v1-gradle-cache-{{ arch }}-{{ checksum "build.gradle" }} +jobs: + android-test: + machine: + image: android:2022.12.1 + resource_class: large + steps: + - run: lsb_release -a + - checkout + - run: python3 scripts/generateKsPropFile.py + - run: python3 scripts/generateGoogleServicesJson.py + - restore_gradle_cache + - run: ./gradlew tasks + - save_gradle_cache + - run: sdkmanager --list + - run: (yes || true) | sdkmanager "tools" "platform-tools" "build-tools;33.0.2" "platforms;android-33" "system-images;android-30;google_atd;x86" + - run: (yes || true) | sdkmanager --licenses + - run: ./gradlew assembleDebug --stacktrace + - run: ./gradlew pixel2api30DebugAndroidTest --stacktrace + - run: ./gradlew testDebugUnitTest --stacktrace + - run: ./gradlew jacocoTestReport --stacktrace + - codecov/upload: + file: app/build/mergedReportDir/jacocoTestReport/jacocoTestReport.xml + - store_test_results: + path: app/build/test-results/testDebugUnitTest - store_artifacts: - path: screenshots + path: app/build/mergedReportDir workflows: workflow: jobs: - - android-test \ No newline at end of file + - android-test diff --git a/README.md b/README.md index c75c331a..e0102fa7 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,8 @@ Terminal-like app to send commands to Arduino through USB ![Android CI](https://github.com/k4biri/arduino-usb-terminal/workflows/Android%20CI/badge.svg) - +[![superus8r](https://circleci.com/gh/superus8r/arduino-usb-terminal.svg?style=shield)](https://circleci.com/gh/superus8r/arduino-usb-terminal) +[![codecov](https://codecov.io/gh/superus8r/arduino-usb-terminal/branch/develop/graph/badge.svg?token=RYIUU345QG)](https://codecov.io/gh/superus8r/arduino-usb-terminal) This app simplifies testing your Arduino components that work with direct usb commands by giving you the ability to send custom commands and view the returned message from your Arduino device. diff --git a/app/build.gradle b/app/build.gradle deleted file mode 100644 index 01e6fc54..00000000 --- a/app/build.gradle +++ /dev/null @@ -1,114 +0,0 @@ -plugins { - id 'com.android.application' - id 'com.google.gms.google-services' - id 'com.google.firebase.crashlytics' - id 'org.jetbrains.kotlin.android' - id 'kotlin-kapt' - id 'dagger.hilt.android.plugin' -} - -repositories { -} - -android { - compileSdkVersion 33 - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - - defaultConfig { - applicationId "org.kabiri.android.usbterminal" - minSdkVersion 23 - targetSdkVersion 33 - versionCode 13 - versionName "0.9.12" - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - } - - signingConfigs { - // read release credentials from keystore.properties file - def ksProp = new Properties() - // load keys inside the ksProp file - loadKeyStore(ksProp) - release { - keyAlias ksProp.getProperty("release.keyAlias") - keyPassword ksProp.getProperty("release.keyPassword") - storeFile file(ksProp.getProperty("release.file")) - storePassword ksProp.getProperty("release.storePassword") - } - } - - buildTypes { - release { - signingConfig signingConfigs.release - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } - } - - namespace 'org.kabiri.android.usbterminal' - -} - -private void loadKeyStore(Properties ksProp) { - def ksPropFile = file("keystore.properties") - if (ksPropFile.exists()) { - ksProp.load(new FileInputStream(ksPropFile)) - } else { - println 'ERROR: local keystore file not found' - } -} - -dependencies { - - implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation 'androidx.appcompat:appcompat:1.6.0' - implementation 'androidx.core:core-ktx:1.9.0' - implementation 'androidx.constraintlayout:constraintlayout:2.1.4' - - // Firebase - implementation platform("com.google.firebase:firebase-bom:$firebase_bom_version") - implementation 'com.google.firebase:firebase-analytics-ktx' - implementation 'com.google.firebase:firebase-crashlytics-ktx' - - // Dependency Injection - implementation "com.google.dagger:hilt-android:$hilt_version" - kapt "com.google.dagger:hilt-compiler:$hilt_version" - - // Coroutines - implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.1' - implementation 'androidx.activity:activity-compose:1.6.1' - implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1" - implementation "androidx.lifecycle:lifecycle-extensions:2.2.0" - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4' - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4' - - // hilt testing - // more info: - // https://developer.android.com/training/dependency-injection/hilt-testing - androidTestImplementation "com.google.dagger:hilt-android-testing:$hilt_version" - kaptAndroidTest "com.google.dagger:hilt-android-compiler:$hilt_version" - - // unit test libs - testImplementation 'junit:junit:4.13.2' - testImplementation "com.google.truth:truth:1.1.3" - - // instrumented test libs - androidTestImplementation 'androidx.test:core:1.5.0' - androidTestImplementation 'androidx.test.ext:junit:1.1.5' - androidTestImplementation 'androidx.test.ext:junit-ktx:1.1.5' - // Espresso - androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' - // Hamcrest for view matching - androidTestImplementation 'org.hamcrest:hamcrest-library:2.2' - androidTestImplementation 'androidx.test:runner:1.5.2' - androidTestImplementation 'androidx.test:rules:1.5.0' - - /** - * This library helps to automate some parts of the USB serial connection. - * For more information, visit: https://github.com/felHR85/UsbSerial - */ - implementation 'com.github.felHR85:UsbSerial:6.1.0' -} diff --git a/app/build.gradle.kts b/app/build.gradle.kts new file mode 100644 index 00000000..23b09bd3 --- /dev/null +++ b/app/build.gradle.kts @@ -0,0 +1,167 @@ +import java.io.FileInputStream +import java.util.* + +plugins { + id("com.android.application") + id("com.google.gms.google-services") + id("com.google.firebase.crashlytics") + id("org.jetbrains.kotlin.android") + id("kotlin-kapt") + id("dagger.hilt.android.plugin") + id("jacoco") +} + +repositories { +} + +android { + compileSdkVersion(33) + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + + defaultConfig { + applicationId = "org.kabiri.android.usbterminal" + minSdkVersion(23) + targetSdkVersion(33) + versionCode = 13 + versionName = "0.9.12" + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + + signingConfigs { + // read release credentials from keystore.properties file + val ksProp = Properties() + // load keys inside the ksProp file + loadKeyStore(ksProp) + create("release") { + keyAlias = ksProp.getProperty("release.keyAlias") + keyPassword = ksProp.getProperty("release.keyPassword") + storeFile = file(ksProp.getProperty("release.file")) + storePassword = ksProp.getProperty("release.storePassword") + } + } + + buildTypes { + named("release") { + signingConfig = signingConfigs.getByName("release") + isMinifyEnabled = false + proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") + } + named("debug") { + isTestCoverageEnabled = true + } + } + + testOptions { + + animationsDisabled = true + + managedDevices { + devices { + maybeCreate("pixel2api30").apply { + device = "Pixel 2" + apiLevel = 30 + systemImageSource = "google_atd" + } + } + } + } + + namespace = "org.kabiri.android.usbterminal" + +} + +jacoco { + val jacoco_version: String by project + toolVersion = jacoco_version + reportsDirectory.set(layout.buildDirectory.dir("mergedReportDir")) +} + +tasks.register("jacocoTestReport") { + + dependsOn("testDebugUnitTest") + dependsOn("pixel2api30DebugAndroidTest") + + reports { + xml.required.set(true) + html.required.set(true) + csv.required.set(false) + } + + val fileFilter = listOf("**/R.class", "**/R$*.class", "**/BuildConfig.*", "**/Manifest*.*", "**/*Test*.*", "android/**/*.*") + val debugTree = fileTree("${buildDir}/tmp/kotlin-classes/debug") { exclude(fileFilter) } + val mainSrc = "${project.projectDir}/src/main/kotlin" + + sourceDirectories.from(files(setOf(mainSrc))) + classDirectories.from(files(setOf(debugTree))) + executionData.from(fileTree(buildDir) { include(setOf( + "outputs/unit_test_code_coverage/debugUnitTest/testDebugUnitTest.exec", + "outputs/managed_device_code_coverage/pixel2api30/coverage.ec" + ))}) +} + +fun loadKeyStore(ksProp: Properties) { + val ksPropFile = file("keystore.properties") + if (ksPropFile.exists()) { + ksProp.load(FileInputStream(ksPropFile)) + } else { + println("ERROR: local keystore file not found") + } +} + +val firebase_bom_version: String by project +val hilt_version: String by project +dependencies { + +// implementation fileTree("libs") { include(setOf("*.jar")) } + implementation("androidx.appcompat:appcompat:1.6.0") + implementation("androidx.core:core-ktx:1.9.0") + implementation("androidx.constraintlayout:constraintlayout:2.1.4") + + // Firebase + implementation(platform("com.google.firebase:firebase-bom:$firebase_bom_version")) + implementation("com.google.firebase:firebase-analytics-ktx") + implementation("com.google.firebase:firebase-crashlytics-ktx") + + // Dependency Injection + implementation("com.google.dagger:hilt-android:$hilt_version") + kapt("com.google.dagger:hilt-compiler:$hilt_version") + + // Coroutines + implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.5.1") + implementation("androidx.activity:activity-compose:1.6.1") + implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1") + implementation("androidx.lifecycle:lifecycle-extensions:2.2.0") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4") + + // hilt testing + // more info: + // https://developer.android.com/training/dependency-injection/hilt-testing + androidTestImplementation("com.google.dagger:hilt-android-testing:$hilt_version") + kaptAndroidTest("com.google.dagger:hilt-android-compiler:$hilt_version") + + // unit test libs + testImplementation("junit:junit:4.13.2") + testImplementation("com.google.truth:truth:1.1.3") + + // instrumented test libs + androidTestImplementation("androidx.test:core:1.5.0") + androidTestImplementation("androidx.test.ext:junit:1.1.5") + androidTestImplementation("androidx.test.ext:junit-ktx:1.1.5") + // Espresso + androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") + // Hamcrest for view matching + androidTestImplementation("org.hamcrest:hamcrest-library:2.2") + androidTestImplementation("androidx.test:runner:1.5.2") + androidTestImplementation("androidx.test:rules:1.5.0") + + /** + * This library helps to automate some parts of the USB serial connection. + * For more information, visit: https://github.com/felHR85/UsbSerial + */ + implementation("com.github.felHR85:UsbSerial:6.1.0") +} \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index f1b42451..2f9dc5a4 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -1,6 +1,6 @@ # Add project specific ProGuard rules here. # You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. +# proguardFiles setting in build.gradle.kts. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html diff --git a/build.gradle b/build.gradle index 462fec47..2e395f32 100644 --- a/build.gradle +++ b/build.gradle @@ -5,13 +5,14 @@ buildscript { kotlin_version = '1.7.10' hilt_version = '2.44' firebase_bom_version = '30.5.0' + jacoco_version = '0.8.8' } repositories { google() mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:7.4.0-rc01' + classpath 'com.android.tools.build:gradle:7.4.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version" classpath 'com.google.gms:google-services:4.3.14' diff --git a/scripts/generateGoogleServicesJson.py b/scripts/generateGoogleServicesJson.py new file mode 100644 index 00000000..3cd613ce --- /dev/null +++ b/scripts/generateGoogleServicesJson.py @@ -0,0 +1,9 @@ +import os + +outputPath = "app/google-services.json" + +fileContent = os.getenv('GOOGLE_SERVICES_JSON') + +fhand = open(outputPath, 'w') +fhand.write(str(fileContent)) +fhand.close() \ No newline at end of file