Skip to content
This repository was archived by the owner on Oct 15, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions .github/workflows/pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,20 @@ jobs:
if: ${{ steps.service-changed.outputs.result == 'true' }}
run: ./gradlew test${{ matrix.variant }} lint${{ matrix.variant}} -Dpre-dex=false

- name: Run instrumentation tests
if: ${{ steps.service-changed.outputs.result == 'true' }}
- name: Run instrumentation tests on free flavor
if: ${{ steps.service-changed.outputs.result == 'true' && matrix.variant == 'freeRelease' }}
uses: reactivecircus/android-emulator-runner@v2.11.0
with:
api-level: ${{ matrix.api-level }}
target: default
script: |
adb shell settings put global animator_duration_scale 0
adb shell settings put global transition_animation_scale 0
adb shell settings put global window_animation_scale 0
./gradlew :app:connectedFreeDebugAndroidTest

- name: Run instrumentation tests on nonFree flavor
if: ${{ steps.service-changed.outputs.result == 'true' && matrix.variant == 'nonFreeRelease' }}
uses: reactivecircus/android-emulator-runner@v2.11.0
with:
api-level: ${{ matrix.api-level }}
Expand All @@ -79,4 +91,4 @@ jobs:
adb shell settings put global animator_duration_scale 0
adb shell settings put global transition_animation_scale 0
adb shell settings put global window_animation_scale 0
./gradlew connectedCheck
./gradlew :app:connectedNonFreeDebugAndroidTest
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
* Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
* SPDX-License-Identifier: GPL-3.0-only
*/

package com.zeapo.pwdstore.model

import com.zeapo.pwdstore.utils.Otp
import com.zeapo.pwdstore.utils.UriTotpFinder
import java.util.Date
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertNotNull
import kotlin.test.assertNull
import kotlin.test.assertTrue
import org.junit.Test

class PasswordEntryAndroidTest {

private fun makeEntry(content: String) = PasswordEntry(content, UriTotpFinder())

@Test fun testGetPassword() {
assertEquals("fooooo", makeEntry("fooooo\nbla\n").password)
assertEquals("fooooo", makeEntry("fooooo\nbla").password)
assertEquals("fooooo", makeEntry("fooooo\n").password)
assertEquals("fooooo", makeEntry("fooooo").password)
assertEquals("", makeEntry("\nblubb\n").password)
assertEquals("", makeEntry("\nblubb").password)
assertEquals("", makeEntry("\n").password)
assertEquals("", makeEntry("").password)
}

@Test fun testGetExtraContent() {
assertEquals("bla\n", makeEntry("fooooo\nbla\n").extraContent)
assertEquals("bla", makeEntry("fooooo\nbla").extraContent)
assertEquals("", makeEntry("fooooo\n").extraContent)
assertEquals("", makeEntry("fooooo").extraContent)
assertEquals("blubb\n", makeEntry("\nblubb\n").extraContent)
assertEquals("blubb", makeEntry("\nblubb").extraContent)
assertEquals("", makeEntry("\n").extraContent)
assertEquals("", makeEntry("").extraContent)
}

@Test fun testGetUsername() {
for (field in PasswordEntry.USERNAME_FIELDS) {
assertEquals("username", makeEntry("\n$field username").username)
assertEquals("username", makeEntry("\n${field.toUpperCase()} username").username)
}
assertEquals(
"username",
makeEntry("secret\nextra\nlogin: username\ncontent\n").username)
assertEquals(
"username",
makeEntry("\nextra\nusername: username\ncontent\n").username)
assertEquals(
"username", makeEntry("\nUSERNaMe: username\ncontent\n").username)
assertEquals("username", makeEntry("\nlogin: username").username)
assertEquals("foo@example.com", makeEntry("\nemail: foo@example.com").username)
assertEquals("username", makeEntry("\nidentity: username\nlogin: another_username").username)
assertEquals("username", makeEntry("\nLOGiN:username").username)
assertNull(makeEntry("secret\nextra\ncontent\n").username)
}

@Test fun testHasUsername() {
assertTrue(makeEntry("secret\nextra\nlogin: username\ncontent\n").hasUsername())
assertFalse(makeEntry("secret\nextra\ncontent\n").hasUsername())
assertFalse(makeEntry("secret\nlogin failed\n").hasUsername())
assertFalse(makeEntry("\n").hasUsername())
assertFalse(makeEntry("").hasUsername())
}

@Test fun testGeneratesOtpFromTotpUri() {
val entry = makeEntry("secret\nextra\n$TOTP_URI")
assertTrue(entry.hasTotp())
val code = Otp.calculateCode(
entry.totpSecret!!,
// The hardcoded date value allows this test to stay reproducible.
Date(8640000).time / (1000 * entry.totpPeriod),
entry.totpAlgorithm,
entry.digits
)
assertNotNull(code) { "Generated OTP cannot be null" }
assertEquals(entry.digits.toInt(), code.length)
assertEquals("545293", code)
}

companion object {

const val TOTP_URI = "otpauth://totp/ACME%20Co:john@example.com?secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ&issuer=ACME%20Co&algorithm=SHA1&digits=6&period=30"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,30 @@ class UriTotpFinderTest {
fun findSecret() {
assertEquals("HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ", totpFinder.findSecret(TOTP_URI))
assertEquals("HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ", totpFinder.findSecret("name\npassword\ntotp: HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ"))
assertEquals("HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ", totpFinder.findSecret(PASS_FILE_CONTENT))
}

@Test
fun findDigits() {
assertEquals("12", totpFinder.findDigits(TOTP_URI))
assertEquals("12", totpFinder.findDigits(PASS_FILE_CONTENT))
}

@Test
fun findPeriod() {
assertEquals(25, totpFinder.findPeriod(TOTP_URI))
assertEquals(25, totpFinder.findPeriod(PASS_FILE_CONTENT))
}

@Test
fun findAlgorithm() {
assertEquals("SHA256", totpFinder.findAlgorithm(TOTP_URI))
assertEquals("SHA256", totpFinder.findAlgorithm(PASS_FILE_CONTENT))
}

companion object {

const val TOTP_URI = "otpauth://totp/ACME%20Co:john@example.com?secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ&issuer=ACME%20Co&algorithm=SHA256&digits=12&period=25"
const val PASS_FILE_CONTENT = "password\n$TOTP_URI"
}
}