diff --git a/build.gradle b/build.gradle index 6f95ae42..02fe0561 100644 --- a/build.gradle +++ b/build.gradle @@ -63,6 +63,7 @@ dependencies { compile 'com.github.kittinunf.fuel:fuel-rxjava:1.9.0' compile group: 'org.eclipse.jgit', name: 'org.eclipse.jgit', version: '4.8.0.201706111038-r' + testCompile 'org.jetbrains.kotlin:kotlin-test' testCompile 'org.jetbrains.spek:spek-api:1.1.2' testCompile 'org.junit.platform:junit-platform-runner:1.0.0-M4' @@ -76,7 +77,7 @@ protobuf { } sourceSets.main.java.srcDirs += 'build/generated/source/proto/main/java' -sourceSets.test.java.srcDirs += 'src/test' +sourceSets.test.java.srcDirs += 'src/test/kotlin' compileKotlin.dependsOn ':generateProto' // Include dependent libraries in archive. diff --git a/src/main/kotlin/app/hashers/CodeLongevity.kt b/src/main/kotlin/app/hashers/CodeLongevity.kt index 6307ac53..32e8e527 100644 --- a/src/main/kotlin/app/hashers/CodeLongevity.kt +++ b/src/main/kotlin/app/hashers/CodeLongevity.kt @@ -79,7 +79,6 @@ class CodeLine(val from: RevCommitLine, val to: RevCommitLine, class CodeLongevity(private val localRepo: LocalRepo, private val serverRepo: Repo, private val api: Api, - private val configurator: Configurator, private val git: Git, tailRev: String = "") { val CODE_LONGEVITY_KEY = "line-longevity-avg" val CODE_LONGEVITY_REPO_KEY = "line-longevity-repo-avg" diff --git a/src/main/kotlin/app/hashers/CommitHasher.kt b/src/main/kotlin/app/hashers/CommitHasher.kt index b90b4e5b..38d8bc74 100644 --- a/src/main/kotlin/app/hashers/CommitHasher.kt +++ b/src/main/kotlin/app/hashers/CommitHasher.kt @@ -23,6 +23,7 @@ import java.nio.charset.Charset import org.eclipse.jgit.diff.DiffFormatter import org.eclipse.jgit.lib.ObjectId import org.eclipse.jgit.errors.MissingObjectException +import org.eclipse.jgit.revwalk.RevCommit import org.eclipse.jgit.util.io.DisabledOutputStream import java.util.concurrent.TimeUnit @@ -32,7 +33,6 @@ import java.util.concurrent.TimeUnit class CommitHasher(private val localRepo: LocalRepo, private val repo: Repo = Repo(), private val api: Api, - private val configurator: Configurator, private val git: Git) { private val gitRepo: Repository = git.repository @@ -46,7 +46,9 @@ class CommitHasher(private val localRepo: LocalRepo, private fun hashAndSendCommits() { val lastKnownCommit = repo.commits.lastOrNull() val knownCommits = repo.commits.toHashSet() - getObservableCommits() + // Commits are combined in pairs, an empty commit concatenated to + // calculate the diff of the initial commit. + Observable.concat(getObservableCommits(), Observable.just(Commit())) .pairWithNext() // Pair commits to get diff. .takeWhile { (new, _) -> // Hash until last known commit. new.rehash != lastKnownCommit?.rehash } @@ -77,15 +79,12 @@ class CommitHasher(private val localRepo: LocalRepo, private fun getDiffFiles(commitNew: Commit, commitOld: Commit): List { // TODO(anatoly): Binary files. - val revCommitNew = commitNew.raw - val revCommitOld = commitOld.raw - if (revCommitNew == null || revCommitOld == null) { - return listOf() - } + val revCommitNew:RevCommit? = commitNew.raw + val revCommitOld:RevCommit? = commitOld.raw return DiffFormatter(DisabledOutputStream.INSTANCE).use { formatter -> formatter.setRepository(gitRepo) - formatter.scan(revCommitOld.tree, revCommitNew.tree) + formatter.scan(revCommitOld?.tree, revCommitNew?.tree) // RENAME change type doesn't change file content. .filter { it.changeType != DiffEntry.ChangeType.RENAME } .map { diff -> @@ -112,7 +111,7 @@ class CommitHasher(private val localRepo: LocalRepo, gitRepo.open(objectId).bytes.toString(Charset.defaultCharset()) .split('\n') } catch (e: MissingObjectException) { - listOf() + listOf() } } @@ -154,11 +153,11 @@ class CommitHasher(private val localRepo: LocalRepo, fun Observable.pairWithNext(): Observable> { return this.map { emit -> Pair(emit, emit) } - // Accumulate emits by prev-next pair. - .scan { pairAccumulated, pairNext -> - Pair(pairAccumulated.second, pairNext.second) - } - .skip(1) // Skip initial not paired emit. + // Accumulate emits by prev-next pair. + .scan { pairAccumulated, pairNext -> + Pair(pairAccumulated.second, pairNext.second) + } + .skip(1) // Skip initial not paired emit. } fun update() { diff --git a/src/main/kotlin/app/hashers/RepoHasher.kt b/src/main/kotlin/app/hashers/RepoHasher.kt index f6d9a759..f5ad7b2d 100644 --- a/src/main/kotlin/app/hashers/RepoHasher.kt +++ b/src/main/kotlin/app/hashers/RepoHasher.kt @@ -38,8 +38,8 @@ class RepoHasher(private val localRepo: LocalRepo, private val api: Api, getRepoFromServer() // Hash by all plugins. - CommitHasher(localRepo, repo, api, configurator, git).update() - CodeLongevity(localRepo, repo, api, configurator, git).update() + CommitHasher(localRepo, repo, api, git).update() + CodeLongevity(localRepo, repo, api, git).update() // Confirm hashing completion. postRepoToServer() diff --git a/src/test/CommitUploadProtocolTest.kt b/src/test/kotlin/app/CommitHasherTest.kt similarity index 72% rename from src/test/CommitUploadProtocolTest.kt rename to src/test/kotlin/app/CommitHasherTest.kt index 59b91466..67a8e5ad 100644 --- a/src/test/CommitUploadProtocolTest.kt +++ b/src/test/kotlin/app/CommitHasherTest.kt @@ -1,31 +1,44 @@ // Copyright 2017 Sourcerer Inc. All Rights Reserved. // Author: Liubov Yaronskaya (lyaronskaya@sourcerer.io) +// Author: Anatoly Kislov (anatoly@sourcerer.io) -package test +package app -import app.hashers.CommitHasher import app.api.MockApi -import app.config.MockConfigurator +import app.hashers.CommitHasher import app.model.* import app.utils.RepoHelper import org.eclipse.jgit.api.Git import org.jetbrains.spek.api.Spek -import org.jetbrains.spek.api.dsl.* +import org.jetbrains.spek.api.dsl.given +import org.jetbrains.spek.api.dsl.it import java.io.File import java.util.stream.StreamSupport.stream import kotlin.streams.toList import kotlin.test.assertEquals import kotlin.test.assertNotEquals -class CommitUploadProtocolTest : Spek({ +class CommitHasherTest : Spek({ + val userName = "Contributor" + val userEmail = "test@domain.com" + + // Creation of test repo. val repoPath = "./tmp_repo/.git" val git = Git.init().setGitDir(File(repoPath)).call() + val config = git.repository.config + config.setString("user", null, "name", userName) + config.setString("user", null, "email", userEmail) + config.save() + + // Common parameters for CommitHasher. val gitHasher = Git.open(File(repoPath)) - val localRepo: LocalRepo = LocalRepo(repoPath) - val initialCommit = Commit(git.commit().setMessage("Initial commit.").call()) - val repoRehash = RepoHelper.calculateRepoRehash(initialCommit.rehash, localRepo) - var repo = Repo(rehash = repoRehash, initialCommitRehash = initialCommit.rehash) - repo.commits = listOf(initialCommit) + val localRepo = LocalRepo(repoPath) + localRepo.author = Author(userName, userEmail) + val initialCommit = Commit(git.commit().setMessage("Initial commit").call()) + val repoRehash = RepoHelper.calculateRepoRehash(initialCommit.rehash, + localRepo) + val repo = Repo(rehash = repoRehash, + initialCommitRehash = initialCommit.rehash) fun getRepoRehash(git: Git, localRepo: LocalRepo): String { @@ -33,7 +46,7 @@ class CommitUploadProtocolTest : Spek({ .toList().first() val initialCommit = Commit(initialRevCommit) val repoRehash = RepoHelper.calculateRepoRehash(initialCommit.rehash, - localRepo) + localRepo) return repoRehash } @@ -43,12 +56,26 @@ class CommitUploadProtocolTest : Spek({ return lastCommit } - given("empty repo") { + given("repo with initial commit and no history") { + repo.commits = listOf() + + val mockApi = MockApi(mockRepo = repo) + CommitHasher(localRepo, repo, mockApi, gitHasher).update() + + it("send added commits") { + assertEquals(1, mockApi.receivedAddedCommits.size) + } + + it("doesn't send deleted commits") { + assertEquals(0, mockApi.receivedDeletedCommits.size) + } + } + + given("repo with initial commit") { repo.commits = listOf(getLastCommit(git)) val mockApi = MockApi(mockRepo = repo) - val mockConfigurator = MockConfigurator(mockRepos = mutableListOf(repo)) - CommitHasher(localRepo, repo, mockApi, mockConfigurator, gitHasher).update() + CommitHasher(localRepo, repo, mockApi, gitHasher).update() it("doesn't send added commits") { assertEquals(0, mockApi.receivedAddedCommits.size) @@ -61,12 +88,12 @@ class CommitUploadProtocolTest : Spek({ given("happy path: added one commit") { repo.commits = listOf(getLastCommit(git)) + val mockApi = MockApi(mockRepo = repo) - val mockConfigurator = MockConfigurator(mockRepos = mutableListOf(repo)) val revCommit = git.commit().setMessage("Second commit.").call() val addedCommit = Commit(revCommit) - CommitHasher(localRepo, repo, mockApi, mockConfigurator, gitHasher).update() + CommitHasher(localRepo, repo, mockApi, gitHasher).update() it("doesn't send deleted commits") { assertEquals(0, mockApi.receivedDeletedCommits.size) @@ -83,13 +110,16 @@ class CommitUploadProtocolTest : Spek({ given("happy path: added a few new commits") { repo.commits = listOf(getLastCommit(git)) + val mockApi = MockApi(mockRepo = repo) - val mockConfigurator = MockConfigurator(mockRepos = mutableListOf(repo)) val otherAuthorsNames = listOf("a", "b", "a") val otherAuthorsEmails = listOf("a@a", "b@b", "a@a") for (i in 0..2) { - git.commit().setMessage("Create $i.").setAuthor(otherAuthorsNames.get(i), otherAuthorsEmails.get(i)).call() + git.commit().setMessage("Create $i.") + .setAuthor(otherAuthorsNames.get(i), + otherAuthorsEmails.get(i)) + .call() } val authorCommits = mutableListOf() for (i in 0..4) { @@ -97,7 +127,7 @@ class CommitUploadProtocolTest : Spek({ val revCommit = git.commit().setMessage(message).call() authorCommits.add(Commit(revCommit)) } - CommitHasher(localRepo, repo, mockApi, mockConfigurator, gitHasher).update() + CommitHasher(localRepo, repo, mockApi, gitHasher).update() it("posts five commits as added") { assertEquals(5, mockApi.receivedAddedCommits.size) @@ -108,12 +138,12 @@ class CommitUploadProtocolTest : Spek({ } it("processes author's commits") { - assertEquals(authorCommits.asReversed(), mockApi.receivedAddedCommits) + assertEquals(authorCommits.asReversed(), + mockApi.receivedAddedCommits) } } given("fork event") { - val forkedRepoPath = "./forked_repo/" val originalRepoPath = "./original_repo/" val forked = Git.cloneRepository() @@ -138,13 +168,12 @@ class CommitUploadProtocolTest : Spek({ forked.close() original.repository.close() original.close() - } given("lost server") { repo.commits = listOf(getLastCommit(git)) - var mockApi = MockApi(mockRepo = repo) - var mockConfigurator = MockConfigurator(mockRepos = mutableListOf(repo)) + + val mockApi = MockApi(mockRepo = repo) // Add some commits. val addedCommits = mutableListOf() @@ -153,14 +182,12 @@ class CommitUploadProtocolTest : Spek({ val revCommit = git.commit().setMessage(message).call() addedCommits.add(Commit(revCommit)) } - CommitHasher(localRepo, repo, mockApi, mockConfigurator, gitHasher).update() // Remove one commit from server history. val removedCommit = addedCommits.removeAt(1) repo.commits = addedCommits.toList().asReversed() - mockConfigurator = MockConfigurator(mockRepos = mutableListOf(repo)) - mockApi = MockApi(mockRepo = repo) - CommitHasher(localRepo, repo, mockApi, mockConfigurator, gitHasher).update() + + CommitHasher(localRepo, repo, mockApi, gitHasher).update() it("adds posts one commit as added and received commit is lost one") { assertEquals(1, mockApi.receivedAddedCommits.size) @@ -170,7 +197,6 @@ class CommitUploadProtocolTest : Spek({ it("doesn't posts deleted commits") { assertEquals(0, mockApi.receivedDeletedCommits.size) } - } Runtime.getRuntime().exec("src/test/delete_repo.sh").waitFor()