diff --git a/app/build.gradle b/app/build.gradle index e4f2e03e1..e08af72ba 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -10,8 +10,8 @@ android { defaultConfig { minSdkVersion 21 targetSdkVersion 33 - versionCode 212 - versionName "1.8.24" + versionCode 214 + versionName "1.8.25" applicationId = "com.orgzlyrevived" resValue "string", "application_id", "com.orgzlyrevived" diff --git a/app/src/androidTest/java/com/orgzly/android/repos/GitRepoTest.kt b/app/src/androidTest/java/com/orgzly/android/repos/GitRepoTest.kt new file mode 100644 index 000000000..3f4b1a260 --- /dev/null +++ b/app/src/androidTest/java/com/orgzly/android/repos/GitRepoTest.kt @@ -0,0 +1,152 @@ +package com.orgzly.android.repos + +import android.net.Uri +import android.os.Build +import androidx.core.net.toUri +import com.orgzly.R +import com.orgzly.android.OrgzlyTest +import com.orgzly.android.db.entity.BookView +import com.orgzly.android.db.entity.Repo +import com.orgzly.android.git.GitFileSynchronizer +import com.orgzly.android.git.GitPreferencesFromRepoPrefs +import com.orgzly.android.prefs.AppPreferences +import com.orgzly.android.prefs.RepoPreferences +import com.orgzly.android.util.MiscUtils +import org.eclipse.jgit.api.Git +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Assume +import org.junit.Rule +import org.junit.Test +import org.junit.rules.ExpectedException +import java.io.File +import java.io.IOException +import java.nio.file.Path +import kotlin.io.path.createTempDirectory + +class GitRepoTest : OrgzlyTest() { + + private lateinit var bareRepoPath: Path + private lateinit var repoUri: Uri + private lateinit var gitPreferences: GitPreferencesFromRepoPrefs + private lateinit var workingtree: File + private lateinit var repoPreferences: RepoPreferences + private lateinit var repo: Repo + private lateinit var syncRepo: GitRepo + private lateinit var git: Git + private lateinit var synchronizer: GitFileSynchronizer + + @Rule + @JvmField + var exceptionRule: ExpectedException = ExpectedException.none() + + override fun setUp() { + super.setUp() + bareRepoPath = createTempDirectory() + Git.init().setBare(true).setDirectory(bareRepoPath.toFile()).call() + AppPreferences.gitIsEnabled(context, true) + repoUri = bareRepoPath.toFile().toUri() + repo = testUtils.setupRepo(RepoType.GIT, repoUri.toString()) + repoPreferences = RepoPreferences(context, repo.id, repoUri) + gitPreferences = GitPreferencesFromRepoPrefs(repoPreferences) + workingtree = File(gitPreferences.repositoryFilepath()) + workingtree.mkdirs() + git = GitRepo.ensureRepositoryExists(gitPreferences, true, null) + syncRepo = dataRepository.getRepoInstance(repo.id, RepoType.GIT, repo.url) as GitRepo + synchronizer = GitFileSynchronizer(git, gitPreferences) + } + + override fun tearDown() { + super.tearDown() + testUtils.deleteRepo(repo.url) + workingtree.deleteRecursively() + bareRepoPath.toFile()?.deleteRecursively() + } + + @Test + fun testSyncNewBookWithoutLinkAndOneRepo() { + testUtils.setupBook("book1", "book content") + testUtils.sync() + val bookView = dataRepository.getBooks()[0] + assertEquals(repoUri.toString(), bookView.linkRepo?.url) + assertEquals(1, syncRepo.books.size) + assertEquals(bookView.syncedTo.toString(), syncRepo.books[0].toString()) + assertEquals(context.getString(R.string.sync_status_saved, repo.url), bookView.book.lastAction!!.message) + assertEquals("/book1.org", bookView.syncedTo!!.uri.toString()) + } + + @Test + fun testIgnoredFilesInRepoAreNotLoaded() { + Assume.assumeTrue(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) + // Create ignore file in working tree and commit + val ignoreFileContents = """ + ignoredbook.org + ignored-*.org + """.trimIndent() + addAndCommitIgnoreFile(ignoreFileContents) + // Add multiple files to repo + for (fileName in arrayOf("ignoredbook.org", "ignored-3.org", "notignored.org")) { + val tmpFile = File.createTempFile("orgzlytest", null) + MiscUtils.writeStringToFile("book content", tmpFile) + synchronizer.addAndCommitNewFile(tmpFile, fileName) + tmpFile.delete() + } + testUtils.sync() + assertEquals(1, syncRepo.books.size) + assertEquals(1, dataRepository.getBooks().size) + assertEquals("notignored", dataRepository.getBooks()[0].book.name) + } + + @Test + fun testUnIgnoredFilesInRepoAreLoaded() { + Assume.assumeTrue(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) + // Create ignore file in working tree and commit + val ignoreFileContents = """ + *.org + !notignored.org + """.trimIndent() + addAndCommitIgnoreFile(ignoreFileContents) + // Add multiple files to repo + for (fileName in arrayOf("ignoredbook.org", "ignored-3.org", "notignored.org")) { + val tmpFile = File.createTempFile("orgzlytest", null) + MiscUtils.writeStringToFile("book content", tmpFile) + synchronizer.addAndCommitNewFile(tmpFile, fileName) + tmpFile.delete() + } + testUtils.sync() + assertEquals(1, syncRepo.books.size) + assertEquals(1, dataRepository.getBooks().size) + assertEquals("notignored", dataRepository.getBooks()[0].book.name) + } + + @Test + fun testIgnoreRulePreventsLinkingBook() { + Assume.assumeTrue(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) + addAndCommitIgnoreFile("*.org") + testUtils.setupBook("booky", "") + exceptionRule.expect(IOException::class.java) + exceptionRule.expectMessage("matches a rule in .orgzlyignore") + testUtils.syncOrThrow() + } + + @Test + fun testIgnoreRulePreventsRenamingBook() { + Assume.assumeTrue(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) + addAndCommitIgnoreFile("badname*") + testUtils.setupBook("goodname", "") + testUtils.sync() + var bookView: BookView? = dataRepository.getBookView("goodname") + dataRepository.renameBook(bookView!!, "badname") + bookView = dataRepository.getBooks()[0] + assertTrue( + bookView.book.lastAction.toString().contains("matches a rule in .orgzlyignore") + ) + } + + private fun addAndCommitIgnoreFile(contents: String) { + val tmpFile = File.createTempFile("orgzlytest", null) + MiscUtils.writeStringToFile(contents, tmpFile) + synchronizer.addAndCommitNewFile(tmpFile, RepoIgnoreNode.IGNORE_FILE) + tmpFile.delete() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/orgzly/android/repos/SyncRepo.java b/app/src/main/java/com/orgzly/android/repos/SyncRepo.java index 192bbf470..ff09131d4 100644 --- a/app/src/main/java/com/orgzly/android/repos/SyncRepo.java +++ b/app/src/main/java/com/orgzly/android/repos/SyncRepo.java @@ -41,6 +41,14 @@ public interface SyncRepo { */ InputStream openRepoFileInputStream(String fileName) throws IOException; + /** + * Open a file in the repository for reading. Originally added for parsing the .orgzlyignore + * file. + * @param fileName The file to open + * @throws IOException + */ + InputStream openRepoFileInputStream(String fileName) throws IOException; + /** * Uploads book storing it under given filename under repo's url. * @param file The contents of this file should be stored at the remote location/repo diff --git a/app/src/main/java/com/orgzly/android/repos/WebdavRepo.kt b/app/src/main/java/com/orgzly/android/repos/WebdavRepo.kt index 03f96d475..e807e8633 100644 --- a/app/src/main/java/com/orgzly/android/repos/WebdavRepo.kt +++ b/app/src/main/java/com/orgzly/android/repos/WebdavRepo.kt @@ -291,4 +291,4 @@ class WebdavRepo( private fun Uri.toUrl(): String { return this.toString().replace("^(?:web)?dav(s?://)".toRegex(), "http$1") } -} \ No newline at end of file +} diff --git a/app/src/main/res/layout/dialog_whats_new.xml b/app/src/main/res/layout/dialog_whats_new.xml index 28a7422d9..1cb332d59 100644 --- a/app/src/main/res/layout/dialog_whats_new.xml +++ b/app/src/main/res/layout/dialog_whats_new.xml @@ -20,6 +20,19 @@ android:layout_height="wrap_content" /> + + + + + +