NewPipe-app-android/app/src/test/java/org/schabi/newpipe/settings/ImportExportManagerTest.kt

215 lines
8.3 KiB
Kotlin

package org.schabi.newpipe.settings
import android.content.SharedPreferences
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertThrows
import org.junit.Assert.assertTrue
import org.junit.Assume
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito
import org.mockito.Mockito.anyBoolean
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.anyString
import org.mockito.Mockito.atLeastOnce
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.Mockito.withSettings
import org.mockito.junit.MockitoJUnitRunner
import org.schabi.newpipe.settings.export.ImportExportManager
import org.schabi.newpipe.streams.io.StoredFileHelper
import us.shandian.giga.io.FileStream
import java.io.File
import java.io.ObjectInputStream
import java.nio.file.Files
import java.util.zip.ZipFile
@RunWith(MockitoJUnitRunner::class)
class ImportExportManagerTest {
companion object {
private val classloader = ImportExportManager::class.java.classLoader!!
}
private lateinit var fileLocator: NewPipeFileLocator
private lateinit var storedFileHelper: StoredFileHelper
@Before
fun setupFileLocator() {
fileLocator = Mockito.mock(NewPipeFileLocator::class.java, withSettings().stubOnly())
storedFileHelper = Mockito.mock(StoredFileHelper::class.java, withSettings().stubOnly())
}
@Test
fun `The settings must be exported successfully in the correct format`() {
val db = File(classloader.getResource("settings/newpipe.db")!!.file)
val newpipeSettings = File.createTempFile("newpipe_", "")
`when`(fileLocator.db).thenReturn(db)
`when`(fileLocator.settings).thenReturn(newpipeSettings)
val expectedPreferences = mapOf("such pref" to "much wow")
val sharedPreferences =
Mockito.mock(SharedPreferences::class.java, withSettings().stubOnly())
`when`(sharedPreferences.all).thenReturn(expectedPreferences)
val output = File.createTempFile("newpipe_", "")
`when`(storedFileHelper.stream).thenReturn(FileStream(output))
ImportExportManager(fileLocator).exportDatabase(sharedPreferences, storedFileHelper)
val zipFile = ZipFile(output)
val entries = zipFile.entries().toList()
assertEquals(2, entries.size)
zipFile.getInputStream(entries.first { it.name == "newpipe.db" }).use { actual ->
db.inputStream().use { expected ->
assertEquals(expected.reader().readText(), actual.reader().readText())
}
}
zipFile.getInputStream(entries.first { it.name == "newpipe.settings" }).use { actual ->
val actualPreferences = ObjectInputStream(actual).readObject()
assertEquals(expectedPreferences, actualPreferences)
}
}
@Test
fun `Settings file must be deleted`() {
val settings = File.createTempFile("newpipe_", "")
`when`(fileLocator.settings).thenReturn(settings)
ImportExportManager(fileLocator).deleteSettingsFile()
assertFalse(settings.exists())
}
@Test
fun `Deleting settings file must do nothing if none exist`() {
val settings = File("non_existent")
`when`(fileLocator.settings).thenReturn(settings)
ImportExportManager(fileLocator).deleteSettingsFile()
assertFalse(settings.exists())
}
@Test
fun `Ensuring db directory existence must work`() {
val dir = Files.createTempDirectory("newpipe_").toFile()
Assume.assumeTrue(dir.delete())
`when`(fileLocator.dbDir).thenReturn(dir)
ImportExportManager(fileLocator).ensureDbDirectoryExists()
assertTrue(dir.exists())
}
@Test
fun `Ensuring db directory existence must work when the directory already exists`() {
val dir = Files.createTempDirectory("newpipe_").toFile()
`when`(fileLocator.dbDir).thenReturn(dir)
ImportExportManager(fileLocator).ensureDbDirectoryExists()
assertTrue(dir.exists())
}
@Test
fun `The database must be extracted from the zip file`() {
val db = File.createTempFile("newpipe_", "")
val dbJournal = File.createTempFile("newpipe_", "")
val dbWal = File.createTempFile("newpipe_", "")
val dbShm = File.createTempFile("newpipe_", "")
`when`(fileLocator.db).thenReturn(db)
`when`(fileLocator.dbJournal).thenReturn(dbJournal)
`when`(fileLocator.dbShm).thenReturn(dbShm)
`when`(fileLocator.dbWal).thenReturn(dbWal)
val zip = File(classloader.getResource("settings/newpipe.zip")?.file!!)
`when`(storedFileHelper.stream).thenReturn(FileStream(zip))
val success = ImportExportManager(fileLocator).extractDb(storedFileHelper)
assertTrue(success)
assertFalse(dbJournal.exists())
assertFalse(dbWal.exists())
assertFalse(dbShm.exists())
assertTrue("database file size is zero", Files.size(db.toPath()) > 0)
}
@Test
fun `Extracting the database from an empty zip must not work`() {
val db = File.createTempFile("newpipe_", "")
val dbJournal = File.createTempFile("newpipe_", "")
val dbWal = File.createTempFile("newpipe_", "")
val dbShm = File.createTempFile("newpipe_", "")
`when`(fileLocator.db).thenReturn(db)
val emptyZip = File(classloader.getResource("settings/empty.zip")?.file!!)
`when`(storedFileHelper.stream).thenReturn(FileStream(emptyZip))
val success = ImportExportManager(fileLocator).extractDb(storedFileHelper)
assertFalse(success)
assertTrue(dbJournal.exists())
assertTrue(dbWal.exists())
assertTrue(dbShm.exists())
assertEquals(0, Files.size(db.toPath()))
}
@Test
fun `Contains setting must return true if a settings file exists in the zip`() {
val settings = File.createTempFile("newpipe_", "")
`when`(fileLocator.settings).thenReturn(settings)
val zip = File(classloader.getResource("settings/newpipe.zip")?.file!!)
`when`(storedFileHelper.stream).thenReturn(FileStream(zip))
val contains = ImportExportManager(fileLocator).extractSettings(storedFileHelper)
assertTrue(contains)
}
@Test
fun `Contains setting must return false if a no settings file exists in the zip`() {
val settings = File.createTempFile("newpipe_", "")
`when`(fileLocator.settings).thenReturn(settings)
val emptyZip = File(classloader.getResource("settings/empty.zip")?.file!!)
`when`(storedFileHelper.stream).thenReturn(FileStream(emptyZip))
val contains = ImportExportManager(fileLocator).extractSettings(storedFileHelper)
assertFalse(contains)
}
@Test
fun `Preferences must be set from the settings file`() {
val settings = File(classloader.getResource("settings/newpipe.settings")!!.path)
`when`(fileLocator.settings).thenReturn(settings)
val preferences = Mockito.mock(SharedPreferences::class.java, withSettings().stubOnly())
val editor = Mockito.mock(SharedPreferences.Editor::class.java)
`when`(preferences.edit()).thenReturn(editor)
ImportExportManager(fileLocator).loadSharedPreferences(preferences)
verify(editor, atLeastOnce()).putBoolean(anyString(), anyBoolean())
verify(editor, atLeastOnce()).putString(anyString(), anyString())
verify(editor, atLeastOnce()).putInt(anyString(), anyInt())
}
@Test
fun `Importing preferences with a serialization injected class should fail`() {
val settings = File.createTempFile("newpipe_", "")
`when`(fileLocator.settings).thenReturn(settings)
val emptyZip = File(classloader.getResource("settings/vulnerable_serialization.zip")?.file!!)
`when`(storedFileHelper.stream).thenReturn(FileStream(emptyZip))
Assume.assumeTrue(ImportExportManager(fileLocator).extractSettings(storedFileHelper))
val preferences = Mockito.mock(SharedPreferences::class.java, withSettings().stubOnly())
val editor = Mockito.mock(SharedPreferences.Editor::class.java)
`when`(preferences.edit()).thenReturn(editor)
assertThrows(ClassNotFoundException::class.java) {
ImportExportManager(fileLocator).loadSharedPreferences(preferences)
}
}
}