Project: agit
/*
 * Copyright (c) 2011, 2012 Roberto Tyley 
 * 
 * This file is part of 'Agit' - an Android Git client. 
 * 
 * Agit is free software: you can redistribute it and/or modify 
 * it under the terms of the GNU General Public License as published by 
 * the Free Software Foundation, either version 3 of the License, or 
 * (at your option) any later version. 
 * 
 * Agit is distributed in the hope that it will be useful, 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 * GNU General Public License for more details. 
 * 
 * You should have received a copy of the GNU General Public License 
 * along with this program.  If not, see http://www.gnu.org/licenses/ . 
 */
 
package com.madgag.agit; 
 
import static com.google.inject.util.Modules.override; 
import static com.madgag.agit.GitTestUtils.DSA_USER; 
import static com.madgag.agit.GitTestUtils.RSA_USER; 
import static com.madgag.agit.GitTestUtils.integrationGitServerURIFor; 
import static com.madgag.agit.git.Repos.remoteConfigFor; 
import static com.madgag.agit.matchers.HasGitObjectMatcher.hasGitObject; 
import static com.madgag.hamcrest.FileExistenceMatcher.exists; 
import static com.madgag.hamcrest.FileLengthMatcher.ofLength; 
import static java.lang.System.currentTimeMillis; 
import static java.lang.Thread.currentThread; 
import static java.util.concurrent.TimeUnit.SECONDS; 
import static org.eclipse.jgit.lib.Constants.DEFAULT_REMOTE_NAME; 
import static org.hamcrest.MatcherAssert.assertThat; 
import static org.hamcrest.Matchers.is; 
import static org.hamcrest.Matchers.not; 
import static roboguice.RoboGuice.newDefaultRoboModule; 
import android.app.Application; 
import android.os.Looper; 
import android.test.ActivityInstrumentationTestCase2; 
import android.test.suitebuilder.annotation.MediumTest; 
import android.util.Log; 
 
import com.google.inject.Injector; 
import com.madgag.agit.matchers.GitTestHelper; 
import com.madgag.agit.operation.lifecycle.OperationLifecycleSupport; 
import com.madgag.agit.operations.Clone; 
import com.madgag.agit.operations.GitAsyncTask; 
import com.madgag.agit.operations.GitAsyncTaskFactory; 
import com.madgag.agit.operations.GitOperation; 
import com.madgag.agit.operations.OpNotification; 
import com.madgag.agit.operations.Progress; 
import com.madgag.agit.operations.Pull; 
 
import java.io.File; 
import java.io.IOException; 
import java.util.concurrent.CountDownLatch; 
 
import org.eclipse.jgit.lib.Repository; 
import org.eclipse.jgit.storage.file.FileRepository; 
import org.eclipse.jgit.transport.RemoteConfig; 
import org.eclipse.jgit.transport.URIish; 
 
import roboguice.RoboGuice; 
 
public class GitAsyncTaskTest extends ActivityInstrumentationTestCase2<DashboardActivity> { 
 
    private static final String TAG = "GitAsyncTaskTest"
 
    private Injector injector; 
 
    public GitAsyncTaskTest() { 
        super("com.madgag.agit", DashboardActivity.class); 
    } 
 
    private GitTestHelper helper() { 
        return AndroidTestEnvironment.helper(getInstrumentation()); 
    } 
 
    @Override 
    public void setUp() throws ClassNotFoundException, InstantiationException, IllegalAccessException { 
        Application application = (Application) getInstrumentation().getTargetContext().getApplicationContext(); 
        injector = RoboGuice.setBaseApplicationInjector(application, RoboGuice.DEFAULT_STAGE, 
                newDefaultRoboModule(application), 
                override(new AgitModule()).with(new AgitIntegrationTestModule())); 
    } 
 
    @Override 
    public void tearDown() { 
        RoboGuice.util.reset(); 
    } 
 
    @MediumTest 
    public void testCloneRepoWithEmptyBlobInPack() throws Exception { 
        Clone cloneOp = new Clone(true, integrationGitServerURIFor("tiny-repo.with-empty-file.git"), 
                helper().newFolder()); 
 
        Repository repo = executeAndWaitFor(cloneOp); 
        assertThat(repo, hasGitObject("e69de29bb2d1d6434b8b29ae775ad8c2e48c5391")); // empty blob 
        assertThat(repo, hasGitObject("adcb77d8f74590f54b4c1919b322aed456b22aeb")); // populated blob 
    } 
 
    @MediumTest 
    public void testCloneNonBareRepoFromLocalTestServer() throws Exception { 
        Clone cloneOp = new Clone(false, integrationGitServerURIFor("small-repo.early.git"), helper().newFolder()); 
 
        Repository repo = executeAndWaitFor(cloneOp); 
 
        assertThat(repo, hasGitObject("ba1f63e4430bff267d112b1e8afc1d6294db0ccc")); 
 
        File readmeFile = new File(repo.getWorkTree(), "README"); 
        assertThat(readmeFile, exists()); 
        assertThat(readmeFile, ofLength(12)); 
    } 
 
    @MediumTest 
    public void testPullUpdatesFromLocalTestServer() throws Exception { 
        Repository repository = helper().unpackRepo("small-test-repo.early.zip"); 
        RemoteConfig remoteConfig = remoteConfigFor(repository, DEFAULT_REMOTE_NAME); 
        for (URIish urIish : remoteConfig.getURIs()) { 
            remoteConfig.removeURI(urIish); 
        } 
        remoteConfig.addURI(integrationGitServerURIFor("small-test-repo.later.git")); 
        remoteConfig.update(repository.getConfig()); 
        repository.getConfig().save(); 
        // Git.wrap(repository).branchCreate().setName("master").setStartPoint("origin/master"); 
 
        assertThat(repository, hasGitObject("3974996807a9f596cf25ac3a714995c24bb97e2c")); 
        String commit1 = "ce1e0703402e989bedf03d5df535401340f54b42"
        assertThat(repository, not(hasGitObject(commit1))); 
        assertFileLength(2, repository.getWorkTree(), "EXAMPLE"); 
        Pull pullOp = new Pull(repository); 
 
        executeAndWaitFor(pullOp); 
        assertThat(repository, hasGitObject(commit1)); 
 
        assertFileLength(4, repository.getWorkTree(), "EXAMPLE"); 
    } 
 
    private void assertFileLength(int length, File workTree, String exampleFile) { 
        File readmeFile = new File(workTree, exampleFile); 
        assertThat(readmeFile, exists()); 
        assertThat("len=" + readmeFile.length(), readmeFile, ofLength(length)); 
    } 
 
    @MediumTest 
    public void testCloneRepoUsingRSA() throws Exception { 
        Clone cloneOp = new Clone(true, integrationGitServerURIFor("small-repo.early.git").setUser(RSA_USER), 
                helper().newFolder()); 
 
        assertThat(executeAndWaitFor(cloneOp), hasGitObject("ba1f63e4430bff267d112b1e8afc1d6294db0ccc")); 
    } 
 
    @MediumTest 
    public void testCloneRepoUsingDSA() throws Exception { 
        Clone cloneOp = new Clone(true, integrationGitServerURIFor("small-repo.early.git").setUser(DSA_USER), 
                helper().newFolder()); 
 
        assertThat(executeAndWaitFor(cloneOp), hasGitObject("ba1f63e4430bff267d112b1e8afc1d6294db0ccc")); 
    } 
 
    @MediumTest 
    public void testSimpleReadOnlyCloneFromGitHub() throws Exception { 
        Clone cloneOp = new Clone(falsenew URIish("git://github.com/agittest/small-project.git"), 
                helper().newFolder()); 
        Repository repo = executeAndWaitFor(cloneOp); 
 
        assertThat(repo, hasGitObject("9e0b5e42b3e1c59bc83b55142a8c50dfae36b144")); 
        assertThat(repo, not(hasGitObject("111111111111111111111111111111111111cafe"))); 
 
        File readmeFile = new File(repo.getWorkTree(), "README"); 
        assertThat(readmeFile, exists()); 
    } 
 
    @MediumTest 
    public void testNonBareCloneFromRepoWithFiveMBBlobForIssue47() throws Exception { 
        Clone cloneOp = new Clone(falsenew URIish("git://github.com/rtyley/five-mb-file-test-repo.git"), 
                helper().newFolder()); 
        Repository repo = executeAndWaitFor(cloneOp); 
 
        assertThat(repo, hasGitObject("3995316735a53542acdf0d92e0b725fe296c0b49")); 
        assertThat(repo, not(hasGitObject("111111111111111111111111111111111111cafe"))); 
 
        File bigFile = new File(repo.getWorkTree(), "5mb.zeros"); 
        assertThat(bigFile, exists()); 
    } 
 
//    @LargeTest 
// public void testCanCloneAllSuggestedRepos() throws Exception { 
//        for (SuggestedRepo suggestedRepo : SUGGESTIONS) { 
//            Repository repo = executeAndWaitFor(new Clone(true, new URIish(suggestedRepo.getURI()), tempFolder())); 
//            Map<String,Ref> allRefs = repo.getAllRefs(); 
//            assertThat("Refs for " + suggestedRepo + " @ " + repo, allRefs.size(), greaterThan(0)); 
//        } 
// } 
 
    private Repository executeAndWaitFor(final GitOperation operation) 
            throws InterruptedException, IOException { 
        final CountDownLatch latch = new CountDownLatch(1); 
        Log.d(TAG, "About to start " + operation); 
        new Thread() { 
            public void run() { 
                Looper.prepare(); 
                Log.d(TAG, "In run method for " + operation); 
                GitAsyncTask task = injector.getInstance(GitAsyncTaskFactory.class).createTaskFor(operation, 
                        new OperationLifecycleSupport() { 
                            public void startedWith(OpNotification ongoingNotification) { 
                                Log.i(TAG, "Started " + operation + " with " + ongoingNotification); 
                            } 
 
                            public void publish(Progress progress) { 
                            } 
 
                            public void error(OpNotification notification) { 
                                Log.i(TAG, "Errored " + operation + " with " + notification); 
                            } 
 
                            public void success(OpNotification completionNotification) { 
                            } 
 
                            public void completed(OpNotification completionNotification) { 
                                Log.i(TAG, "Completed " + operation + " with " + completionNotification); 
                                latch.countDown(); 
                            } 
                        }); 
                task.execute(); 
                Log.d(TAG, "Called execute() on task for " + operation); 
                Looper.loop(); 
            } 
        }.start(); 
        long startTime = currentTimeMillis(); 
        Log.i(TAG, "Waiting for " + operation + " to complete - currentThread=" + currentThread()); 
        // http://stackoverflow.com/questions/5497324/why-arent-java-util-concurrent-timeunit-types-greater-than 
        // -seconds-available-in 
        boolean timeout = !latch.await(7 * 60, SECONDS); 
        long duration = currentTimeMillis() - startTime; 
        Log.i(TAG, "Finished waiting - timeout=" + timeout + " duration=" + duration); 
        assertThat("Timeout for " + operation, timeout, is(false)); 
        return new FileRepository(operation.getGitDir()); 
    } 
 
}