]> git.proxmox.com Git - libgit2.git/commitdiff
Cleanup Clar to make it SIMPLER
authorVicent Marti <tanoku@gmail.com>
Tue, 18 Dec 2012 23:12:26 +0000 (00:12 +0100)
committerVicent Marti <tanoku@gmail.com>
Wed, 2 Jan 2013 01:05:11 +0000 (02:05 +0100)
19 files changed:
.gitignore
.gitmodules [new file with mode: 0644]
CMakeLists.txt
tests-clar/clar [changed from file to submodule]
tests-clar/clar_helpers.c
tests-clar/clar_libgit2.h
tests-clar/clone/network.c [deleted file]
tests-clar/fetchhead/network.c [deleted file]
tests-clar/main.c [new file with mode: 0644]
tests-clar/network/fetch.c [deleted file]
tests-clar/network/push.c [deleted file]
tests-clar/network/push_util.c [deleted file]
tests-clar/network/push_util.h [deleted file]
tests-clar/online/clone.c [new file with mode: 0644]
tests-clar/online/fetch.c [new file with mode: 0644]
tests-clar/online/fetchhead.c [new file with mode: 0644]
tests-clar/online/push.c [new file with mode: 0644]
tests-clar/online/push_util.c [new file with mode: 0644]
tests-clar/online/push_util.h [new file with mode: 0644]

index 5441aa59702da266c51876264be7253e69d11750..b81a1b13cc87ce8fd8cbf186e01367cff3b70da4 100644 (file)
@@ -1,6 +1,5 @@
-/tests-clar/clar.h
-/tests-clar/clar_main.c
-/tests-clar/clar_main.c.rule
+/tests-clar/clar.suite
+/tests-clar/.clarcache
 /apidocs
 /trash-*.exe
 /libgit2.pc
@@ -28,4 +27,4 @@ CMake*
 .DS_Store
 *~
 tags
-mkmf.log
\ No newline at end of file
+mkmf.log
diff --git a/.gitmodules b/.gitmodules
new file mode 100644 (file)
index 0000000..5d7eda9
--- /dev/null
@@ -0,0 +1,3 @@
+[submodule "tests-clar/clar"]
+       path = tests-clar/clar
+       url = https://github.com/vmg/clar.git
index 696d6a088890875b1010a2ea58e51facd50b09ee..fe9f5afa11c07de96a90ed1c93f568b2b2500f63 100644 (file)
@@ -233,7 +233,6 @@ INSTALL(FILES include/git2.h DESTINATION ${INCLUDE_INSTALL_DIR} )
 
 # Tests
 IF (BUILD_CLAR)
-
        FIND_PACKAGE(PythonInterp REQUIRED)
 
        SET(CLAR_FIXTURES "${CMAKE_CURRENT_SOURCE_DIR}/tests-clar/resources/")
@@ -243,23 +242,25 @@ IF (BUILD_CLAR)
        ADD_DEFINITIONS(-DCLAR_RESOURCES=\"${TEST_RESOURCES}\")
 
        INCLUDE_DIRECTORIES(${CLAR_PATH})
-       FILE(GLOB_RECURSE SRC_TEST ${CLAR_PATH}/*/*.c ${CLAR_PATH}/clar_helpers.c ${CLAR_PATH}/testlib.c)
+       FILE(GLOB_RECURSE SRC_TEST ${CLAR_PATH}/*/*.c ${CLAR_PATH}/clar_helpers.c)
+       SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-missing-prototypes")
 
        ADD_CUSTOM_COMMAND(
-               OUTPUT ${CLAR_PATH}/clar_main.c ${CLAR_PATH}/clar.h
-               COMMAND ${PYTHON_EXECUTABLE} clar .
-               DEPENDS ${CLAR_PATH}/clar ${SRC_TEST}
+               OUTPUT ${CLAR_PATH}/clar.suite
+               COMMAND ${PYTHON_EXECUTABLE} clar/generate.py .
+               DEPENDS ${CLAR_PATH}/clar.suite ${SRC_TEST}
                WORKING_DIRECTORY ${CLAR_PATH}
        )
-       ADD_EXECUTABLE(libgit2_clar ${SRC_GIT2} ${SRC_OS} ${CLAR_PATH}/clar_main.c ${SRC_TEST} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX} ${SRC_SHA1})
+
+       ADD_EXECUTABLE(libgit2_clar ${SRC_GIT2} ${SRC_OS} ${CLAR_PATH}/main.c ${SRC_TEST} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX} ${SRC_SHA1})
        TARGET_LINK_LIBRARIES(libgit2_clar ${SSL_LIBRARIES})
        TARGET_OS_LIBRARIES(libgit2_clar)
        MSVC_SPLIT_SOURCES(libgit2_clar)
 
-        IF (MSVC_IDE)
-           # Precompiled headers
-           SET_TARGET_PROPERTIES(libgit2_clar PROPERTIES COMPILE_FLAGS "/Yuprecompiled.h /FIprecompiled.h")
-        ENDIF ()
+       IF (MSVC_IDE)
+               # Precompiled headers
+               SET_TARGET_PROPERTIES(libgit2_clar PROPERTIES COMPILE_FLAGS "/Yuprecompiled.h /FIprecompiled.h")
+       ENDIF ()
 
        ENABLE_TESTING()
        ADD_TEST(libgit2_clar libgit2_clar -iall)
deleted file mode 100755 (executable)
index 7634b2c76b8f46932665dd783491528d9455a047..0000000000000000000000000000000000000000
+++ /dev/null
@@ -1,346 +0,0 @@
-#!/usr/bin/env python
-
-from __future__ import with_statement
-from string import Template
-import re, fnmatch, os, codecs
-
-VERSION = "0.10.0"
-
-TEST_FUNC_REGEX = r"^(void\s+(test_%s__(\w+))\(\s*void\s*\))\s*\{"
-
-EVENT_CB_REGEX = re.compile(
-    r"^(void\s+clar_on_(\w+)\(\s*void\s*\))\s*\{",
-    re.MULTILINE)
-
-SKIP_COMMENTS_REGEX = re.compile(
-    r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"',
-    re.DOTALL | re.MULTILINE)
-
-CATEGORY_REGEX = re.compile(r"CL_IN_CATEGORY\(\s*\"([^\"]+)\"\s*\)")
-
-CLAR_HEADER = """
-/*
- * Clar v%s
- *
- * This is an autogenerated file. Do not modify.
- * To add new unit tests or suites, regenerate the whole
- * file with `./clar`
- */
-""" % VERSION
-
-CLAR_EVENTS = [
-    'init',
-    'shutdown',
-    'test',
-    'suite'
-]
-
-def main():
-    from optparse import OptionParser
-
-    parser = OptionParser()
-
-    parser.add_option('-c', '--clar-path', dest='clar_path')
-    parser.add_option('-v', '--report-to', dest='print_mode', default='default')
-
-    options, args = parser.parse_args()
-
-    for folder in args or ['.']:
-        builder = ClarTestBuilder(folder,
-            clar_path = options.clar_path,
-            print_mode = options.print_mode)
-
-        builder.render()
-
-
-class ClarTestBuilder:
-    def __init__(self, path, clar_path = None, print_mode = 'default'):
-        self.declarations = []
-        self.suite_names = []
-        self.callback_data = {}
-        self.suite_data = {}
-        self.category_data = {}
-        self.event_callbacks = []
-
-        self.clar_path = os.path.abspath(clar_path) if clar_path else None
-
-        self.path = os.path.abspath(path)
-        self.modules = [
-            "clar_sandbox.c",
-            "clar_fixtures.c",
-            "clar_fs.c",
-            "clar_categorize.c",
-        ]
-
-        self.modules.append("clar_print_%s.c" % print_mode)
-
-        print("Loading test suites...")
-
-        for root, dirs, files in os.walk(self.path):
-            module_root = root[len(self.path):]
-            module_root = [c for c in module_root.split(os.sep) if c]
-
-            tests_in_module = fnmatch.filter(files, "*.c")
-
-            for test_file in tests_in_module:
-                full_path = os.path.join(root, test_file)
-                test_name = "_".join(module_root + [test_file[:-2]])
-
-                with codecs.open(full_path, 'r', 'utf-8') as f:
-                    self._process_test_file(test_name, f.read())
-
-        if not self.suite_data:
-            raise RuntimeError(
-                'No tests found under "%s"' % path)
-
-    def render(self):
-        main_file = os.path.join(self.path, 'clar_main.c')
-        with open(main_file, "w") as out:
-            out.write(self._render_main())
-
-        header_file = os.path.join(self.path, 'clar.h')
-        with open(header_file, "w") as out:
-            out.write(self._render_header())
-
-        print ('Written Clar suite to "%s"' % self.path)
-
-    #####################################################
-    # Internal methods
-    #####################################################
-
-    def _render_cb(self, cb):
-        return '{"%s", &%s}' % (cb['short_name'], cb['symbol'])
-
-    def _render_suite(self, suite, index):
-        template = Template(
-r"""
-    {
-        ${suite_index},
-        "${clean_name}",
-        ${initialize},
-        ${cleanup},
-        ${categories},
-        ${cb_ptr}, ${cb_count}
-    }
-""")
-
-        callbacks = {}
-        for cb in ['initialize', 'cleanup']:
-            callbacks[cb] = (self._render_cb(suite[cb])
-                if suite[cb] else "{NULL, NULL}")
-
-        if len(self.category_data[suite['name']]) > 0:
-            cats = "_clar_cat_%s" % suite['name']
-        else:
-            cats = "NULL"
-
-        return template.substitute(
-            suite_index = index,
-            clean_name = suite['name'].replace("_", "::"),
-            initialize = callbacks['initialize'],
-            cleanup = callbacks['cleanup'],
-            categories = cats,
-            cb_ptr = "_clar_cb_%s" % suite['name'],
-            cb_count = suite['cb_count']
-        ).strip()
-
-    def _render_callbacks(self, suite_name, callbacks):
-        template = Template(
-r"""
-static const struct clar_func _clar_cb_${suite_name}[] = {
-    ${callbacks}
-};
-""")
-        callbacks = [
-            self._render_cb(cb)
-            for cb in callbacks
-            if cb['short_name'] not in ('initialize', 'cleanup')
-        ]
-
-        return template.substitute(
-            suite_name = suite_name,
-            callbacks = ",\n\t".join(callbacks)
-        ).strip()
-
-    def _render_categories(self, suite_name, categories):
-        template = Template(
-r"""
-static const char *_clar_cat_${suite_name}[] = { "${categories}", NULL };
-""")
-        if len(categories) > 0:
-            return template.substitute(
-                suite_name = suite_name,
-                categories = '","'.join(categories)
-                ).strip()
-        else:
-            return ""
-
-    def _render_event_overrides(self):
-        overrides = []
-        for event in CLAR_EVENTS:
-            if event in self.event_callbacks:
-                continue
-
-            overrides.append(
-                "#define clar_on_%s() /* nop */" % event
-            )
-
-        return '\n'.join(overrides)
-
-    def _render_header(self):
-        template = Template(self._load_file('clar.h'))
-
-        declarations = "\n".join(
-            "extern %s;" % decl
-            for decl in sorted(self.declarations)
-        )
-
-        return template.substitute(
-            extern_declarations = declarations,
-        )
-
-    def _render_main(self):
-        template = Template(self._load_file('clar.c'))
-        suite_names = sorted(self.suite_names)
-
-        suite_data = [
-            self._render_suite(self.suite_data[s], i)
-            for i, s in enumerate(suite_names)
-        ]
-
-        callbacks = [
-            self._render_callbacks(s, self.callback_data[s])
-            for s in suite_names
-        ]
-
-        callback_count = sum(
-            len(cbs) for cbs in self.callback_data.values()
-        )
-
-        categories = [
-            self._render_categories(s, self.category_data[s])
-            for s in suite_names
-        ]
-
-        return template.substitute(
-            clar_modules = self._get_modules(),
-            clar_callbacks = "\n".join(callbacks),
-            clar_categories = "".join(categories),
-            clar_suites = ",\n\t".join(suite_data),
-            clar_suite_count = len(suite_data),
-            clar_callback_count = callback_count,
-            clar_event_overrides = self._render_event_overrides(),
-        )
-
-    def _load_file(self, filename):
-        if self.clar_path:
-            filename = os.path.join(self.clar_path, filename)
-            with open(filename) as cfile:
-                return cfile.read()
-
-        else:
-            import zlib, base64, sys
-            content = CLAR_FILES[filename]
-
-            if sys.version_info >= (3, 0):
-                content = bytearray(content, 'utf_8')
-                content = base64.b64decode(content)
-                content = zlib.decompress(content)
-                return str(content, 'utf-8')
-            else:
-                content = base64.b64decode(content)
-                return zlib.decompress(content)
-
-    def _get_modules(self):
-        return "\n".join(self._load_file(f) for f in self.modules)
-
-    def _skip_comments(self, text):
-        def _replacer(match):
-            s = match.group(0)
-            return "" if s.startswith('/') else s
-
-        return re.sub(SKIP_COMMENTS_REGEX, _replacer, text)
-
-    def _process_test_file(self, suite_name, contents):
-        contents = self._skip_comments(contents)
-
-        self._process_events(contents)
-        self._process_declarations(suite_name, contents)
-        self._process_categories(suite_name, contents)
-
-    def _process_events(self, contents):
-        for (decl, event) in EVENT_CB_REGEX.findall(contents):
-            if event not in CLAR_EVENTS:
-                continue
-
-            self.declarations.append(decl)
-            self.event_callbacks.append(event)
-
-    def _process_declarations(self, suite_name, contents):
-        callbacks = []
-        initialize = cleanup = None
-
-        regex_string = TEST_FUNC_REGEX % suite_name
-        regex = re.compile(regex_string, re.MULTILINE)
-
-        for (declaration, symbol, short_name) in regex.findall(contents):
-            data = {
-                "short_name" : short_name,
-                "declaration" : declaration,
-                "symbol" : symbol
-            }
-
-            if short_name == 'initialize':
-                initialize = data
-            elif short_name == 'cleanup':
-                cleanup = data
-            else:
-                callbacks.append(data)
-
-        if not callbacks:
-            return
-
-        tests_in_suite = len(callbacks)
-
-        suite = {
-            "name" : suite_name,
-            "initialize" : initialize,
-            "cleanup" : cleanup,
-            "cb_count" : tests_in_suite
-        }
-
-        if initialize:
-            self.declarations.append(initialize['declaration'])
-
-        if cleanup:
-            self.declarations.append(cleanup['declaration'])
-
-        self.declarations += [
-            callback['declaration']
-            for callback in callbacks
-        ]
-
-        callbacks.sort(key=lambda x: x['short_name'])
-        self.callback_data[suite_name] = callbacks
-        self.suite_data[suite_name] = suite
-        self.suite_names.append(suite_name)
-
-        print("  %s (%d tests)" % (suite_name, tests_in_suite))
-
-    def _process_categories(self, suite_name, contents):
-        self.category_data[suite_name] = [
-            cat for cat in CATEGORY_REGEX.findall(contents) ]
-
-
-CLAR_FILES = {
-"clar.c" : r"""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""",
-"clar_print_default.c" : r"""eJyFU8Fu2zAMPdtfwQUwIgVuenew9tZTsMuwU1sYqiW3AhzJkOhswNB/n0Q5rRws6Ukmxff4RD6XHgXqDo5WS+gG4drRaYOtNhpZ+ABUHtvOTgZriLGfNKpTorPGI3RvwsEmXRhxUJ6Xf8uCRUr+Cd+VBVH3bLW3QioJlUxsvoHKP5lVDbEjX3TIWTOGnygcKhlAIftelhde4d8mlPa3+folMaGcsy4lLr0gpTLkRy4D78pPoU8maSxIlVOjddhSrWdXpVMN6TbT4TRpj27qMJVRAWzoILmnlhAGy+FB6GFyqqG5Bgqeq6p801QeWOU5PIagks/weIPhiOVlURDrzR09NIvjLGK4Mhak8p3TI2q7gPR6yBGDNmF90+FFuTOeObvQBScjzHVpqAf/SlW6BzZfZM3h23f48Wu/54H+Ek9Wzpfbue4fa6JSlts8SQ9+TJ7JXpISfZi7kuf+iYDdMkOYzNJVF/QmNNzD+mENDay36y/00YbY///D3ObaSPWHVN1uwFg7wuZ2aWeqOLN4kn2tv3gJhl70D9uqYbvdUrOjaAcdroR7HXcU+vjnshjXkBZbHPt5Bh5lWBjla4LwhFFGsjl8L/8BsUiTTQ==""",
-"clar_print_tap.c" : r"""eJyNVE1vnDAQPcOvmGWFBAiQot6yaqr2HFU9tLdKyAGzscLayDbbVlX+e8cDJPbuJtsTzPObmTcfdmwss6KFoxIdtAPTzaiFtI2Qwmb4A5Yb27RqkrYEZ5tJWL4CrZLGQvvINBTzgWQHbvL4bxxlLmT+6r5bIY94gq08ktBnyffP3+DItRFKws2HnzLJd/FzHL8h2TxOtlO/5HXZDuBaKz0D/yM3xDznXRxHoodsEwSMXmrYwsiM4R2wYYC0I2GZybGY0hOJhUV8MDxw7JkY0BGd2EHJ/am3l7BEvyiMtoa5qeu0O8/2dhspLPVQTod1xMbqqbUzjQhQ0MdrHbJdL9a8AFVVzSPzMJy5YXsOt5Ca1yKqu7mWg9mHdMNx/ML+uaVenEWj0QCcRSM8pLri4QLV4SGzx6ZfYjo8ZA5CrszOZzq8wXY8cJ2v67Ecddy0WozWbfTmI3z9cX/vLwuARzgV4B3lYafrur52OZSk1fEvLO2Du4bzhZhNUj0D8/rRhNdUqXFLWC3CUPiyop8gkcqCekqwGQl+3Jkf8MXEdHFE8kmc5qPSy86Z7EoFNNbs8pvj33IhO/470L2FoihQNWTbtMudQY313X3X92WwB5QcyMC9Ld0QKOeRNYPAI6b3445MjIQOzi5hWfF+UWbwxZrwRUq+YCMBfzdAO348JVAKFyKfY3LZZYv5HP8D5Mbj9w==""",
-"clar_sandbox.c" : r"""eJydVWtP4kAU/dz+iism0gpKfWQ3G9YPm+gasioEMJgomdR2KhPplMwM7KLxv++dTqEP0DVrTKjcO+eec+6cKpWvWADBxBdAgqkvyMxXk/tT79uXcdu2pSkzrmwmycKfspCoeJY2OUHCpTJH9/UXrv1qW4PhjyEZglR42mIROBrC0eUm7Enlws4ZeK5tWYKqueDgrfp2BqQzOO/08cChVCROQupW+7Jnxw8CKmWGOiLdXy6cadi2/VbiHDFe5JsyfZxHERVNkOyFEgVTyp8M9V0W8ZBGQEadm5Nj28pwjMqse4EGBcmcKziD03alx+BTvkCjhLwfYw8aYtWG1z3UVWuCfko/Lszn7eCi3+t3f3auLmo2WG8oEaxsEtN6o0SAwxDHawOD7/n4NjQazE3hK7Ox+YkqfHDWRNgYjbGMyfilNlWfUozPqZ6SVjbXq1vNCJQpeDBbOivvsNRcOaehC0uyrDcbf22rtQ+dCNSE6m4mEh5TtC1MqOR19NNfgs+XasL4UxOUWIJKYC4ptHA+7Lfsd0jVdL2W8arSMsUSswIxJLVLp5Ia6EuqhjSe9TSocz7q9s9dc6wJBq5y+XYpD1lkdA0nTIJcSkXjtaApe6YooKRFiw/mQqTCmaCBSrD4gbjDd5UdfiRr9efBUTEAi4SFkEZ6zqXPw8fkj6O/S2OqCRTy7o11gOoPXj1XjVcDI1FMRDBBFcgSaRYMiSQRcQGsmkL0k01DklEwStc8CrdXF4jy2TRNTi3F09bcpT81nbZ1ZFcvjXLAcw4m3klUpOVigIpvHu2WbSEYTkO/8aEsoqr+FXD1PBExLu2FpnT1onvdQecOMKm/fRGCnPpyQmW65EKUrY0oaxF5iKv7YNk+HtJ9WFalBPVWfR219SIqGFrZARyN9RsX+82gcr3RyMH0PVpdu7wLGpppM1/ONmdxDDZllgF6xjgNHUKuOzeXo5NjQtyMXPyMkZmVjqLMm9urq4296P74Wd+34la9r5638S9EH8BkF0enKytPJfKf92ML7v8QWb1i8NQn5a5XmOe6HKEU4fMhhr29banbngCNYpJdJLrVixK9v7GvgW8=""",
-"clar_fixtures.c" : r"""eJyFUV1LwzAUfW5+xZU9rLUVJ4ggZQ9DFAUfZEwQSglZmrBAl5Qkk6n43236tWbKfMvNOfecc+81llhBgSppLNAN0XCOuNjbnWa4InYTjpE1MSzxuD1Vki2L0BcKTKfn0EYgu57d3uRpjYhPhi1opSwumUwRCvo3zMFYXT9C5xA5stWSVh9hI5FAa+wUFG//osgJCA5tmQ1SF3CVw9kcppfTCAWBj8ZxDg3UN4/zZ7MaHBrHSBw7vpcJ4mGS5Ijtai9qnannNqk1q7myXU+KvhGaCF4wDnfPiyV+eHpbvS7v8cti9YjGq6Yl7lzCkxfo1L0j/lJOwOtrUrwrUcDBBRsii7Xan3bjBlNVL2WUzuMkgGlJdLuIP21oyYjcVf/a6G3ozXTQPRqmsZkwWQiOfgAVGffP""",
-"clar_fs.c" : r"""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""",
-"clar_categorize.c" : r"""eJydVV1v0zAUfU5+xV3RKudj0/ZcOqkqAyGVIU3bA9omy01dsJQlJXYRY+K/c32ddE6WtMBLGzv365x77s2blVyrQsJ8Mbvm89nN5YfP11/4u8v3s9vFDYzwrdjmZhSG5mkj8QTaVNvMwHMYZGWhDWTfRAVxXIhHqSdhoAq8KreFqZ9FnpfZJPwNWS4qngkjv5bVE8+VRotQG2FU1vMOuH+nfkkuC7HM5erFiTK1HFVBvqwnXGx/U/BLRoMofHZVKqx2XVbAFEzhbAIK3oL1OLlwWCBJVBQGgVoDQwIyoWX2uGEYIq3tCP+deohgihGsbVBJs60KOMfY9eMZErGr/0epVh0AYrXipvwvEFhYPxm1D9rZolwhSCJ5eBDhYlojoY5FtsGBdwHJFM6x/uaS8CJZrKWCqJJkzSx+RgjjyHNI/RwQg8bOlutWjCjCJMiSn+fOKzRJHjAt4jnApdML65BF74ixYebHQ9ojGl2Ev0rOESazELkBvVVGckLQdLCeJPKk1xDTn6b6aj+rzBbFhLxbfg22d4gja94Vt1cOXiSJ1QbZYEi0cnWgnE8bFnC4LXoYj4lHOji3/lJImT5Ldsz4p9nHKz6fd9iiUSKMTjGiYcgdl8RHPUIn4M2fSJeHOrGpMHHTwJYaNhVuvp/+CgiD77qsDGuEO6SDU6dlxD5o4RqNFn0KT1/j723S/ui7pUQQ12x0rI/1fTFKwSFLh/13y6rboM4K0U6XHfqGdOvGyteqzsRu1xztR2OB/KOmKSplxtWwk+nLlhv4OljJ7hnxAzNkITUD4qedAKGFoylc3S4WE7AnNyDDu3lPGRQt6nxH2h+SPx6FmFM=""",
-"clar.h" : r"""eJy9VV1vmzAUfS6/wg17gAi1TR+7tlIUJWukqJvaVNueLGNMsQaG2mbNNO2/7xpICF/Juoe+JOZe33uOOecam4ciYCHCeLaaPuD1/HGN7zC2bAhywTpxy+aCxnnA0LXSQcz9s+jWsn6mPEA0JhJjohST2rFOuNCIpiLgmqfCs05grSASEYnGIY+ZV26JAaWVZVKmshULmKKSZ1UvU6iiNI8DTPxUavdjDwfMXnISY+Xs9/GGH6BpJwCNh/pyxxT0FfV12bbBimlMY0ZEnjlFzBlXj275PHY9VC7SjLzkrKaAQ9UoNW1tHhr5CpEWy2/rp4c5/jJd31n7HEwp3+hcMqepQhHDgiQNlCqsiAj8dPOWki27AyU2A0uE1s5gsxVe3uPZdD3/9PnhuwML17LOx2MLjdG0eN8gOUoIlalCr1xHiG2ymFOuUeETlDClyDOD/ee7pkApyZXGGSiGHSiQHjIOcpsmLTIuur1BFx44fbFczTE2q9XyvliNFrmgBQFK4hiFBHwbXKERsuueHpq4HWCz8zjw9SDufJMxqlmAwgYBnRYcjjCobHoU/nT43IAv4b0aYK6QSDXSMmd9uFutZhGjP/5DJ2rq3kmoC7eL/M5K9VF4B6Eujg2VSP9xnCpKfRN2/7Ra9Y9Cq2j/nXeKqqPvKppuLrcPm+7YOWq71QhdC3ZI1V5plx08S7GlXdF7kkUqqTERdIPL8vyVSMHFc5t9QaDHJ0PuWDO4hsthOBv1XxYV0lu6fi1LUJBL86cNCNswmhtXXY16PLf+lcHhSMt57dO1Pttq4qnLJqVdDpKu50Da2zHcERw96oJXwlVCNI2KYVAT+IU5MsvLgQtz912feLwfmDuQBGDeC2zzGoQfBvEdf+L5QyCnp5B2PfPXD+TXQP5hoMzJJl52uTdJDkRcdHODHAjvSWRUTJiO0gD0M7SIkaoU6cNvttFMCryf+WNtP+Z/AaQwXp0="""
-}
-if __name__ == '__main__':
-    main()
new file mode 160000 (submodule)
index 0000000000000000000000000000000000000000..161e8cd0f7642385d22153052fbcf770517de6c1
--- /dev/null
@@ -0,0 +1 @@
+Subproject commit 161e8cd0f7642385d22153052fbcf770517de6c1
index b718d43051496133d41d876435b97ebe32d1c99d..ce3ec4af44813e638ca9414c5f530eb66a6bb1ec 100644 (file)
@@ -2,17 +2,6 @@
 #include "posix.h"
 #include "path.h"
 
-void clar_on_init(void)
-{
-       git_threads_init();
-}
-
-void clar_on_shutdown(void)
-{
-       giterr_clear();
-       git_threads_shutdown();
-}
-
 void cl_git_mkfile(const char *filename, const char *content)
 {
        int fd;
index 91a5426547883e8d9b4563eebef10b6325b102cf..15b5daaf375bdd0db969f0de070296110421ef19 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef __CLAR_LIBGIT2__
 #define __CLAR_LIBGIT2__
 
-#include "clar.h"
+#include "clar/clar.h"
 #include <git2.h>
 #include "common.h"
 
diff --git a/tests-clar/clone/network.c b/tests-clar/clone/network.c
deleted file mode 100644 (file)
index d300966..0000000
+++ /dev/null
@@ -1,154 +0,0 @@
-#include "clar_libgit2.h"
-
-#include "git2/clone.h"
-#include "repository.h"
-
-CL_IN_CATEGORY("network")
-
-#define LIVE_REPO_URL "http://github.com/libgit2/TestGitRepository"
-#define LIVE_EMPTYREPO_URL "http://github.com/libgit2/TestEmptyRepository"
-
-static git_repository *g_repo;
-static git_remote *g_origin;
-static git_clone_options g_options;
-
-void test_clone_network__initialize(void)
-{
-       g_repo = NULL;
-
-       memset(&g_options, 0, sizeof(git_clone_options));
-       g_options.version = GIT_CLONE_OPTIONS_VERSION;
-       cl_git_pass(git_remote_new(&g_origin, NULL, "origin", LIVE_REPO_URL, GIT_REMOTE_DEFAULT_FETCH));
-}
-
-void test_clone_network__cleanup(void)
-{
-       git_remote_free(g_origin);
-}
-
-static void cleanup_repository(void *path)
-{
-       if (g_repo) {
-               git_repository_free(g_repo);
-               g_repo = NULL;
-       }
-       cl_fixture_cleanup((const char *)path);
-}
-
-
-void test_clone_network__network_full(void)
-{
-       git_remote *origin;
-
-       cl_set_cleanup(&cleanup_repository, "./foo");
-
-       cl_git_pass(git_clone(&g_repo, g_origin, "./foo", &g_options));
-       cl_assert(!git_repository_is_bare(g_repo));
-       cl_git_pass(git_remote_load(&origin, g_repo, "origin"));
-
-       git_remote_free(origin);
-}
-
-
-void test_clone_network__network_bare(void)
-{
-       git_remote *origin;
-
-       cl_set_cleanup(&cleanup_repository, "./foo");
-       g_options.bare = true;
-
-       cl_git_pass(git_clone(&g_repo, g_origin, "./foo", &g_options));
-       cl_assert(git_repository_is_bare(g_repo));
-       cl_git_pass(git_remote_load(&origin, g_repo, "origin"));
-
-       git_remote_free(origin);
-}
-
-void test_clone_network__cope_with_already_existing_directory(void)
-{
-       cl_set_cleanup(&cleanup_repository, "./foo");
-
-       p_mkdir("./foo", GIT_DIR_MODE);
-       cl_git_pass(git_clone(&g_repo, g_origin, "./foo", &g_options));
-}
-
-void test_clone_network__empty_repository(void)
-{
-       git_reference *head;
-
-       cl_set_cleanup(&cleanup_repository, "./foo");
-
-       git_remote_free(g_origin);
-       cl_git_pass(git_remote_new(&g_origin, NULL, "origin", LIVE_EMPTYREPO_URL, GIT_REMOTE_DEFAULT_FETCH));
-
-       cl_git_pass(git_clone(&g_repo, g_origin, "./foo", &g_options));
-
-       cl_assert_equal_i(true, git_repository_is_empty(g_repo));
-       cl_assert_equal_i(true, git_repository_head_orphan(g_repo));
-
-       cl_git_pass(git_reference_lookup(&head, g_repo, GIT_HEAD_FILE));
-       cl_assert_equal_i(GIT_REF_SYMBOLIC, git_reference_type(head));
-       cl_assert_equal_s("refs/heads/master", git_reference_symbolic_target(head));
-
-       git_reference_free(head);
-}
-
-void test_clone_network__can_prevent_the_checkout_of_a_standard_repo(void)
-{
-       git_buf path = GIT_BUF_INIT;
-       cl_set_cleanup(&cleanup_repository, "./foo");
-
-       cl_git_pass(git_clone(&g_repo, g_origin, "./foo", &g_options));
-
-       cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "master.txt"));
-       cl_assert_equal_i(false, git_path_isfile(git_buf_cstr(&path)));
-
-       git_buf_free(&path);
-}
-
-static void checkout_progress(const char *path, size_t cur, size_t tot, void *payload)
-{
-       bool *was_called = (bool*)payload;
-       GIT_UNUSED(path); GIT_UNUSED(cur); GIT_UNUSED(tot);
-       (*was_called) = true;
-}
-
-static void fetch_progress(const git_transfer_progress *stats, void *payload)
-{
-       bool *was_called = (bool*)payload;
-       GIT_UNUSED(stats);
-       (*was_called) = true;
-}
-
-void test_clone_network__can_checkout_a_cloned_repo(void)
-{
-       git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
-       git_buf path = GIT_BUF_INIT;
-       git_reference *head;
-       bool checkout_progress_cb_was_called = false,
-                 fetch_progress_cb_was_called = false;
-
-       opts.checkout_strategy = GIT_CHECKOUT_SAFE;
-       opts.progress_cb = &checkout_progress;
-       opts.progress_payload = &checkout_progress_cb_was_called;
-       g_options.checkout_opts = &opts;
-       g_options.fetch_progress_cb = &fetch_progress;
-       g_options.fetch_progress_payload = &fetch_progress_cb_was_called;
-
-       cl_set_cleanup(&cleanup_repository, "./foo");
-
-       cl_git_pass(git_clone(&g_repo, g_origin, "./foo", &g_options));
-
-       cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "master.txt"));
-       cl_assert_equal_i(true, git_path_isfile(git_buf_cstr(&path)));
-
-       cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD"));
-       cl_assert_equal_i(GIT_REF_SYMBOLIC, git_reference_type(head));
-       cl_assert_equal_s("refs/heads/master", git_reference_symbolic_target(head));
-
-       cl_assert_equal_i(true, checkout_progress_cb_was_called);
-       cl_assert_equal_i(true, fetch_progress_cb_was_called);
-
-       git_reference_free(head);
-       git_buf_free(&path);
-}
diff --git a/tests-clar/fetchhead/network.c b/tests-clar/fetchhead/network.c
deleted file mode 100644 (file)
index dc223e2..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-#include "clar_libgit2.h"
-
-#include "repository.h"
-#include "fetchhead.h"
-#include "fetchhead_data.h"
-#include "git2/clone.h"
-
-CL_IN_CATEGORY("network")
-
-#define LIVE_REPO_URL "git://github.com/libgit2/TestGitRepository"
-
-static git_repository *g_repo;
-static git_remote *g_origin;
-static git_clone_options g_options;
-
-void test_fetchhead_network__initialize(void)
-{
-       g_repo = NULL;
-
-       memset(&g_options, 0, sizeof(git_clone_options));
-       g_options.version = GIT_CLONE_OPTIONS_VERSION;
-       cl_git_pass(git_remote_new(&g_origin, NULL, "origin", LIVE_REPO_URL, GIT_REMOTE_DEFAULT_FETCH));
-}
-
-void test_fetchhead_network__cleanup(void)
-{
-       git_remote_free(g_origin);
-}
-
-static void cleanup_repository(void *path)
-{
-       if (g_repo) {
-               git_repository_free(g_repo);
-               g_repo = NULL;
-       }
-
-       cl_fixture_cleanup((const char *)path);
-}
-
-
-static void fetchhead_test_clone(void)
-{
-       cl_set_cleanup(&cleanup_repository, "./foo");
-
-       cl_git_pass(git_clone(&g_repo, g_origin, "./foo", &g_options));
-}
-
-static void fetchhead_test_fetch(const char *fetchspec, const char *expected_fetchhead)
-{
-       git_remote *remote;
-       git_buf fetchhead_buf = GIT_BUF_INIT;
-       int equals = 0;
-
-       cl_git_pass(git_remote_load(&remote, g_repo, "origin"));
-       git_remote_set_autotag(remote, GIT_REMOTE_DOWNLOAD_TAGS_AUTO);
-
-       if(fetchspec != NULL)
-               git_remote_set_fetchspec(remote, fetchspec);
-
-       cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH));
-       cl_git_pass(git_remote_download(remote, NULL, NULL));
-       cl_git_pass(git_remote_update_tips(remote));
-       git_remote_disconnect(remote);
-       git_remote_free(remote);
-
-       cl_git_pass(git_futils_readbuffer(&fetchhead_buf, "./foo/.git/FETCH_HEAD"));
-
-       equals = (strcmp(fetchhead_buf.ptr, expected_fetchhead) == 0);
-
-       git_buf_free(&fetchhead_buf);
-
-       cl_assert(equals);
-}
-
-void test_fetchhead_network__wildcard_spec(void)
-{
-       fetchhead_test_clone();
-       fetchhead_test_fetch(NULL, FETCH_HEAD_WILDCARD_DATA);
-}
-
-void test_fetchhead_network__explicit_spec(void)
-{
-       fetchhead_test_clone();
-       fetchhead_test_fetch("refs/heads/first-merge:refs/remotes/origin/first-merge", FETCH_HEAD_EXPLICIT_DATA);
-}
-
-void test_fetchhead_network__no_merges(void)
-{
-       git_config *config;
-
-       fetchhead_test_clone();
-
-       cl_git_pass(git_repository_config(&config, g_repo));
-       cl_git_pass(git_config_set_string(config, "branch.master.remote", NULL));
-       cl_git_pass(git_config_set_string(config, "branch.master.merge", NULL));
-    git_config_free(config);
-
-       fetchhead_test_fetch(NULL, FETCH_HEAD_NO_MERGE_DATA);
-}
diff --git a/tests-clar/main.c b/tests-clar/main.c
new file mode 100644 (file)
index 0000000..e9b3b64
--- /dev/null
@@ -0,0 +1,16 @@
+#include "clar_libgit2.h"
+
+int main(int argc, char *argv[])
+{
+       int res;
+
+       git_threads_init();
+
+       /* Run the test suite */
+       res = clar_test(argc, argv);
+
+       giterr_clear();
+       git_threads_shutdown();
+
+       return res;
+}
diff --git a/tests-clar/network/fetch.c b/tests-clar/network/fetch.c
deleted file mode 100644 (file)
index 4cc2331..0000000
+++ /dev/null
@@ -1,116 +0,0 @@
-#include "clar_libgit2.h"
-
-CL_IN_CATEGORY("network")
-
-static git_repository *_repo;
-static int counter;
-
-void test_network_fetch__initialize(void)
-{
-       cl_git_pass(git_repository_init(&_repo, "./fetch", 0));
-}
-
-void test_network_fetch__cleanup(void)
-{
-       git_repository_free(_repo);
-       _repo = NULL;
-
-       cl_fixture_cleanup("./fetch");
-}
-
-static int update_tips(const char *refname, const git_oid *a, const git_oid *b, void *data)
-{
-       GIT_UNUSED(refname); GIT_UNUSED(a); GIT_UNUSED(b); GIT_UNUSED(data);
-
-       ++counter;
-
-       return 0;
-}
-
-static void progress(const git_transfer_progress *stats, void *payload)
-{
-       size_t *bytes_received = (size_t *)payload;
-       *bytes_received = stats->received_bytes;
-}
-
-static void do_fetch(const char *url, git_remote_autotag_option_t flag, int n)
-{
-       git_remote *remote;
-       git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
-       size_t bytes_received = 0;
-
-       callbacks.update_tips = update_tips;
-       counter = 0;
-
-       cl_git_pass(git_remote_add(&remote, _repo, "test", url));
-       git_remote_set_callbacks(remote, &callbacks);
-       git_remote_set_autotag(remote, flag);
-       cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH));
-       cl_git_pass(git_remote_download(remote, progress, &bytes_received));
-       cl_git_pass(git_remote_update_tips(remote));
-       git_remote_disconnect(remote);
-       cl_assert_equal_i(counter, n);
-       cl_assert(bytes_received > 0);
-
-       git_remote_free(remote);
-}
-
-void test_network_fetch__default_git(void)
-{
-       do_fetch("git://github.com/libgit2/TestGitRepository.git", GIT_REMOTE_DOWNLOAD_TAGS_AUTO, 6);
-}
-
-void test_network_fetch__default_http(void)
-{
-       do_fetch("http://github.com/libgit2/TestGitRepository.git", GIT_REMOTE_DOWNLOAD_TAGS_AUTO, 6);
-}
-
-void test_network_fetch__no_tags_git(void)
-{
-       do_fetch("git://github.com/libgit2/TestGitRepository.git", GIT_REMOTE_DOWNLOAD_TAGS_NONE, 3);
-}
-
-void test_network_fetch__no_tags_http(void)
-{
-       do_fetch("http://github.com/libgit2/TestGitRepository.git", GIT_REMOTE_DOWNLOAD_TAGS_NONE, 3);
-}
-
-static void transferProgressCallback(const git_transfer_progress *stats, void *payload)
-{
-       bool *invoked = (bool *)payload;
-
-       GIT_UNUSED(stats);
-       *invoked = true;
-}
-
-void test_network_fetch__doesnt_retrieve_a_pack_when_the_repository_is_up_to_date(void)
-{
-       git_repository *_repository;
-       bool invoked = false;
-       git_remote *remote, *origin;
-       git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
-
-       opts.bare = true;
-       cl_git_pass(git_remote_new(&origin, NULL, "origin", "https://github.com/libgit2/TestGitRepository.git", GIT_REMOTE_DEFAULT_FETCH));
-
-       cl_git_pass(git_clone(&_repository, origin, "./fetch/lg2", &opts));
-       git_repository_free(_repository);
-
-       cl_git_pass(git_repository_open(&_repository, "./fetch/lg2"));
-
-       cl_git_pass(git_remote_load(&remote, _repository, "origin"));
-       cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH));
-
-       cl_assert_equal_i(false, invoked);
-
-       cl_git_pass(git_remote_download(remote, &transferProgressCallback, &invoked));
-
-       cl_assert_equal_i(false, invoked);
-
-       cl_git_pass(git_remote_update_tips(remote));
-       git_remote_disconnect(remote);
-
-       git_remote_free(remote);
-       git_remote_free(origin);
-       git_repository_free(_repository);
-}
diff --git a/tests-clar/network/push.c b/tests-clar/network/push.c
deleted file mode 100644 (file)
index 788af52..0000000
+++ /dev/null
@@ -1,525 +0,0 @@
-#include "clar_libgit2.h"
-#include "buffer.h"
-#include "posix.h"
-#include "vector.h"
-#include "../submodule/submodule_helpers.h"
-#include "push_util.h"
-
-CL_IN_CATEGORY("network")
-
-static git_repository *_repo;
-
-static char *_remote_url;
-static char *_remote_user;
-static char *_remote_pass;
-
-static git_remote *_remote;
-static record_callbacks_data _record_cbs_data = {{ 0 }};
-static git_remote_callbacks _record_cbs = RECORD_CALLBACKS_INIT(&_record_cbs_data);
-
-static git_oid _oid_b6;
-static git_oid _oid_b5;
-static git_oid _oid_b4;
-static git_oid _oid_b3;
-static git_oid _oid_b2;
-static git_oid _oid_b1;
-
-/* git_oid *oid, git_repository *repo, (string literal) blob */
-#define CREATE_BLOB(oid, repo, blob) git_blob_create_frombuffer(oid, repo, blob, sizeof(blob) - 1)
-
-static int cred_acquire_cb(git_cred **cred, const char *url, unsigned int allowed_types, void *payload)
-{
-       GIT_UNUSED(url);
-
-       *((bool*)payload) = true;
-
-       if ((GIT_CREDTYPE_USERPASS_PLAINTEXT & allowed_types) == 0 ||
-               git_cred_userpass_plaintext_new(cred, _remote_user, _remote_pass) < 0)
-               return -1;
-
-       return 0;
-}
-
-typedef struct {
-       const char *ref;
-       const char *msg;
-} push_status;
-
-/**
- * git_push_status_foreach callback that records status entries.
- * @param data (git_vector *) of push_status instances
- */
-static int record_push_status_cb(const char *ref, const char *msg, void *data)
-{
-       git_vector *statuses = (git_vector *)data;
-       push_status *s;
-
-       cl_assert(s = git__malloc(sizeof(*s)));
-       s->ref = ref;
-       s->msg = msg;
-
-       git_vector_insert(statuses, s);
-
-       return 0;
-}
-
-static void do_verify_push_status(git_push *push, const push_status expected[], const size_t expected_len)
-{
-       git_vector actual = GIT_VECTOR_INIT;
-       push_status *iter;
-       bool failed = false;
-       size_t i;
-
-       git_push_status_foreach(push, record_push_status_cb, &actual);
-
-       if (expected_len != actual.length)
-               failed = true;
-       else
-               git_vector_foreach(&actual, i, iter)
-                       if (strcmp(expected[i].ref, iter->ref) ||
-                               (expected[i].msg && strcmp(expected[i].msg, iter->msg))) {
-                               failed = true;
-                               break;
-                       }
-
-       if (failed) {
-               git_buf msg = GIT_BUF_INIT;
-
-               git_buf_puts(&msg, "Expected and actual push statuses differ:\nEXPECTED:\n");
-
-               for(i = 0; i < expected_len; i++) {
-                       git_buf_printf(&msg, "%s: %s\n",
-                               expected[i].ref,
-                               expected[i].msg ? expected[i].msg : "<NULL>");
-               }
-
-               git_buf_puts(&msg, "\nACTUAL:\n");
-
-               git_vector_foreach(&actual, i, iter)
-                       git_buf_printf(&msg, "%s: %s\n", iter->ref, iter->msg);
-
-               cl_fail(git_buf_cstr(&msg));
-
-               git_buf_free(&msg);
-       }
-
-       git_vector_foreach(&actual, i, iter)
-               git__free(iter);
-
-       git_vector_free(&actual);
-}
-
-/**
- * Verifies that after git_push_finish(), refs on a remote have the expected
- * names, oids, and order.
- * 
- * @param remote remote to verify
- * @param expected_refs expected remote refs after push
- * @param expected_refs_len length of expected_refs
- */
-static void verify_refs(git_remote *remote, expected_ref expected_refs[], size_t expected_refs_len)
-{
-       git_vector actual_refs = GIT_VECTOR_INIT;
-
-       git_remote_ls(remote, record_ref_cb, &actual_refs);
-       verify_remote_refs(&actual_refs, expected_refs, expected_refs_len);
-
-       git_vector_free(&actual_refs);
-}
-
-void test_network_push__initialize(void)
-{
-       git_vector delete_specs = GIT_VECTOR_INIT;
-       size_t i;
-       char *curr_del_spec;
-       bool cred_acquire_called = false;
-
-       _repo = cl_git_sandbox_init("push_src");
-
-       cl_fixture_sandbox("testrepo.git");
-       cl_rename("push_src/submodule/.gitted", "push_src/submodule/.git");
-
-       rewrite_gitmodules(git_repository_workdir(_repo));
-
-       /* git log --format=oneline --decorate --graph
-        * *-.   951bbbb90e2259a4c8950db78946784fb53fcbce (HEAD, b6) merge b3, b4, and b5 to b6
-        * |\ \
-        * | | * fa38b91f199934685819bea316186d8b008c52a2 (b5) added submodule named 'submodule' pointing to '../testrepo.git'
-        * | * | 27b7ce66243eb1403862d05f958c002312df173d (b4) edited fold\b.txt
-        * | |/
-        * * | d9b63a88223d8367516f50bd131a5f7349b7f3e4 (b3) edited a.txt
-        * |/
-        * * a78705c3b2725f931d3ee05348d83cc26700f247 (b2, b1) added fold and fold/b.txt
-        * * 5c0bb3d1b9449d1cc69d7519fd05166f01840915 added a.txt
-        */
-       git_oid_fromstr(&_oid_b6, "951bbbb90e2259a4c8950db78946784fb53fcbce");
-       git_oid_fromstr(&_oid_b5, "fa38b91f199934685819bea316186d8b008c52a2");
-       git_oid_fromstr(&_oid_b4, "27b7ce66243eb1403862d05f958c002312df173d");
-       git_oid_fromstr(&_oid_b3, "d9b63a88223d8367516f50bd131a5f7349b7f3e4");
-       git_oid_fromstr(&_oid_b2, "a78705c3b2725f931d3ee05348d83cc26700f247");
-       git_oid_fromstr(&_oid_b1, "a78705c3b2725f931d3ee05348d83cc26700f247");
-
-       /* Remote URL environment variable must be set.  User and password are optional.  */
-       _remote_url = cl_getenv("GITTEST_REMOTE_URL");
-       _remote_user = cl_getenv("GITTEST_REMOTE_USER");
-       _remote_pass = cl_getenv("GITTEST_REMOTE_PASS");
-       _remote = NULL;
-
-       if (_remote_url) {
-               cl_git_pass(git_remote_add(&_remote, _repo, "test", _remote_url));
-
-               git_remote_set_cred_acquire_cb(_remote, cred_acquire_cb, &cred_acquire_called);
-               record_callbacks_data_clear(&_record_cbs_data);
-               git_remote_set_callbacks(_remote, &_record_cbs);
-
-               cl_git_pass(git_remote_connect(_remote, GIT_DIRECTION_PUSH));
-
-               /* Clean up previously pushed branches.  Fails if receive.denyDeletes is
-                * set on the remote.  Also, on Git 1.7.0 and newer, you must run
-                * 'git config receive.denyDeleteCurrent ignore' in the remote repo in
-                * order to delete the remote branch pointed to by HEAD (usually master).
-                * See: https://raw.github.com/git/git/master/Documentation/RelNotes/1.7.0.txt
-                */
-               cl_git_pass(git_remote_ls(_remote, delete_ref_cb, &delete_specs));
-               if (delete_specs.length) {
-                       git_push *push;
-
-                       cl_git_pass(git_push_new(&push, _remote));
-
-                       git_vector_foreach(&delete_specs, i, curr_del_spec) {
-                               git_push_add_refspec(push, curr_del_spec);
-                               git__free(curr_del_spec);
-                       }
-
-                       cl_git_pass(git_push_finish(push));
-                       git_push_free(push);
-               }
-
-               git_remote_disconnect(_remote);
-               git_vector_free(&delete_specs);
-
-               /* Now that we've deleted everything, fetch from the remote */
-               cl_git_pass(git_remote_connect(_remote, GIT_DIRECTION_FETCH));
-               cl_git_pass(git_remote_download(_remote, NULL, NULL));
-               cl_git_pass(git_remote_update_tips(_remote));
-               git_remote_disconnect(_remote);
-       } else
-               printf("GITTEST_REMOTE_URL unset; skipping push test\n");
-}
-
-void test_network_push__cleanup(void)
-{
-       if (_remote)
-               git_remote_free(_remote);
-       _remote = NULL;
-
-       /* Freed by cl_git_sandbox_cleanup */
-       _repo = NULL;
-
-       record_callbacks_data_clear(&_record_cbs_data);
-
-       cl_fixture_cleanup("testrepo.git");
-       cl_git_sandbox_cleanup();
-}
-
-/**
- * Calls push and relists refs on remote to verify success.
- * 
- * @param refspecs refspecs to push
- * @param refspecs_len length of refspecs
- * @param expected_refs expected remote refs after push
- * @param expected_refs_len length of expected_refs
- * @param expected_ret expected return value from git_push_finish()
- */
-static void do_push(const char *refspecs[], size_t refspecs_len,
-       push_status expected_statuses[], size_t expected_statuses_len,
-       expected_ref expected_refs[], size_t expected_refs_len, int expected_ret)
-{
-       git_push *push;
-       size_t i;
-       int ret;
-
-       if (_remote) {
-               cl_git_pass(git_remote_connect(_remote, GIT_DIRECTION_PUSH));
-
-               cl_git_pass(git_push_new(&push, _remote));
-
-               for (i = 0; i < refspecs_len; i++)
-                       cl_git_pass(git_push_add_refspec(push, refspecs[i]));
-
-               if (expected_ret < 0) {
-                       cl_git_fail(ret = git_push_finish(push));
-                       cl_assert_equal_i(0, git_push_unpack_ok(push));
-               }
-               else {
-                       cl_git_pass(ret = git_push_finish(push));
-                       cl_assert_equal_i(1, git_push_unpack_ok(push));
-               }
-
-               do_verify_push_status(push, expected_statuses, expected_statuses_len);
-
-               cl_assert_equal_i(expected_ret, ret);
-
-               git_push_free(push);
-
-               verify_refs(_remote, expected_refs, expected_refs_len);
-
-               cl_git_pass(git_remote_update_tips(_remote));
-
-               git_remote_disconnect(_remote);
-       }
-}
-
-/* Call push_finish() without ever calling git_push_add_refspec() */
-void test_network_push__noop(void)
-{
-       do_push(NULL, 0, NULL, 0, NULL, 0, 0);
-}
-
-void test_network_push__b1(void)
-{
-       const char *specs[] = { "refs/heads/b1:refs/heads/b1" };
-       push_status exp_stats[] = { { "refs/heads/b1", NULL } };
-       expected_ref exp_refs[] = { { "refs/heads/b1", &_oid_b1 } };
-       do_push(specs, ARRAY_SIZE(specs),
-               exp_stats, ARRAY_SIZE(exp_stats),
-               exp_refs, ARRAY_SIZE(exp_refs), 0);
-}
-
-void test_network_push__b2(void)
-{
-       const char *specs[] = { "refs/heads/b2:refs/heads/b2" };
-       push_status exp_stats[] = { { "refs/heads/b2", NULL } };
-       expected_ref exp_refs[] = { { "refs/heads/b2", &_oid_b2 } };
-       do_push(specs, ARRAY_SIZE(specs),
-               exp_stats, ARRAY_SIZE(exp_stats),
-               exp_refs, ARRAY_SIZE(exp_refs), 0);
-}
-
-void test_network_push__b3(void)
-{
-       const char *specs[] = { "refs/heads/b3:refs/heads/b3" };
-       push_status exp_stats[] = { { "refs/heads/b3", NULL } };
-       expected_ref exp_refs[] = { { "refs/heads/b3", &_oid_b3 } };
-       do_push(specs, ARRAY_SIZE(specs),
-               exp_stats, ARRAY_SIZE(exp_stats),
-               exp_refs, ARRAY_SIZE(exp_refs), 0);
-}
-
-void test_network_push__b4(void)
-{
-       const char *specs[] = { "refs/heads/b4:refs/heads/b4" };
-       push_status exp_stats[] = { { "refs/heads/b4", NULL } };
-       expected_ref exp_refs[] = { { "refs/heads/b4", &_oid_b4 } };
-       do_push(specs, ARRAY_SIZE(specs),
-               exp_stats, ARRAY_SIZE(exp_stats),
-               exp_refs, ARRAY_SIZE(exp_refs), 0);
-}
-
-void test_network_push__b5(void)
-{
-       const char *specs[] = { "refs/heads/b5:refs/heads/b5" };
-       push_status exp_stats[] = { { "refs/heads/b5", NULL } };
-       expected_ref exp_refs[] = { { "refs/heads/b5", &_oid_b5 } };
-       do_push(specs, ARRAY_SIZE(specs),
-               exp_stats, ARRAY_SIZE(exp_stats),
-               exp_refs, ARRAY_SIZE(exp_refs), 0);
-}
-
-void test_network_push__multi(void)
-{
-       const char *specs[] = {
-               "refs/heads/b1:refs/heads/b1",
-               "refs/heads/b2:refs/heads/b2",
-               "refs/heads/b3:refs/heads/b3",
-               "refs/heads/b4:refs/heads/b4",
-               "refs/heads/b5:refs/heads/b5"
-       };
-       push_status exp_stats[] = {
-               { "refs/heads/b1", NULL },
-               { "refs/heads/b2", NULL },
-               { "refs/heads/b3", NULL },
-               { "refs/heads/b4", NULL },
-               { "refs/heads/b5", NULL }
-       };
-       expected_ref exp_refs[] = {
-               { "refs/heads/b1", &_oid_b1 },
-               { "refs/heads/b2", &_oid_b2 },
-               { "refs/heads/b3", &_oid_b3 },
-               { "refs/heads/b4", &_oid_b4 },
-               { "refs/heads/b5", &_oid_b5 }
-       };
-       do_push(specs, ARRAY_SIZE(specs),
-               exp_stats, ARRAY_SIZE(exp_stats),
-               exp_refs, ARRAY_SIZE(exp_refs), 0);
-}
-
-void test_network_push__implicit_tgt(void)
-{
-       const char *specs1[] = { "refs/heads/b1:" };
-       push_status exp_stats1[] = { { "refs/heads/b1", NULL } };
-       expected_ref exp_refs1[] = { { "refs/heads/b1", &_oid_b1 } };
-
-       const char *specs2[] = { "refs/heads/b2:" };
-       push_status exp_stats2[] = { { "refs/heads/b2", NULL } };
-       expected_ref exp_refs2[] = {
-       { "refs/heads/b1", &_oid_b1 },
-       { "refs/heads/b2", &_oid_b2 }
-       };
-
-       do_push(specs1, ARRAY_SIZE(specs1),
-               exp_stats1, ARRAY_SIZE(exp_stats1),
-               exp_refs1, ARRAY_SIZE(exp_refs1), 0);
-       do_push(specs2, ARRAY_SIZE(specs2),
-               exp_stats2, ARRAY_SIZE(exp_stats2),
-               exp_refs2, ARRAY_SIZE(exp_refs2), 0);
-}
-
-void test_network_push__fast_fwd(void)
-{
-       /* Fast forward b1 in tgt from _oid_b1 to _oid_b6. */
-
-       const char *specs_init[] = { "refs/heads/b1:refs/heads/b1" };
-       push_status exp_stats_init[] = { { "refs/heads/b1", NULL } };
-       expected_ref exp_refs_init[] = { { "refs/heads/b1", &_oid_b1 } };
-
-       const char *specs_ff[] = { "refs/heads/b6:refs/heads/b1" };
-       push_status exp_stats_ff[] = { { "refs/heads/b1", NULL } };
-       expected_ref exp_refs_ff[] = { { "refs/heads/b1", &_oid_b6 } };
-
-       /* Do a force push to reset b1 in target back to _oid_b1 */
-       const char *specs_reset[] = { "+refs/heads/b1:refs/heads/b1" };
-       /* Force should have no effect on a fast forward push */
-       const char *specs_ff_force[] = { "+refs/heads/b6:refs/heads/b1" };
-
-       do_push(specs_init, ARRAY_SIZE(specs_init),
-               exp_stats_init, ARRAY_SIZE(exp_stats_init),
-               exp_refs_init, ARRAY_SIZE(exp_refs_init), 0);
-
-       do_push(specs_ff, ARRAY_SIZE(specs_ff),
-               exp_stats_ff, ARRAY_SIZE(exp_stats_ff),
-               exp_refs_ff, ARRAY_SIZE(exp_refs_ff), 0);
-
-       do_push(specs_reset, ARRAY_SIZE(specs_reset),
-               exp_stats_init, ARRAY_SIZE(exp_stats_init),
-               exp_refs_init, ARRAY_SIZE(exp_refs_init), 0);
-
-       do_push(specs_ff_force, ARRAY_SIZE(specs_ff_force),
-               exp_stats_ff, ARRAY_SIZE(exp_stats_ff),
-               exp_refs_ff, ARRAY_SIZE(exp_refs_ff), 0);
-}
-
-void test_network_push__force(void)
-{
-       const char *specs1[] = {"refs/heads/b3:refs/heads/tgt"};
-       push_status exp_stats1[] = { { "refs/heads/tgt", NULL } };
-       expected_ref exp_refs1[] = { { "refs/heads/tgt", &_oid_b3 } };
-
-       const char *specs2[] = {"refs/heads/b4:refs/heads/tgt"};
-
-       const char *specs2_force[] = {"+refs/heads/b4:refs/heads/tgt"};
-       push_status exp_stats2_force[] = { { "refs/heads/tgt", NULL } };
-       expected_ref exp_refs2_force[] = { { "refs/heads/tgt", &_oid_b4 } };
-
-       do_push(specs1, ARRAY_SIZE(specs1),
-               exp_stats1, ARRAY_SIZE(exp_stats1),
-               exp_refs1, ARRAY_SIZE(exp_refs1), 0);
-
-       do_push(specs2, ARRAY_SIZE(specs2),
-               NULL, 0,
-               exp_refs1, ARRAY_SIZE(exp_refs1), GIT_ENONFASTFORWARD);
-
-       /* Non-fast-forward update with force should pass. */
-       do_push(specs2_force, ARRAY_SIZE(specs2_force),
-               exp_stats2_force, ARRAY_SIZE(exp_stats2_force),
-               exp_refs2_force, ARRAY_SIZE(exp_refs2_force), 0);
-}
-
-void test_network_push__delete(void)
-{
-       const char *specs1[] = {
-               "refs/heads/b1:refs/heads/tgt1",
-               "refs/heads/b1:refs/heads/tgt2"
-       };
-       push_status exp_stats1[] = {
-               { "refs/heads/tgt1", NULL },
-               { "refs/heads/tgt2", NULL }
-       };
-       expected_ref exp_refs1[] = {
-               { "refs/heads/tgt1", &_oid_b1 },
-               { "refs/heads/tgt2", &_oid_b1 }
-       };
-
-       const char *specs_del_fake[] = { ":refs/heads/fake" };
-       /* Force has no effect for delete. */
-       const char *specs_del_fake_force[] = { "+:refs/heads/fake" };
-
-       const char *specs_delete[] = { ":refs/heads/tgt1" };
-       push_status exp_stats_delete[] = { { "refs/heads/tgt1", NULL } };
-       expected_ref exp_refs_delete[] = { { "refs/heads/tgt2", &_oid_b1 } };
-       /* Force has no effect for delete. */
-       const char *specs_delete_force[] = { "+:refs/heads/tgt1" };
-
-       do_push(specs1, ARRAY_SIZE(specs1),
-               exp_stats1, ARRAY_SIZE(exp_stats1),
-               exp_refs1, ARRAY_SIZE(exp_refs1), 0);
-
-       /* Deleting a non-existent branch should fail before the request is sent to
-        * the server because the client cannot find the old oid for the ref.
-        */
-       do_push(specs_del_fake, ARRAY_SIZE(specs_del_fake),
-               NULL, 0,
-               exp_refs1, ARRAY_SIZE(exp_refs1), -1);
-       do_push(specs_del_fake_force, ARRAY_SIZE(specs_del_fake_force),
-               NULL, 0,
-               exp_refs1, ARRAY_SIZE(exp_refs1), -1);
-
-       /* Delete one of the pushed branches. */
-       do_push(specs_delete, ARRAY_SIZE(specs_delete),
-               exp_stats_delete, ARRAY_SIZE(exp_stats_delete),
-               exp_refs_delete, ARRAY_SIZE(exp_refs_delete), 0);
-
-       /* Re-push branches and retry delete with force. */
-       do_push(specs1, ARRAY_SIZE(specs1),
-               exp_stats1, ARRAY_SIZE(exp_stats1),
-               exp_refs1, ARRAY_SIZE(exp_refs1), 0);
-       do_push(specs_delete_force, ARRAY_SIZE(specs_delete_force),
-               exp_stats_delete, ARRAY_SIZE(exp_stats_delete),
-               exp_refs_delete, ARRAY_SIZE(exp_refs_delete), 0);
-}
-
-void test_network_push__bad_refspecs(void)
-{
-       /* All classes of refspecs that should be rejected by
-        * git_push_add_refspec() should go in this test.
-        */
-       git_push *push;
-
-       if (_remote) {
-               cl_git_pass(git_remote_connect(_remote, GIT_DIRECTION_PUSH));
-               cl_git_pass(git_push_new(&push, _remote));
-
-               /* Unexpanded branch names not supported */
-               cl_git_fail(git_push_add_refspec(push, "b6:b6"));
-
-               git_push_free(push);
-       }
-}
-
-void test_network_push__expressions(void)
-{
-       /* TODO: Expressions in refspecs doesn't actually work yet */
-       const char *specs_left_expr[] = { "refs/heads/b2~1:refs/heads/b2" };
-
-       const char *specs_right_expr[] = { "refs/heads/b2:refs/heads/b2~1" };
-       push_status exp_stats_right_expr[] = { { "refs/heads/b2~1", "funny refname" } };
-
-       /* TODO: Find a more precise way of checking errors than a exit code of -1. */
-       do_push(specs_left_expr, ARRAY_SIZE(specs_left_expr),
-               NULL, 0,
-               NULL, 0, -1);
-
-       do_push(specs_right_expr, ARRAY_SIZE(specs_right_expr),
-               exp_stats_right_expr, ARRAY_SIZE(exp_stats_right_expr),
-               NULL, 0, 0);
-}
diff --git a/tests-clar/network/push_util.c b/tests-clar/network/push_util.c
deleted file mode 100644 (file)
index 2e45784..0000000
+++ /dev/null
@@ -1,126 +0,0 @@
-
-#include "clar_libgit2.h"
-#include "buffer.h"
-#include "vector.h"
-#include "push_util.h"
-
-const git_oid OID_ZERO = {{ 0 }};
-
-void updated_tip_free(updated_tip *t)
-{
-       git__free(t->name);
-       git__free(t->old_oid);
-       git__free(t->new_oid);
-       git__free(t);
-}
-
-void record_callbacks_data_clear(record_callbacks_data *data)
-{
-       size_t i;
-       updated_tip *tip;
-
-       git_vector_foreach(&data->updated_tips, i, tip)
-               updated_tip_free(tip);
-
-       git_vector_free(&data->updated_tips);
-}
-
-int record_update_tips_cb(const char *refname, const git_oid *a, const git_oid *b, void *data)
-{
-       updated_tip *t;
-       record_callbacks_data *record_data = (record_callbacks_data *)data;
-
-       cl_assert(t = git__malloc(sizeof(*t)));
-
-       cl_assert(t->name = git__strdup(refname));
-       cl_assert(t->old_oid = git__malloc(sizeof(*t->old_oid)));
-       git_oid_cpy(t->old_oid, a);
-
-       cl_assert(t->new_oid = git__malloc(sizeof(*t->new_oid)));
-       git_oid_cpy(t->new_oid, b);
-
-       git_vector_insert(&record_data->updated_tips, t);
-
-       return 0;
-}
-
-int delete_ref_cb(git_remote_head *head, void *payload)
-{
-       git_vector *delete_specs = (git_vector *)payload;
-       git_buf del_spec = GIT_BUF_INIT;
-
-       /* Ignore malformed ref names (which also saves us from tag^{} */
-       if (!git_reference_is_valid_name(head->name))
-               return 0;
-
-       /* Create a refspec that deletes a branch in the remote */
-       if (strcmp(head->name, "refs/heads/master")) {
-               cl_git_pass(git_buf_putc(&del_spec, ':'));
-               cl_git_pass(git_buf_puts(&del_spec, head->name));
-               cl_git_pass(git_vector_insert(delete_specs, git_buf_detach(&del_spec)));
-       }
-
-       return 0;
-}
-
-int record_ref_cb(git_remote_head *head, void *payload)
-{
-       git_vector *refs = (git_vector *) payload;
-       return git_vector_insert(refs, head);
-}
-
-void verify_remote_refs(git_vector *actual_refs, const expected_ref expected_refs[], size_t expected_refs_len)
-{
-       size_t i, j = 0;
-       git_buf msg = GIT_BUF_INIT;
-       git_remote_head *actual;
-       char *oid_str;
-       bool master_present = false;
-
-       /* We don't care whether "master" is present on the other end or not */
-       git_vector_foreach(actual_refs, i, actual) {
-               if (!strcmp(actual->name, "refs/heads/master")) {
-                       master_present = true;
-                       break;
-               }
-       }
-
-       if (expected_refs_len + (master_present ? 1 : 0) != actual_refs->length)
-               goto failed;
-
-       git_vector_foreach(actual_refs, i, actual) {
-               if (master_present && !strcmp(actual->name, "refs/heads/master"))
-                       continue;
-
-               if (strcmp(expected_refs[j].name, actual->name) ||
-                       git_oid_cmp(expected_refs[j].oid, &actual->oid))
-                       goto failed;
-
-               j++;
-       }
-
-       return;
-
-failed:
-       git_buf_puts(&msg, "Expected and actual refs differ:\nEXPECTED:\n");
-
-       for(i = 0; i < expected_refs_len; i++) {
-               cl_assert(oid_str = git_oid_allocfmt(expected_refs[i].oid));
-               cl_git_pass(git_buf_printf(&msg, "%s = %s\n", expected_refs[i].name, oid_str));
-               git__free(oid_str);
-       }
-
-       git_buf_puts(&msg, "\nACTUAL:\n");
-       git_vector_foreach(actual_refs, i, actual) {
-               if (master_present && !strcmp(actual->name, "refs/heads/master"))
-                       continue;
-
-               cl_assert(oid_str = git_oid_allocfmt(&actual->oid));
-               cl_git_pass(git_buf_printf(&msg, "%s = %s\n", actual->name, oid_str));
-               git__free(oid_str);
-       }
-
-       cl_fail(git_buf_cstr(&msg));
-
-       git_buf_free(&msg);
-}
diff --git a/tests-clar/network/push_util.h b/tests-clar/network/push_util.h
deleted file mode 100644 (file)
index 759122a..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-#ifndef INCLUDE_cl_push_util_h__
-#define INCLUDE_cl_push_util_h__
-
-#include "git2/oid.h"
-
-/* Constant for zero oid */
-extern const git_oid OID_ZERO;
-
-/**
- * Macro for initializing git_remote_callbacks to use test helpers that
- * record data in a record_callbacks_data instance.
- * @param data pointer to a record_callbacks_data instance
- */
-#define RECORD_CALLBACKS_INIT(data) \
-       { GIT_REMOTE_CALLBACKS_VERSION, NULL, NULL, record_update_tips_cb, data }
-
-typedef struct {
-       char *name;
-       git_oid *old_oid;
-       git_oid *new_oid;
-} updated_tip;
-
-typedef struct {
-       git_vector updated_tips;
-} record_callbacks_data;
-
-typedef struct {
-       const char *name;
-       const git_oid *oid;
-} expected_ref;
-
-void updated_tip_free(updated_tip *t);
-
-void record_callbacks_data_clear(record_callbacks_data *data);
-
-/**
- * Callback for git_remote_update_tips that records updates
- *
- * @param data (git_vector *) of updated_tip instances
- */
-int record_update_tips_cb(const char *refname, const git_oid *a, const git_oid *b, void *data);
-
-/**
- * Callback for git_remote_list that adds refspecs to delete each ref
- *
- * @param head a ref on the remote
- * @param payload a git_push instance
- */
-int delete_ref_cb(git_remote_head *head, void *payload);
-
-/**
- * Callback for git_remote_list that adds refspecs to vector
- *
- * @param head a ref on the remote
- * @param payload (git_vector *) of git_remote_head instances
- */
-int record_ref_cb(git_remote_head *head, void *payload);
-
-/**
- * Verifies that refs on remote stored by record_ref_cb match the expected
- * names, oids, and order.
- *
- * @param actual_refs actual refs stored by record_ref_cb()
- * @param expected_refs expected remote refs
- * @param expected_refs_len length of expected_refs
- */
-void verify_remote_refs(git_vector *actual_refs, const expected_ref expected_refs[], size_t expected_refs_len);
-
-#endif /* INCLUDE_cl_push_util_h__ */
diff --git a/tests-clar/online/clone.c b/tests-clar/online/clone.c
new file mode 100644 (file)
index 0000000..f783a5a
--- /dev/null
@@ -0,0 +1,152 @@
+#include "clar_libgit2.h"
+
+#include "git2/clone.h"
+#include "repository.h"
+
+#define LIVE_REPO_URL "http://github.com/libgit2/TestGitRepository"
+#define LIVE_EMPTYREPO_URL "http://github.com/libgit2/TestEmptyRepository"
+
+static git_repository *g_repo;
+static git_remote *g_origin;
+static git_clone_options g_options;
+
+void test_clone_network__initialize(void)
+{
+       g_repo = NULL;
+
+       memset(&g_options, 0, sizeof(git_clone_options));
+       g_options.version = GIT_CLONE_OPTIONS_VERSION;
+       cl_git_pass(git_remote_new(&g_origin, NULL, "origin", LIVE_REPO_URL, GIT_REMOTE_DEFAULT_FETCH));
+}
+
+void test_clone_network__cleanup(void)
+{
+       git_remote_free(g_origin);
+}
+
+static void cleanup_repository(void *path)
+{
+       if (g_repo) {
+               git_repository_free(g_repo);
+               g_repo = NULL;
+       }
+       cl_fixture_cleanup((const char *)path);
+}
+
+
+void test_clone_network__network_full(void)
+{
+       git_remote *origin;
+
+       cl_set_cleanup(&cleanup_repository, "./foo");
+
+       cl_git_pass(git_clone(&g_repo, g_origin, "./foo", &g_options));
+       cl_assert(!git_repository_is_bare(g_repo));
+       cl_git_pass(git_remote_load(&origin, g_repo, "origin"));
+
+       git_remote_free(origin);
+}
+
+
+void test_clone_network__network_bare(void)
+{
+       git_remote *origin;
+
+       cl_set_cleanup(&cleanup_repository, "./foo");
+       g_options.bare = true;
+
+       cl_git_pass(git_clone(&g_repo, g_origin, "./foo", &g_options));
+       cl_assert(git_repository_is_bare(g_repo));
+       cl_git_pass(git_remote_load(&origin, g_repo, "origin"));
+
+       git_remote_free(origin);
+}
+
+void test_clone_network__cope_with_already_existing_directory(void)
+{
+       cl_set_cleanup(&cleanup_repository, "./foo");
+
+       p_mkdir("./foo", GIT_DIR_MODE);
+       cl_git_pass(git_clone(&g_repo, g_origin, "./foo", &g_options));
+}
+
+void test_clone_network__empty_repository(void)
+{
+       git_reference *head;
+
+       cl_set_cleanup(&cleanup_repository, "./foo");
+
+       git_remote_free(g_origin);
+       cl_git_pass(git_remote_new(&g_origin, NULL, "origin", LIVE_EMPTYREPO_URL, GIT_REMOTE_DEFAULT_FETCH));
+
+       cl_git_pass(git_clone(&g_repo, g_origin, "./foo", &g_options));
+
+       cl_assert_equal_i(true, git_repository_is_empty(g_repo));
+       cl_assert_equal_i(true, git_repository_head_orphan(g_repo));
+
+       cl_git_pass(git_reference_lookup(&head, g_repo, GIT_HEAD_FILE));
+       cl_assert_equal_i(GIT_REF_SYMBOLIC, git_reference_type(head));
+       cl_assert_equal_s("refs/heads/master", git_reference_symbolic_target(head));
+
+       git_reference_free(head);
+}
+
+void test_clone_network__can_prevent_the_checkout_of_a_standard_repo(void)
+{
+       git_buf path = GIT_BUF_INIT;
+       cl_set_cleanup(&cleanup_repository, "./foo");
+
+       cl_git_pass(git_clone(&g_repo, g_origin, "./foo", &g_options));
+
+       cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "master.txt"));
+       cl_assert_equal_i(false, git_path_isfile(git_buf_cstr(&path)));
+
+       git_buf_free(&path);
+}
+
+static void checkout_progress(const char *path, size_t cur, size_t tot, void *payload)
+{
+       bool *was_called = (bool*)payload;
+       GIT_UNUSED(path); GIT_UNUSED(cur); GIT_UNUSED(tot);
+       (*was_called) = true;
+}
+
+static void fetch_progress(const git_transfer_progress *stats, void *payload)
+{
+       bool *was_called = (bool*)payload;
+       GIT_UNUSED(stats);
+       (*was_called) = true;
+}
+
+void test_clone_network__can_checkout_a_cloned_repo(void)
+{
+       git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+       git_buf path = GIT_BUF_INIT;
+       git_reference *head;
+       bool checkout_progress_cb_was_called = false,
+                 fetch_progress_cb_was_called = false;
+
+       opts.checkout_strategy = GIT_CHECKOUT_SAFE;
+       opts.progress_cb = &checkout_progress;
+       opts.progress_payload = &checkout_progress_cb_was_called;
+       g_options.checkout_opts = &opts;
+       g_options.fetch_progress_cb = &fetch_progress;
+       g_options.fetch_progress_payload = &fetch_progress_cb_was_called;
+
+       cl_set_cleanup(&cleanup_repository, "./foo");
+
+       cl_git_pass(git_clone(&g_repo, g_origin, "./foo", &g_options));
+
+       cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "master.txt"));
+       cl_assert_equal_i(true, git_path_isfile(git_buf_cstr(&path)));
+
+       cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD"));
+       cl_assert_equal_i(GIT_REF_SYMBOLIC, git_reference_type(head));
+       cl_assert_equal_s("refs/heads/master", git_reference_symbolic_target(head));
+
+       cl_assert_equal_i(true, checkout_progress_cb_was_called);
+       cl_assert_equal_i(true, fetch_progress_cb_was_called);
+
+       git_reference_free(head);
+       git_buf_free(&path);
+}
diff --git a/tests-clar/online/fetch.c b/tests-clar/online/fetch.c
new file mode 100644 (file)
index 0000000..979b023
--- /dev/null
@@ -0,0 +1,114 @@
+#include "clar_libgit2.h"
+
+static git_repository *_repo;
+static int counter;
+
+void test_network_fetch__initialize(void)
+{
+       cl_git_pass(git_repository_init(&_repo, "./fetch", 0));
+}
+
+void test_network_fetch__cleanup(void)
+{
+       git_repository_free(_repo);
+       _repo = NULL;
+
+       cl_fixture_cleanup("./fetch");
+}
+
+static int update_tips(const char *refname, const git_oid *a, const git_oid *b, void *data)
+{
+       GIT_UNUSED(refname); GIT_UNUSED(a); GIT_UNUSED(b); GIT_UNUSED(data);
+
+       ++counter;
+
+       return 0;
+}
+
+static void progress(const git_transfer_progress *stats, void *payload)
+{
+       size_t *bytes_received = (size_t *)payload;
+       *bytes_received = stats->received_bytes;
+}
+
+static void do_fetch(const char *url, git_remote_autotag_option_t flag, int n)
+{
+       git_remote *remote;
+       git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
+       size_t bytes_received = 0;
+
+       callbacks.update_tips = update_tips;
+       counter = 0;
+
+       cl_git_pass(git_remote_add(&remote, _repo, "test", url));
+       git_remote_set_callbacks(remote, &callbacks);
+       git_remote_set_autotag(remote, flag);
+       cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH));
+       cl_git_pass(git_remote_download(remote, progress, &bytes_received));
+       cl_git_pass(git_remote_update_tips(remote));
+       git_remote_disconnect(remote);
+       cl_assert_equal_i(counter, n);
+       cl_assert(bytes_received > 0);
+
+       git_remote_free(remote);
+}
+
+void test_network_fetch__default_git(void)
+{
+       do_fetch("git://github.com/libgit2/TestGitRepository.git", GIT_REMOTE_DOWNLOAD_TAGS_AUTO, 6);
+}
+
+void test_network_fetch__default_http(void)
+{
+       do_fetch("http://github.com/libgit2/TestGitRepository.git", GIT_REMOTE_DOWNLOAD_TAGS_AUTO, 6);
+}
+
+void test_network_fetch__no_tags_git(void)
+{
+       do_fetch("git://github.com/libgit2/TestGitRepository.git", GIT_REMOTE_DOWNLOAD_TAGS_NONE, 3);
+}
+
+void test_network_fetch__no_tags_http(void)
+{
+       do_fetch("http://github.com/libgit2/TestGitRepository.git", GIT_REMOTE_DOWNLOAD_TAGS_NONE, 3);
+}
+
+static void transferProgressCallback(const git_transfer_progress *stats, void *payload)
+{
+       bool *invoked = (bool *)payload;
+
+       GIT_UNUSED(stats);
+       *invoked = true;
+}
+
+void test_network_fetch__doesnt_retrieve_a_pack_when_the_repository_is_up_to_date(void)
+{
+       git_repository *_repository;
+       bool invoked = false;
+       git_remote *remote, *origin;
+       git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
+
+       opts.bare = true;
+       cl_git_pass(git_remote_new(&origin, NULL, "origin", "https://github.com/libgit2/TestGitRepository.git", GIT_REMOTE_DEFAULT_FETCH));
+
+       cl_git_pass(git_clone(&_repository, origin, "./fetch/lg2", &opts));
+       git_repository_free(_repository);
+
+       cl_git_pass(git_repository_open(&_repository, "./fetch/lg2"));
+
+       cl_git_pass(git_remote_load(&remote, _repository, "origin"));
+       cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH));
+
+       cl_assert_equal_i(false, invoked);
+
+       cl_git_pass(git_remote_download(remote, &transferProgressCallback, &invoked));
+
+       cl_assert_equal_i(false, invoked);
+
+       cl_git_pass(git_remote_update_tips(remote));
+       git_remote_disconnect(remote);
+
+       git_remote_free(remote);
+       git_remote_free(origin);
+       git_repository_free(_repository);
+}
diff --git a/tests-clar/online/fetchhead.c b/tests-clar/online/fetchhead.c
new file mode 100644 (file)
index 0000000..a6b8a6f
--- /dev/null
@@ -0,0 +1,97 @@
+#include "clar_libgit2.h"
+
+#include "repository.h"
+#include "fetchhead.h"
+#include "../fetchhead/fetchhead_data.h"
+#include "git2/clone.h"
+
+#define LIVE_REPO_URL "git://github.com/libgit2/TestGitRepository"
+
+static git_repository *g_repo;
+static git_remote *g_origin;
+static git_clone_options g_options;
+
+void test_fetchhead_network__initialize(void)
+{
+       g_repo = NULL;
+
+       memset(&g_options, 0, sizeof(git_clone_options));
+       g_options.version = GIT_CLONE_OPTIONS_VERSION;
+       cl_git_pass(git_remote_new(&g_origin, NULL, "origin", LIVE_REPO_URL, GIT_REMOTE_DEFAULT_FETCH));
+}
+
+void test_fetchhead_network__cleanup(void)
+{
+       git_remote_free(g_origin);
+}
+
+static void cleanup_repository(void *path)
+{
+       if (g_repo) {
+               git_repository_free(g_repo);
+               g_repo = NULL;
+       }
+
+       cl_fixture_cleanup((const char *)path);
+}
+
+
+static void fetchhead_test_clone(void)
+{
+       cl_set_cleanup(&cleanup_repository, "./foo");
+
+       cl_git_pass(git_clone(&g_repo, g_origin, "./foo", &g_options));
+}
+
+static void fetchhead_test_fetch(const char *fetchspec, const char *expected_fetchhead)
+{
+       git_remote *remote;
+       git_buf fetchhead_buf = GIT_BUF_INIT;
+       int equals = 0;
+
+       cl_git_pass(git_remote_load(&remote, g_repo, "origin"));
+       git_remote_set_autotag(remote, GIT_REMOTE_DOWNLOAD_TAGS_AUTO);
+
+       if(fetchspec != NULL)
+               git_remote_set_fetchspec(remote, fetchspec);
+
+       cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH));
+       cl_git_pass(git_remote_download(remote, NULL, NULL));
+       cl_git_pass(git_remote_update_tips(remote));
+       git_remote_disconnect(remote);
+       git_remote_free(remote);
+
+       cl_git_pass(git_futils_readbuffer(&fetchhead_buf, "./foo/.git/FETCH_HEAD"));
+
+       equals = (strcmp(fetchhead_buf.ptr, expected_fetchhead) == 0);
+
+       git_buf_free(&fetchhead_buf);
+
+       cl_assert(equals);
+}
+
+void test_fetchhead_network__wildcard_spec(void)
+{
+       fetchhead_test_clone();
+       fetchhead_test_fetch(NULL, FETCH_HEAD_WILDCARD_DATA);
+}
+
+void test_fetchhead_network__explicit_spec(void)
+{
+       fetchhead_test_clone();
+       fetchhead_test_fetch("refs/heads/first-merge:refs/remotes/origin/first-merge", FETCH_HEAD_EXPLICIT_DATA);
+}
+
+void test_fetchhead_network__no_merges(void)
+{
+       git_config *config;
+
+       fetchhead_test_clone();
+
+       cl_git_pass(git_repository_config(&config, g_repo));
+       cl_git_pass(git_config_set_string(config, "branch.master.remote", NULL));
+       cl_git_pass(git_config_set_string(config, "branch.master.merge", NULL));
+    git_config_free(config);
+
+       fetchhead_test_fetch(NULL, FETCH_HEAD_NO_MERGE_DATA);
+}
diff --git a/tests-clar/online/push.c b/tests-clar/online/push.c
new file mode 100644 (file)
index 0000000..8878e63
--- /dev/null
@@ -0,0 +1,523 @@
+#include "clar_libgit2.h"
+#include "buffer.h"
+#include "posix.h"
+#include "vector.h"
+#include "../submodule/submodule_helpers.h"
+#include "push_util.h"
+
+static git_repository *_repo;
+
+static char *_remote_url;
+static char *_remote_user;
+static char *_remote_pass;
+
+static git_remote *_remote;
+static record_callbacks_data _record_cbs_data = {{ 0 }};
+static git_remote_callbacks _record_cbs = RECORD_CALLBACKS_INIT(&_record_cbs_data);
+
+static git_oid _oid_b6;
+static git_oid _oid_b5;
+static git_oid _oid_b4;
+static git_oid _oid_b3;
+static git_oid _oid_b2;
+static git_oid _oid_b1;
+
+/* git_oid *oid, git_repository *repo, (string literal) blob */
+#define CREATE_BLOB(oid, repo, blob) git_blob_create_frombuffer(oid, repo, blob, sizeof(blob) - 1)
+
+static int cred_acquire_cb(git_cred **cred, const char *url, unsigned int allowed_types, void *payload)
+{
+       GIT_UNUSED(url);
+
+       *((bool*)payload) = true;
+
+       if ((GIT_CREDTYPE_USERPASS_PLAINTEXT & allowed_types) == 0 ||
+               git_cred_userpass_plaintext_new(cred, _remote_user, _remote_pass) < 0)
+               return -1;
+
+       return 0;
+}
+
+typedef struct {
+       const char *ref;
+       const char *msg;
+} push_status;
+
+/**
+ * git_push_status_foreach callback that records status entries.
+ * @param data (git_vector *) of push_status instances
+ */
+static int record_push_status_cb(const char *ref, const char *msg, void *data)
+{
+       git_vector *statuses = (git_vector *)data;
+       push_status *s;
+
+       cl_assert(s = git__malloc(sizeof(*s)));
+       s->ref = ref;
+       s->msg = msg;
+
+       git_vector_insert(statuses, s);
+
+       return 0;
+}
+
+static void do_verify_push_status(git_push *push, const push_status expected[], const size_t expected_len)
+{
+       git_vector actual = GIT_VECTOR_INIT;
+       push_status *iter;
+       bool failed = false;
+       size_t i;
+
+       git_push_status_foreach(push, record_push_status_cb, &actual);
+
+       if (expected_len != actual.length)
+               failed = true;
+       else
+               git_vector_foreach(&actual, i, iter)
+                       if (strcmp(expected[i].ref, iter->ref) ||
+                               (expected[i].msg && strcmp(expected[i].msg, iter->msg))) {
+                               failed = true;
+                               break;
+                       }
+
+       if (failed) {
+               git_buf msg = GIT_BUF_INIT;
+
+               git_buf_puts(&msg, "Expected and actual push statuses differ:\nEXPECTED:\n");
+
+               for(i = 0; i < expected_len; i++) {
+                       git_buf_printf(&msg, "%s: %s\n",
+                               expected[i].ref,
+                               expected[i].msg ? expected[i].msg : "<NULL>");
+               }
+
+               git_buf_puts(&msg, "\nACTUAL:\n");
+
+               git_vector_foreach(&actual, i, iter)
+                       git_buf_printf(&msg, "%s: %s\n", iter->ref, iter->msg);
+
+               cl_fail(git_buf_cstr(&msg));
+
+               git_buf_free(&msg);
+       }
+
+       git_vector_foreach(&actual, i, iter)
+               git__free(iter);
+
+       git_vector_free(&actual);
+}
+
+/**
+ * Verifies that after git_push_finish(), refs on a remote have the expected
+ * names, oids, and order.
+ *
+ * @param remote remote to verify
+ * @param expected_refs expected remote refs after push
+ * @param expected_refs_len length of expected_refs
+ */
+static void verify_refs(git_remote *remote, expected_ref expected_refs[], size_t expected_refs_len)
+{
+       git_vector actual_refs = GIT_VECTOR_INIT;
+
+       git_remote_ls(remote, record_ref_cb, &actual_refs);
+       verify_remote_refs(&actual_refs, expected_refs, expected_refs_len);
+
+       git_vector_free(&actual_refs);
+}
+
+void test_network_push__initialize(void)
+{
+       git_vector delete_specs = GIT_VECTOR_INIT;
+       size_t i;
+       char *curr_del_spec;
+       bool cred_acquire_called = false;
+
+       _repo = cl_git_sandbox_init("push_src");
+
+       cl_fixture_sandbox("testrepo.git");
+       cl_rename("push_src/submodule/.gitted", "push_src/submodule/.git");
+
+       rewrite_gitmodules(git_repository_workdir(_repo));
+
+       /* git log --format=oneline --decorate --graph
+        * *-.   951bbbb90e2259a4c8950db78946784fb53fcbce (HEAD, b6) merge b3, b4, and b5 to b6
+        * |\ \
+        * | | * fa38b91f199934685819bea316186d8b008c52a2 (b5) added submodule named 'submodule' pointing to '../testrepo.git'
+        * | * | 27b7ce66243eb1403862d05f958c002312df173d (b4) edited fold\b.txt
+        * | |/
+        * * | d9b63a88223d8367516f50bd131a5f7349b7f3e4 (b3) edited a.txt
+        * |/
+        * * a78705c3b2725f931d3ee05348d83cc26700f247 (b2, b1) added fold and fold/b.txt
+        * * 5c0bb3d1b9449d1cc69d7519fd05166f01840915 added a.txt
+        */
+       git_oid_fromstr(&_oid_b6, "951bbbb90e2259a4c8950db78946784fb53fcbce");
+       git_oid_fromstr(&_oid_b5, "fa38b91f199934685819bea316186d8b008c52a2");
+       git_oid_fromstr(&_oid_b4, "27b7ce66243eb1403862d05f958c002312df173d");
+       git_oid_fromstr(&_oid_b3, "d9b63a88223d8367516f50bd131a5f7349b7f3e4");
+       git_oid_fromstr(&_oid_b2, "a78705c3b2725f931d3ee05348d83cc26700f247");
+       git_oid_fromstr(&_oid_b1, "a78705c3b2725f931d3ee05348d83cc26700f247");
+
+       /* Remote URL environment variable must be set.  User and password are optional.  */
+       _remote_url = cl_getenv("GITTEST_REMOTE_URL");
+       _remote_user = cl_getenv("GITTEST_REMOTE_USER");
+       _remote_pass = cl_getenv("GITTEST_REMOTE_PASS");
+       _remote = NULL;
+
+       if (_remote_url) {
+               cl_git_pass(git_remote_add(&_remote, _repo, "test", _remote_url));
+
+               git_remote_set_cred_acquire_cb(_remote, cred_acquire_cb, &cred_acquire_called);
+               record_callbacks_data_clear(&_record_cbs_data);
+               git_remote_set_callbacks(_remote, &_record_cbs);
+
+               cl_git_pass(git_remote_connect(_remote, GIT_DIRECTION_PUSH));
+
+               /* Clean up previously pushed branches.  Fails if receive.denyDeletes is
+                * set on the remote.  Also, on Git 1.7.0 and newer, you must run
+                * 'git config receive.denyDeleteCurrent ignore' in the remote repo in
+                * order to delete the remote branch pointed to by HEAD (usually master).
+                * See: https://raw.github.com/git/git/master/Documentation/RelNotes/1.7.0.txt
+                */
+               cl_git_pass(git_remote_ls(_remote, delete_ref_cb, &delete_specs));
+               if (delete_specs.length) {
+                       git_push *push;
+
+                       cl_git_pass(git_push_new(&push, _remote));
+
+                       git_vector_foreach(&delete_specs, i, curr_del_spec) {
+                               git_push_add_refspec(push, curr_del_spec);
+                               git__free(curr_del_spec);
+                       }
+
+                       cl_git_pass(git_push_finish(push));
+                       git_push_free(push);
+               }
+
+               git_remote_disconnect(_remote);
+               git_vector_free(&delete_specs);
+
+               /* Now that we've deleted everything, fetch from the remote */
+               cl_git_pass(git_remote_connect(_remote, GIT_DIRECTION_FETCH));
+               cl_git_pass(git_remote_download(_remote, NULL, NULL));
+               cl_git_pass(git_remote_update_tips(_remote));
+               git_remote_disconnect(_remote);
+       } else
+               printf("GITTEST_REMOTE_URL unset; skipping push test\n");
+}
+
+void test_network_push__cleanup(void)
+{
+       if (_remote)
+               git_remote_free(_remote);
+       _remote = NULL;
+
+       /* Freed by cl_git_sandbox_cleanup */
+       _repo = NULL;
+
+       record_callbacks_data_clear(&_record_cbs_data);
+
+       cl_fixture_cleanup("testrepo.git");
+       cl_git_sandbox_cleanup();
+}
+
+/**
+ * Calls push and relists refs on remote to verify success.
+ *
+ * @param refspecs refspecs to push
+ * @param refspecs_len length of refspecs
+ * @param expected_refs expected remote refs after push
+ * @param expected_refs_len length of expected_refs
+ * @param expected_ret expected return value from git_push_finish()
+ */
+static void do_push(const char *refspecs[], size_t refspecs_len,
+       push_status expected_statuses[], size_t expected_statuses_len,
+       expected_ref expected_refs[], size_t expected_refs_len, int expected_ret)
+{
+       git_push *push;
+       size_t i;
+       int ret;
+
+       if (_remote) {
+               cl_git_pass(git_remote_connect(_remote, GIT_DIRECTION_PUSH));
+
+               cl_git_pass(git_push_new(&push, _remote));
+
+               for (i = 0; i < refspecs_len; i++)
+                       cl_git_pass(git_push_add_refspec(push, refspecs[i]));
+
+               if (expected_ret < 0) {
+                       cl_git_fail(ret = git_push_finish(push));
+                       cl_assert_equal_i(0, git_push_unpack_ok(push));
+               }
+               else {
+                       cl_git_pass(ret = git_push_finish(push));
+                       cl_assert_equal_i(1, git_push_unpack_ok(push));
+               }
+
+               do_verify_push_status(push, expected_statuses, expected_statuses_len);
+
+               cl_assert_equal_i(expected_ret, ret);
+
+               git_push_free(push);
+
+               verify_refs(_remote, expected_refs, expected_refs_len);
+
+               cl_git_pass(git_remote_update_tips(_remote));
+
+               git_remote_disconnect(_remote);
+       }
+}
+
+/* Call push_finish() without ever calling git_push_add_refspec() */
+void test_network_push__noop(void)
+{
+       do_push(NULL, 0, NULL, 0, NULL, 0, 0);
+}
+
+void test_network_push__b1(void)
+{
+       const char *specs[] = { "refs/heads/b1:refs/heads/b1" };
+       push_status exp_stats[] = { { "refs/heads/b1", NULL } };
+       expected_ref exp_refs[] = { { "refs/heads/b1", &_oid_b1 } };
+       do_push(specs, ARRAY_SIZE(specs),
+               exp_stats, ARRAY_SIZE(exp_stats),
+               exp_refs, ARRAY_SIZE(exp_refs), 0);
+}
+
+void test_network_push__b2(void)
+{
+       const char *specs[] = { "refs/heads/b2:refs/heads/b2" };
+       push_status exp_stats[] = { { "refs/heads/b2", NULL } };
+       expected_ref exp_refs[] = { { "refs/heads/b2", &_oid_b2 } };
+       do_push(specs, ARRAY_SIZE(specs),
+               exp_stats, ARRAY_SIZE(exp_stats),
+               exp_refs, ARRAY_SIZE(exp_refs), 0);
+}
+
+void test_network_push__b3(void)
+{
+       const char *specs[] = { "refs/heads/b3:refs/heads/b3" };
+       push_status exp_stats[] = { { "refs/heads/b3", NULL } };
+       expected_ref exp_refs[] = { { "refs/heads/b3", &_oid_b3 } };
+       do_push(specs, ARRAY_SIZE(specs),
+               exp_stats, ARRAY_SIZE(exp_stats),
+               exp_refs, ARRAY_SIZE(exp_refs), 0);
+}
+
+void test_network_push__b4(void)
+{
+       const char *specs[] = { "refs/heads/b4:refs/heads/b4" };
+       push_status exp_stats[] = { { "refs/heads/b4", NULL } };
+       expected_ref exp_refs[] = { { "refs/heads/b4", &_oid_b4 } };
+       do_push(specs, ARRAY_SIZE(specs),
+               exp_stats, ARRAY_SIZE(exp_stats),
+               exp_refs, ARRAY_SIZE(exp_refs), 0);
+}
+
+void test_network_push__b5(void)
+{
+       const char *specs[] = { "refs/heads/b5:refs/heads/b5" };
+       push_status exp_stats[] = { { "refs/heads/b5", NULL } };
+       expected_ref exp_refs[] = { { "refs/heads/b5", &_oid_b5 } };
+       do_push(specs, ARRAY_SIZE(specs),
+               exp_stats, ARRAY_SIZE(exp_stats),
+               exp_refs, ARRAY_SIZE(exp_refs), 0);
+}
+
+void test_network_push__multi(void)
+{
+       const char *specs[] = {
+               "refs/heads/b1:refs/heads/b1",
+               "refs/heads/b2:refs/heads/b2",
+               "refs/heads/b3:refs/heads/b3",
+               "refs/heads/b4:refs/heads/b4",
+               "refs/heads/b5:refs/heads/b5"
+       };
+       push_status exp_stats[] = {
+               { "refs/heads/b1", NULL },
+               { "refs/heads/b2", NULL },
+               { "refs/heads/b3", NULL },
+               { "refs/heads/b4", NULL },
+               { "refs/heads/b5", NULL }
+       };
+       expected_ref exp_refs[] = {
+               { "refs/heads/b1", &_oid_b1 },
+               { "refs/heads/b2", &_oid_b2 },
+               { "refs/heads/b3", &_oid_b3 },
+               { "refs/heads/b4", &_oid_b4 },
+               { "refs/heads/b5", &_oid_b5 }
+       };
+       do_push(specs, ARRAY_SIZE(specs),
+               exp_stats, ARRAY_SIZE(exp_stats),
+               exp_refs, ARRAY_SIZE(exp_refs), 0);
+}
+
+void test_network_push__implicit_tgt(void)
+{
+       const char *specs1[] = { "refs/heads/b1:" };
+       push_status exp_stats1[] = { { "refs/heads/b1", NULL } };
+       expected_ref exp_refs1[] = { { "refs/heads/b1", &_oid_b1 } };
+
+       const char *specs2[] = { "refs/heads/b2:" };
+       push_status exp_stats2[] = { { "refs/heads/b2", NULL } };
+       expected_ref exp_refs2[] = {
+       { "refs/heads/b1", &_oid_b1 },
+       { "refs/heads/b2", &_oid_b2 }
+       };
+
+       do_push(specs1, ARRAY_SIZE(specs1),
+               exp_stats1, ARRAY_SIZE(exp_stats1),
+               exp_refs1, ARRAY_SIZE(exp_refs1), 0);
+       do_push(specs2, ARRAY_SIZE(specs2),
+               exp_stats2, ARRAY_SIZE(exp_stats2),
+               exp_refs2, ARRAY_SIZE(exp_refs2), 0);
+}
+
+void test_network_push__fast_fwd(void)
+{
+       /* Fast forward b1 in tgt from _oid_b1 to _oid_b6. */
+
+       const char *specs_init[] = { "refs/heads/b1:refs/heads/b1" };
+       push_status exp_stats_init[] = { { "refs/heads/b1", NULL } };
+       expected_ref exp_refs_init[] = { { "refs/heads/b1", &_oid_b1 } };
+
+       const char *specs_ff[] = { "refs/heads/b6:refs/heads/b1" };
+       push_status exp_stats_ff[] = { { "refs/heads/b1", NULL } };
+       expected_ref exp_refs_ff[] = { { "refs/heads/b1", &_oid_b6 } };
+
+       /* Do a force push to reset b1 in target back to _oid_b1 */
+       const char *specs_reset[] = { "+refs/heads/b1:refs/heads/b1" };
+       /* Force should have no effect on a fast forward push */
+       const char *specs_ff_force[] = { "+refs/heads/b6:refs/heads/b1" };
+
+       do_push(specs_init, ARRAY_SIZE(specs_init),
+               exp_stats_init, ARRAY_SIZE(exp_stats_init),
+               exp_refs_init, ARRAY_SIZE(exp_refs_init), 0);
+
+       do_push(specs_ff, ARRAY_SIZE(specs_ff),
+               exp_stats_ff, ARRAY_SIZE(exp_stats_ff),
+               exp_refs_ff, ARRAY_SIZE(exp_refs_ff), 0);
+
+       do_push(specs_reset, ARRAY_SIZE(specs_reset),
+               exp_stats_init, ARRAY_SIZE(exp_stats_init),
+               exp_refs_init, ARRAY_SIZE(exp_refs_init), 0);
+
+       do_push(specs_ff_force, ARRAY_SIZE(specs_ff_force),
+               exp_stats_ff, ARRAY_SIZE(exp_stats_ff),
+               exp_refs_ff, ARRAY_SIZE(exp_refs_ff), 0);
+}
+
+void test_network_push__force(void)
+{
+       const char *specs1[] = {"refs/heads/b3:refs/heads/tgt"};
+       push_status exp_stats1[] = { { "refs/heads/tgt", NULL } };
+       expected_ref exp_refs1[] = { { "refs/heads/tgt", &_oid_b3 } };
+
+       const char *specs2[] = {"refs/heads/b4:refs/heads/tgt"};
+
+       const char *specs2_force[] = {"+refs/heads/b4:refs/heads/tgt"};
+       push_status exp_stats2_force[] = { { "refs/heads/tgt", NULL } };
+       expected_ref exp_refs2_force[] = { { "refs/heads/tgt", &_oid_b4 } };
+
+       do_push(specs1, ARRAY_SIZE(specs1),
+               exp_stats1, ARRAY_SIZE(exp_stats1),
+               exp_refs1, ARRAY_SIZE(exp_refs1), 0);
+
+       do_push(specs2, ARRAY_SIZE(specs2),
+               NULL, 0,
+               exp_refs1, ARRAY_SIZE(exp_refs1), GIT_ENONFASTFORWARD);
+
+       /* Non-fast-forward update with force should pass. */
+       do_push(specs2_force, ARRAY_SIZE(specs2_force),
+               exp_stats2_force, ARRAY_SIZE(exp_stats2_force),
+               exp_refs2_force, ARRAY_SIZE(exp_refs2_force), 0);
+}
+
+void test_network_push__delete(void)
+{
+       const char *specs1[] = {
+               "refs/heads/b1:refs/heads/tgt1",
+               "refs/heads/b1:refs/heads/tgt2"
+       };
+       push_status exp_stats1[] = {
+               { "refs/heads/tgt1", NULL },
+               { "refs/heads/tgt2", NULL }
+       };
+       expected_ref exp_refs1[] = {
+               { "refs/heads/tgt1", &_oid_b1 },
+               { "refs/heads/tgt2", &_oid_b1 }
+       };
+
+       const char *specs_del_fake[] = { ":refs/heads/fake" };
+       /* Force has no effect for delete. */
+       const char *specs_del_fake_force[] = { "+:refs/heads/fake" };
+
+       const char *specs_delete[] = { ":refs/heads/tgt1" };
+       push_status exp_stats_delete[] = { { "refs/heads/tgt1", NULL } };
+       expected_ref exp_refs_delete[] = { { "refs/heads/tgt2", &_oid_b1 } };
+       /* Force has no effect for delete. */
+       const char *specs_delete_force[] = { "+:refs/heads/tgt1" };
+
+       do_push(specs1, ARRAY_SIZE(specs1),
+               exp_stats1, ARRAY_SIZE(exp_stats1),
+               exp_refs1, ARRAY_SIZE(exp_refs1), 0);
+
+       /* Deleting a non-existent branch should fail before the request is sent to
+        * the server because the client cannot find the old oid for the ref.
+        */
+       do_push(specs_del_fake, ARRAY_SIZE(specs_del_fake),
+               NULL, 0,
+               exp_refs1, ARRAY_SIZE(exp_refs1), -1);
+       do_push(specs_del_fake_force, ARRAY_SIZE(specs_del_fake_force),
+               NULL, 0,
+               exp_refs1, ARRAY_SIZE(exp_refs1), -1);
+
+       /* Delete one of the pushed branches. */
+       do_push(specs_delete, ARRAY_SIZE(specs_delete),
+               exp_stats_delete, ARRAY_SIZE(exp_stats_delete),
+               exp_refs_delete, ARRAY_SIZE(exp_refs_delete), 0);
+
+       /* Re-push branches and retry delete with force. */
+       do_push(specs1, ARRAY_SIZE(specs1),
+               exp_stats1, ARRAY_SIZE(exp_stats1),
+               exp_refs1, ARRAY_SIZE(exp_refs1), 0);
+       do_push(specs_delete_force, ARRAY_SIZE(specs_delete_force),
+               exp_stats_delete, ARRAY_SIZE(exp_stats_delete),
+               exp_refs_delete, ARRAY_SIZE(exp_refs_delete), 0);
+}
+
+void test_network_push__bad_refspecs(void)
+{
+       /* All classes of refspecs that should be rejected by
+        * git_push_add_refspec() should go in this test.
+        */
+       git_push *push;
+
+       if (_remote) {
+               cl_git_pass(git_remote_connect(_remote, GIT_DIRECTION_PUSH));
+               cl_git_pass(git_push_new(&push, _remote));
+
+               /* Unexpanded branch names not supported */
+               cl_git_fail(git_push_add_refspec(push, "b6:b6"));
+
+               git_push_free(push);
+       }
+}
+
+void test_network_push__expressions(void)
+{
+       /* TODO: Expressions in refspecs doesn't actually work yet */
+       const char *specs_left_expr[] = { "refs/heads/b2~1:refs/heads/b2" };
+
+       const char *specs_right_expr[] = { "refs/heads/b2:refs/heads/b2~1" };
+       push_status exp_stats_right_expr[] = { { "refs/heads/b2~1", "funny refname" } };
+
+       /* TODO: Find a more precise way of checking errors than a exit code of -1. */
+       do_push(specs_left_expr, ARRAY_SIZE(specs_left_expr),
+               NULL, 0,
+               NULL, 0, -1);
+
+       do_push(specs_right_expr, ARRAY_SIZE(specs_right_expr),
+               exp_stats_right_expr, ARRAY_SIZE(exp_stats_right_expr),
+               NULL, 0, 0);
+}
diff --git a/tests-clar/online/push_util.c b/tests-clar/online/push_util.c
new file mode 100644 (file)
index 0000000..2e45784
--- /dev/null
@@ -0,0 +1,126 @@
+
+#include "clar_libgit2.h"
+#include "buffer.h"
+#include "vector.h"
+#include "push_util.h"
+
+const git_oid OID_ZERO = {{ 0 }};
+
+void updated_tip_free(updated_tip *t)
+{
+       git__free(t->name);
+       git__free(t->old_oid);
+       git__free(t->new_oid);
+       git__free(t);
+}
+
+void record_callbacks_data_clear(record_callbacks_data *data)
+{
+       size_t i;
+       updated_tip *tip;
+
+       git_vector_foreach(&data->updated_tips, i, tip)
+               updated_tip_free(tip);
+
+       git_vector_free(&data->updated_tips);
+}
+
+int record_update_tips_cb(const char *refname, const git_oid *a, const git_oid *b, void *data)
+{
+       updated_tip *t;
+       record_callbacks_data *record_data = (record_callbacks_data *)data;
+
+       cl_assert(t = git__malloc(sizeof(*t)));
+
+       cl_assert(t->name = git__strdup(refname));
+       cl_assert(t->old_oid = git__malloc(sizeof(*t->old_oid)));
+       git_oid_cpy(t->old_oid, a);
+
+       cl_assert(t->new_oid = git__malloc(sizeof(*t->new_oid)));
+       git_oid_cpy(t->new_oid, b);
+
+       git_vector_insert(&record_data->updated_tips, t);
+
+       return 0;
+}
+
+int delete_ref_cb(git_remote_head *head, void *payload)
+{
+       git_vector *delete_specs = (git_vector *)payload;
+       git_buf del_spec = GIT_BUF_INIT;
+
+       /* Ignore malformed ref names (which also saves us from tag^{} */
+       if (!git_reference_is_valid_name(head->name))
+               return 0;
+
+       /* Create a refspec that deletes a branch in the remote */
+       if (strcmp(head->name, "refs/heads/master")) {
+               cl_git_pass(git_buf_putc(&del_spec, ':'));
+               cl_git_pass(git_buf_puts(&del_spec, head->name));
+               cl_git_pass(git_vector_insert(delete_specs, git_buf_detach(&del_spec)));
+       }
+
+       return 0;
+}
+
+int record_ref_cb(git_remote_head *head, void *payload)
+{
+       git_vector *refs = (git_vector *) payload;
+       return git_vector_insert(refs, head);
+}
+
+void verify_remote_refs(git_vector *actual_refs, const expected_ref expected_refs[], size_t expected_refs_len)
+{
+       size_t i, j = 0;
+       git_buf msg = GIT_BUF_INIT;
+       git_remote_head *actual;
+       char *oid_str;
+       bool master_present = false;
+
+       /* We don't care whether "master" is present on the other end or not */
+       git_vector_foreach(actual_refs, i, actual) {
+               if (!strcmp(actual->name, "refs/heads/master")) {
+                       master_present = true;
+                       break;
+               }
+       }
+
+       if (expected_refs_len + (master_present ? 1 : 0) != actual_refs->length)
+               goto failed;
+
+       git_vector_foreach(actual_refs, i, actual) {
+               if (master_present && !strcmp(actual->name, "refs/heads/master"))
+                       continue;
+
+               if (strcmp(expected_refs[j].name, actual->name) ||
+                       git_oid_cmp(expected_refs[j].oid, &actual->oid))
+                       goto failed;
+
+               j++;
+       }
+
+       return;
+
+failed:
+       git_buf_puts(&msg, "Expected and actual refs differ:\nEXPECTED:\n");
+
+       for(i = 0; i < expected_refs_len; i++) {
+               cl_assert(oid_str = git_oid_allocfmt(expected_refs[i].oid));
+               cl_git_pass(git_buf_printf(&msg, "%s = %s\n", expected_refs[i].name, oid_str));
+               git__free(oid_str);
+       }
+
+       git_buf_puts(&msg, "\nACTUAL:\n");
+       git_vector_foreach(actual_refs, i, actual) {
+               if (master_present && !strcmp(actual->name, "refs/heads/master"))
+                       continue;
+
+               cl_assert(oid_str = git_oid_allocfmt(&actual->oid));
+               cl_git_pass(git_buf_printf(&msg, "%s = %s\n", actual->name, oid_str));
+               git__free(oid_str);
+       }
+
+       cl_fail(git_buf_cstr(&msg));
+
+       git_buf_free(&msg);
+}
diff --git a/tests-clar/online/push_util.h b/tests-clar/online/push_util.h
new file mode 100644 (file)
index 0000000..759122a
--- /dev/null
@@ -0,0 +1,69 @@
+#ifndef INCLUDE_cl_push_util_h__
+#define INCLUDE_cl_push_util_h__
+
+#include "git2/oid.h"
+
+/* Constant for zero oid */
+extern const git_oid OID_ZERO;
+
+/**
+ * Macro for initializing git_remote_callbacks to use test helpers that
+ * record data in a record_callbacks_data instance.
+ * @param data pointer to a record_callbacks_data instance
+ */
+#define RECORD_CALLBACKS_INIT(data) \
+       { GIT_REMOTE_CALLBACKS_VERSION, NULL, NULL, record_update_tips_cb, data }
+
+typedef struct {
+       char *name;
+       git_oid *old_oid;
+       git_oid *new_oid;
+} updated_tip;
+
+typedef struct {
+       git_vector updated_tips;
+} record_callbacks_data;
+
+typedef struct {
+       const char *name;
+       const git_oid *oid;
+} expected_ref;
+
+void updated_tip_free(updated_tip *t);
+
+void record_callbacks_data_clear(record_callbacks_data *data);
+
+/**
+ * Callback for git_remote_update_tips that records updates
+ *
+ * @param data (git_vector *) of updated_tip instances
+ */
+int record_update_tips_cb(const char *refname, const git_oid *a, const git_oid *b, void *data);
+
+/**
+ * Callback for git_remote_list that adds refspecs to delete each ref
+ *
+ * @param head a ref on the remote
+ * @param payload a git_push instance
+ */
+int delete_ref_cb(git_remote_head *head, void *payload);
+
+/**
+ * Callback for git_remote_list that adds refspecs to vector
+ *
+ * @param head a ref on the remote
+ * @param payload (git_vector *) of git_remote_head instances
+ */
+int record_ref_cb(git_remote_head *head, void *payload);
+
+/**
+ * Verifies that refs on remote stored by record_ref_cb match the expected
+ * names, oids, and order.
+ *
+ * @param actual_refs actual refs stored by record_ref_cb()
+ * @param expected_refs expected remote refs
+ * @param expected_refs_len length of expected_refs
+ */
+void verify_remote_refs(git_vector *actual_refs, const expected_ref expected_refs[], size_t expected_refs_len);
+
+#endif /* INCLUDE_cl_push_util_h__ */