-/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
.DS_Store
*~
tags
-mkmf.log
\ No newline at end of file
+mkmf.log
--- /dev/null
+[submodule "tests-clar/clar"]
+ path = tests-clar/clar
+ url = https://github.com/vmg/clar.git
# Tests
IF (BUILD_CLAR)
-
FIND_PACKAGE(PythonInterp REQUIRED)
SET(CLAR_FIXTURES "${CMAKE_CURRENT_SOURCE_DIR}/tests-clar/resources/")
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)
+++ /dev/null
-#!/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()
--- /dev/null
+Subproject commit 161e8cd0f7642385d22153052fbcf770517de6c1
#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;
#ifndef __CLAR_LIBGIT2__
#define __CLAR_LIBGIT2__
-#include "clar.h"
+#include "clar/clar.h"
#include <git2.h>
#include "common.h"
+++ /dev/null
-#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);
-}
+++ /dev/null
-#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);
-}
--- /dev/null
+#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;
+}
+++ /dev/null
-#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);
-}
+++ /dev/null
-#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);
-}
+++ /dev/null
-
-#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);
-}
+++ /dev/null
-#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__ */
--- /dev/null
+#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);
+}
--- /dev/null
+#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);
+}
--- /dev/null
+#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);
+}
--- /dev/null
+#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);
+}
--- /dev/null
+
+#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);
+}
--- /dev/null
+#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__ */