Skip to content
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
3 changes: 2 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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.
Expand Down
1 change: 0 additions & 1 deletion src/main/kotlin/app/hashers/CodeLongevity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
27 changes: 13 additions & 14 deletions src/main/kotlin/app/hashers/CommitHasher.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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

Expand All @@ -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 }
Expand Down Expand Up @@ -77,15 +79,12 @@ class CommitHasher(private val localRepo: LocalRepo,
private fun getDiffFiles(commitNew: Commit,
commitOld: Commit): List<DiffFile> {
// 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 ->
Expand All @@ -112,7 +111,7 @@ class CommitHasher(private val localRepo: LocalRepo,
gitRepo.open(objectId).bytes.toString(Charset.defaultCharset())
.split('\n')
} catch (e: MissingObjectException) {
listOf<String>()
listOf()
}
}

Expand Down Expand Up @@ -154,11 +153,11 @@ class CommitHasher(private val localRepo: LocalRepo,

fun <T> Observable<T>.pairWithNext(): Observable<Pair<T, T>> {
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() {
Expand Down
4 changes: 2 additions & 2 deletions src/main/kotlin/app/hashers/RepoHasher.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,39 +1,52 @@
// 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 {

val initialRevCommit = stream(git.log().call().spliterator(), false)
.toList().first()
val initialCommit = Commit(initialRevCommit)
val repoRehash = RepoHelper.calculateRepoRehash(initialCommit.rehash,
localRepo)
localRepo)
return repoRehash
}

Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -83,21 +110,24 @@ 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<Commit>()
for (i in 0..4) {
val message = "Created $i by author."
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)
Expand All @@ -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()
Expand All @@ -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<Commit>()
Expand All @@ -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)
Expand All @@ -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()
Expand Down