view tests/flagprocessorext.py @ 36534:5faeabb07cf5

debugcommands: support for triggering push protocol The mechanism for pushing to a remote is a bit more complicated than other commands. On SSH, we wait for a positive reply from the server before we start sending the bundle payload. This commit adds a mechanism to the "command" action in `hg debugwireproto` to trigger the "push protocol" and to specify a file whose contents should be submitted as the command payload. With this new feature, we implement a handful of tests for the "unbundle" command. We try to cover various server failures and hook/output scenarios so protocol behavior is as comprehensively tested as possible. Even with so much test output, we only cover bundle1 with Python hooks. There's still a lot of test coverage that needs to be implemented. But this is certainly a good start. Because there are so many new tests, we split these tests into their own test file. In order to make output deterministic, we need to disable the doublepipe primitive. We add an option to `hg debugwireproto` to do that. Because something in the bowels of the peer does a read of stderr, we still capture read I/O from stderr. So there is test coverage of what the server emits. The tests around I/O capture some wonkiness. For example, interleaved ui.write() and ui.write_err() calls are emitted in order. However, (presumably due to buffering), print() to sys.stdout and sys.stderr aren't in order. We currently only test bundle1 because bundle2 is substantially harder to test because it is more complicated (the server responds with a stream containing a bundle2 instead of a frame). Differential Revision: https://phab.mercurial-scm.org/D2471
author Gregory Szorc <gregory.szorc@gmail.com>
date Mon, 26 Feb 2018 18:01:13 -0800
parents 83246d6920f2
children 6c7a6b04b274
line wrap: on
line source

# coding=UTF-8

from __future__ import absolute_import

import base64
import zlib

from mercurial import (
    changegroup,
    exchange,
    extensions,
    filelog,
    revlog,
    util,
)

# Test only: These flags are defined here only in the context of testing the
# behavior of the flag processor. The canonical way to add flags is to get in
# touch with the community and make them known in revlog.
REVIDX_NOOP = (1 << 3)
REVIDX_BASE64 = (1 << 2)
REVIDX_GZIP = (1 << 1)
REVIDX_FAIL = 1

def validatehash(self, text):
    return True

def bypass(self, text):
    return False

def noopdonothing(self, text):
    return (text, True)

def b64encode(self, text):
    return (base64.b64encode(text), False)

def b64decode(self, text):
    return (base64.b64decode(text), True)

def gzipcompress(self, text):
    return (zlib.compress(text), False)

def gzipdecompress(self, text):
    return (zlib.decompress(text), True)

def supportedoutgoingversions(orig, repo):
    versions = orig(repo)
    versions.discard(b'01')
    versions.discard(b'02')
    versions.add(b'03')
    return versions

def allsupportedversions(orig, ui):
    versions = orig(ui)
    versions.add(b'03')
    return versions

def noopaddrevision(orig, self, text, transaction, link, p1, p2,
                    cachedelta=None, node=None,
                    flags=revlog.REVIDX_DEFAULT_FLAGS):
    if b'[NOOP]' in text:
        flags |= REVIDX_NOOP
    return orig(self, text, transaction, link, p1, p2, cachedelta=cachedelta,
                node=node, flags=flags)

def b64addrevision(orig, self, text, transaction, link, p1, p2,
                   cachedelta=None, node=None,
                   flags=revlog.REVIDX_DEFAULT_FLAGS):
    if b'[BASE64]' in text:
        flags |= REVIDX_BASE64
    return orig(self, text, transaction, link, p1, p2, cachedelta=cachedelta,
                node=node, flags=flags)

def gzipaddrevision(orig, self, text, transaction, link, p1, p2,
                    cachedelta=None, node=None,
                    flags=revlog.REVIDX_DEFAULT_FLAGS):
    if b'[GZIP]' in text:
        flags |= REVIDX_GZIP
    return orig(self, text, transaction, link, p1, p2, cachedelta=cachedelta,
                node=node, flags=flags)

def failaddrevision(orig, self, text, transaction, link, p1, p2,
                    cachedelta=None, node=None,
                    flags=revlog.REVIDX_DEFAULT_FLAGS):
    # This addrevision wrapper is meant to add a flag we will not have
    # transforms registered for, ensuring we handle this error case.
    if b'[FAIL]' in text:
        flags |= REVIDX_FAIL
    return orig(self, text, transaction, link, p1, p2, cachedelta=cachedelta,
                node=node, flags=flags)

def extsetup(ui):
    # Enable changegroup3 for flags to be sent over the wire
    wrapfunction = extensions.wrapfunction
    wrapfunction(changegroup,
                 'supportedoutgoingversions',
                 supportedoutgoingversions)
    wrapfunction(changegroup,
                 'allsupportedversions',
                 allsupportedversions)

    # Teach revlog about our test flags
    flags = [REVIDX_NOOP, REVIDX_BASE64, REVIDX_GZIP, REVIDX_FAIL]
    revlog.REVIDX_KNOWN_FLAGS |= util.bitsfrom(flags)
    revlog.REVIDX_FLAGS_ORDER.extend(flags)

    # Teach exchange to use changegroup 3
    for k in exchange._bundlespeccgversions.keys():
        exchange._bundlespeccgversions[k] = b'03'

    # Add wrappers for addrevision, responsible to set flags depending on the
    # revision data contents.
    wrapfunction(filelog.filelog, 'addrevision', noopaddrevision)
    wrapfunction(filelog.filelog, 'addrevision', b64addrevision)
    wrapfunction(filelog.filelog, 'addrevision', gzipaddrevision)
    wrapfunction(filelog.filelog, 'addrevision', failaddrevision)

    # Register flag processors for each extension
    revlog.addflagprocessor(
        REVIDX_NOOP,
        (
            noopdonothing,
            noopdonothing,
            validatehash,
        )
    )
    revlog.addflagprocessor(
        REVIDX_BASE64,
        (
            b64decode,
            b64encode,
            bypass,
        ),
    )
    revlog.addflagprocessor(
        REVIDX_GZIP,
        (
            gzipdecompress,
            gzipcompress,
            bypass
        )
    )