public void GetChunk_MiddleBytes_Ok()
 {
     using (var e = new PushStorageEnvForTest())
     {
         string text = "This is some sample text to be used by the push bundle helper tests";
         var bundleHelper = new PushStorageManager(e.PullDataFolderPath, "randomHash");
         File.WriteAllText(bundleHelper.BundlePath, text);
         byte[] chunk = bundleHelper.GetChunk(10, 5);
         Assert.That(Encoding.UTF8.GetString(chunk), Is.EqualTo("me sa"));
     }
 }
 public void GetChunk_LengthTooLarge_ReturnsAdjustedByteArray()
 {
     using (var e = new PushStorageEnvForTest())
     {
         string text = "sample";
         var bundleHelper = new PushStorageManager(e.PullDataFolderPath, "randomHash");
         File.WriteAllText(bundleHelper.BundlePath, text);
         byte[] chunk = bundleHelper.GetChunk(3, 10); // offset is greater than string length
         Assert.That(Encoding.UTF8.GetString(chunk), Is.EqualTo("ple"));
     }
 }
Example #3
0
        public void Push()
        {
            var baseRevisions = GetCommonBaseHashesWithRemoteRepo();

            if (baseRevisions == null)
            {
                const string errorMessage = "Push failed: A common revision could not be found with the server.";
                _progress.WriteError(errorMessage);
                throw new HgResumeOperationFailed(errorMessage);
            }

            // create a bundle to push
            var bundleHelper = new PushStorageManager(PathToLocalStorage,
                                                      GetBundleIdFilenameBase("push", baseRevisions.Select(rev => rev.Number.Hash), _repo.GetTip().Number.Hash));
            var bundleFileInfo = new FileInfo(bundleHelper.BundlePath);

            if (bundleFileInfo.Length == 0)
            {
                _progress.WriteStatus("Preparing data to send");
                bool bundleCreatedSuccessfully = _repo.MakeBundle(GetHashStringsFromRevisions(baseRevisions), bundleHelper.BundlePath);
                if (!bundleCreatedSuccessfully)
                {
                    // try again after clearing revision cache
                    LastKnownCommonBases      = new List <Revision>();
                    baseRevisions             = GetCommonBaseHashesWithRemoteRepo();
                    bundleCreatedSuccessfully = _repo.MakeBundle(GetHashStringsFromRevisions(baseRevisions), bundleHelper.BundlePath);
                    if (!bundleCreatedSuccessfully)
                    {
                        const string errorMessage = "Push failed: Unable to create local bundle.";
                        _progress.WriteError(errorMessage);
                        throw new HgResumeOperationFailed(errorMessage);
                    }
                }
                bundleFileInfo.Refresh();
                if (bundleFileInfo.Length == 0)
                {
                    bundleHelper.Cleanup();
                    _progress.WriteMessage("No changes to send.  Push operation completed");
                    return;
                }
            }

            var req = new HgResumeApiParameters
            {
                RepoId        = _apiServer.ProjectId,
                TransId       = bundleHelper.TransactionId,
                StartOfWindow = 0,
                BundleSize    = (int)bundleFileInfo.Length
            };

            req.ChunkSize = (req.BundleSize < InitialChunkSize) ? req.BundleSize : InitialChunkSize;
//            req.BaseHash = baseRevisions; <-- Unless I'm not reading the php right we don't need to set this on push.  JLN Aug-12
            _progress.ProgressIndicator.Initialize();

            _progress.WriteStatus("Sending data");
            int loopCtr = 0;

            do             // loop until finished... or until the user cancels
            {
                loopCtr++;
                if (_progress.CancelRequested)
                {
                    throw new UserCancelledException();
                }

                int dataRemaining = req.BundleSize - req.StartOfWindow;
                if (dataRemaining < req.ChunkSize)
                {
                    req.ChunkSize = dataRemaining;
                }
                byte[] bundleChunk = bundleHelper.GetChunk(req.StartOfWindow, req.ChunkSize);

                /* API parameters
                 * $repoId, $baseHash, $bundleSize, $offset, $data, $transId
                 * */
                var response = PushOneChunk(req, bundleChunk);
                if (response.Status == PushStatus.NotAvailable)
                {
                    _progress.ProgressIndicator.Initialize();
                    _progress.ProgressIndicator.Finish();
                    return;
                }
                if (response.Status == PushStatus.Timeout)
                {
                    _progress.WriteWarning("Push operation timed out.  Retrying...");
                    continue;
                }
                if (response.Status == PushStatus.InvalidHash)
                {
                    // this should not happen...but sometimes it gets into a state where it remembers the wrong basehash of the server (CJH Feb-12)
                    _progress.WriteVerbose("Invalid basehash response received from server... clearing cache and retrying");
//                    req.BaseHash = GetCommonBaseHashesWithRemoteRepo(false); <-- Unless I'm misreading the php we don't need to set this on a push. JLN Aug-12
                    continue;
                }
                if (response.Status == PushStatus.Fail)
                {
                    // This 'Fail' intentionally aborts the push attempt.  I think we can continue to go around the loop and retry. See Pull also. CP 2012-06
                    continue;
                    //var errorMessage = "Push operation failed";
                    //_progress.WriteError(errorMessage);
                    //throw new HgResumeOperationFailed(errorMessage);
                }
                if (response.Status == PushStatus.Reset)
                {
                    FinishPush(req.TransId);
                    bundleHelper.Cleanup();
                    const string errorMessage = "Push failed: Server reset.";
                    _progress.WriteError(errorMessage);
                    throw new HgResumeOperationFailed(errorMessage);
                }

                if (response.Status == PushStatus.Complete || req.StartOfWindow >= req.BundleSize)
                {
                    if (response.Status == PushStatus.Complete)
                    {
                        _progress.WriteMessage("Finished sending");
                    }
                    else
                    {
                        _progress.WriteMessage("Finished sending. Server unpacking data");
                    }
                    _progress.ProgressIndicator.Finish();

                    // update our local knowledge of what the server has
                    LastKnownCommonBases = new List <Revision>(_repo.BranchingHelper.GetBranches());                    // This may be a little optimistic, the server may still be unbundling the data.
                    //FinishPush(req.TransId);  // We can't really tell when the server has finished processing our pulled data.  The server cleans up after itself. CP 2012-07
                    bundleHelper.Cleanup();
                    return;
                }

                req.ChunkSize     = response.ChunkSize;
                req.StartOfWindow = response.StartOfWindow;
                if (loopCtr == 1 && req.StartOfWindow > req.ChunkSize)
                {
                    string message = String.Format("Resuming push operation at {0} sent", GetHumanReadableByteSize(req.StartOfWindow));
                    _progress.WriteVerbose(message);
                }
                string eta = CalculateEstimatedTimeRemaining(req.BundleSize, req.ChunkSize, req.StartOfWindow);
                _progress.WriteStatus(string.Format("Sending {0} {1}", GetHumanReadableByteSize(req.BundleSize), eta));
                _progress.ProgressIndicator.PercentCompleted = (int)((long)req.StartOfWindow * 100 / req.BundleSize);
            } while (req.StartOfWindow < req.BundleSize);
        }
Example #4
0
 public PullHandlerApiServerForTest(HgRepository repo, ProgressForTest progress)
 {
     const string identifier = "PullHandlerApiServerForTest";
     _storageFolder = new TemporaryFolder(identifier);
     _repo = repo;
     _progress = progress;
     _helper = new PushStorageManager(_storageFolder.Path, "randomHash");
     Host = identifier;
     ProjectId = "SampleProject";
     _executeCount = 0;
     _failCount = -1;
     _timeoutList = new List<int>();
     _serverUnavailableList = new List<ServerUnavailableResponse>();
     OriginalTip = "";
 }
Example #5
0
        public void Push()
        {
            var baseRevisions = GetCommonBaseHashesWithRemoteRepo();
            if (baseRevisions == null)
            {
                const string errorMessage = "Push failed: A common revision could not be found with the server.";
                _progress.WriteError(errorMessage);
                throw new HgResumeOperationFailed(errorMessage);
            }

            // create a bundle to push
            string tip = _repo.GetTip().Number.Hash;
            string bundleId = "";
            foreach (var revision in baseRevisions)
            {
                bundleId += String.Format("{0}-{1}", revision.Number.Hash, tip);
            }
            var bundleHelper = new PushStorageManager(PathToLocalStorage, bundleId);
            var bundleFileInfo = new FileInfo(bundleHelper.BundlePath);
            if (bundleFileInfo.Length == 0)
            {
                _progress.WriteStatus("Preparing data to send");
                bool bundleCreatedSuccessfully = _repo.MakeBundle(GetHashStringsFromRevisions(baseRevisions), bundleHelper.BundlePath);
                if (!bundleCreatedSuccessfully)
                {
                    // try again after clearing revision cache
                    LastKnownCommonBases = new List<Revision>();
                    baseRevisions = GetCommonBaseHashesWithRemoteRepo();
                    bundleCreatedSuccessfully = _repo.MakeBundle(GetHashStringsFromRevisions(baseRevisions), bundleHelper.BundlePath);
                    if (!bundleCreatedSuccessfully)
                    {
                        const string errorMessage = "Push failed: Unable to create local bundle.";
                        _progress.WriteError(errorMessage);
                        throw new HgResumeOperationFailed(errorMessage);
                    }
                }
                bundleFileInfo.Refresh();
                if (bundleFileInfo.Length == 0)
                {
                    bundleHelper.Cleanup();
                    _progress.WriteMessage("No changes to send.  Push operation completed");
                    return;
                }
            }

            var req = new HgResumeApiParameters
                      {
                          RepoId = _apiServer.ProjectId,
                          TransId = bundleHelper.TransactionId,
                          StartOfWindow = 0,
                          BundleSize = (int) bundleFileInfo.Length
                      };
            req.ChunkSize = (req.BundleSize < InitialChunkSize) ? req.BundleSize : InitialChunkSize;
            //            req.BaseHash = baseRevisions; <-- Unless I'm not reading the php right we don't need to set this on push.  JLN Aug-12
            _progress.ProgressIndicator.Initialize();

            _progress.WriteStatus("Sending data");
            int loopCtr = 0;
            do // loop until finished... or until the user cancels
            {
                loopCtr++;
                if (_progress.CancelRequested)
                {
                    throw new UserCancelledException();
                }

                int dataRemaining = req.BundleSize - req.StartOfWindow;
                if (dataRemaining < req.ChunkSize)
                {
                    req.ChunkSize = dataRemaining;
                }
                byte[] bundleChunk = bundleHelper.GetChunk(req.StartOfWindow, req.ChunkSize);

                /* API parameters
                 * $repoId, $baseHash, $bundleSize, $offset, $data, $transId
                 * */
                var response = PushOneChunk(req, bundleChunk);
                if (response.Status == PushStatus.NotAvailable)
                {
                    _progress.ProgressIndicator.Initialize();
                    _progress.ProgressIndicator.Finish();
                    return;
                }
                if (response.Status == PushStatus.Timeout)
                {
                    _progress.WriteWarning("Push operation timed out.  Retrying...");
                    continue;
                }
                if (response.Status == PushStatus.InvalidHash)
                {
                    // this should not happen...but sometimes it gets into a state where it remembers the wrong basehash of the server (CJH Feb-12)
                    _progress.WriteVerbose("Invalid basehash response received from server... clearing cache and retrying");
            //                    req.BaseHash = GetCommonBaseHashesWithRemoteRepo(false); <-- Unless I'm misreading the php we don't need to set this on a push. JLN Aug-12
                    continue;
                }
                if (response.Status == PushStatus.Fail)
                {
                    // This 'Fail' intentionally aborts the push attempt.  I think we can continue to go around the loop and retry. See Pull also. CP 2012-06
                    continue;
                    //var errorMessage = "Push operation failed";
                    //_progress.WriteError(errorMessage);
                    //throw new HgResumeOperationFailed(errorMessage);
                }
                if (response.Status == PushStatus.Reset)
                {
                    FinishPush(req.TransId);
                    bundleHelper.Cleanup();
                    const string errorMessage = "Push failed: Server reset.";
                    _progress.WriteError(errorMessage);
                    throw new HgResumeOperationFailed(errorMessage);
                }

                if (response.Status == PushStatus.Complete || req.StartOfWindow >= req.BundleSize)
                {
                    if (response.Status == PushStatus.Complete)
                    {
                        _progress.WriteMessage("Finished sending");
                    } else
                    {
                        _progress.WriteMessage("Finished sending. Server unpacking data");
                    }
                    _progress.ProgressIndicator.Finish();

                    // update our local knowledge of what the server has
                    LastKnownCommonBases = new List<Revision>(_repo.BranchingHelper.GetBranches()); // This may be a little optimistic, the server may still be unbundling the data.
                    //FinishPush(req.TransId);  // We can't really tell when the server has finished processing our pulled data.  The server cleans up after itself. CP 2012-07
                    bundleHelper.Cleanup();
                    return;
                }

                req.ChunkSize = response.ChunkSize;
                req.StartOfWindow = response.StartOfWindow;
                if (loopCtr == 1 && req.StartOfWindow > req.ChunkSize)
                {
                    string message = String.Format("Resuming push operation at {0} sent", GetHumanReadableByteSize(req.StartOfWindow));
                    _progress.WriteVerbose(message);
                }
                string eta = CalculateEstimatedTimeRemaining(req.BundleSize, req.ChunkSize, req.StartOfWindow);
                _progress.WriteStatus(string.Format("Sending {0} {1}", GetHumanReadableByteSize(req.BundleSize), eta));
                _progress.ProgressIndicator.PercentCompleted = (int)((long)req.StartOfWindow * 100 / req.BundleSize);
            } while (req.StartOfWindow < req.BundleSize);
        }
 public void GetChunk_OffsetOutOfRange_EmptyByteArray()
 {
     using (var e = new PushStorageEnvForTest())
     {
         string text = "sample";
         var bundleHelper = new PushStorageManager(e.PullDataFolderPath, "randomHash");
         File.WriteAllText(bundleHelper.BundlePath, text);
         byte[] chunk = bundleHelper.GetChunk(10, 5); // offset is greater than string length
         Assert.That(chunk.Length, Is.EqualTo(0));
     }
 }