Commit 4fdc5f31 authored by Chris Mumford's avatar Chris Mumford Committed by Commit Bot

Unit tests for sqlite_cherry_picker.py.

These tests required some refactoring to sqlite_cherry_picker.py
to support testing.

Bug: 945204
Change-Id: I66eb10ca94a736a58ca75475d2fee3ba65f62dac
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2131249
Commit-Queue: Chris Mumford <cmumford@google.com>
Reviewed-by: default avatarDarwin Huang <huangdarwin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#758885}
parent 296f6fba
...@@ -8,11 +8,17 @@ Runs Python unit tests in /third_party/sqlite/scripts on upload. ...@@ -8,11 +8,17 @@ Runs Python unit tests in /third_party/sqlite/scripts on upload.
def CheckChangeOnUpload(input_api, output_api): def CheckChangeOnUpload(input_api, output_api):
results = [] results = []
this_dir = input_api.PresubmitLocalPath()
results += input_api.RunTests( results += input_api.RunTests(
input_api.canned_checks.GetUnitTests(input_api, output_api, [ input_api.canned_checks.GetUnitTestsInDirectory(
'scripts/extract_sqlite_api_unittest.py' input_api,
], env=None, run_on_python2=False, run_on_python3=True)) output_api,
input_api.os_path.join(this_dir, 'scripts'),
whitelist=['.*unittest.py$'],
env=None,
run_on_python2=False,
run_on_python3=True))
return results return results
[style]
based_on_style = pep8
# TODO(cmumford): Format older files in a future CL.
extract_sqlite_api.py
extract_sqlite_api_unittest.py
...@@ -12,325 +12,361 @@ import sys ...@@ -12,325 +12,361 @@ import sys
class UnstagedFiles(Exception): class UnstagedFiles(Exception):
pass pass
class UnknownHash(Exception): class UnknownHash(Exception):
pass pass
class IncorrectType(Exception):
pass
class bcolors: class bcolors:
HEADER = '\033[95m' HEADER = '\033[95m'
OKBLUE = '\033[94m' OKBLUE = '\033[94m'
OKGREEN = '\033[92m' OKGREEN = '\033[92m'
WARNING = '\033[93m' WARNING = '\033[93m'
FAIL = '\033[91m' FAIL = '\033[91m'
ENDC = '\033[0m' ENDC = '\033[0m'
def _print_command(cmd): def _print_command(cmd):
"""Print the command to be executed to the console. """Print the command to be executed to the console.
Use a different color so that it can be easily seen amongst the output Use a different color so that it can be easily seen amongst the output
commands. commands.
""" """
if (isinstance(cmd, list)): if (isinstance(cmd, list)):
cmd = ' '.join(cmd) cmd = ' '.join(cmd)
print('{}{}{}'.format(bcolors.OKBLUE, cmd, bcolors.ENDC)) print('{}{}{}'.format(bcolors.OKBLUE, cmd, bcolors.ENDC))
class ManifestEntry(object): class ManifestEntry(object):
"""Represents a single entry in a SQLite manifest.""" """Represents a single entry in a SQLite manifest."""
valid_types = ('C', 'D', 'F', 'P', 'R', 'T', 'U', 'Z') def __init__(self, entry_type, items):
if not len(entry_type) == 1:
def __init__(self, entry_type, items): raise IncorrectType(entry_type)
assert entry_type in ManifestEntry.valid_types self.entry_type = entry_type
self.entry_type = entry_type self.items = items
self.items = items
def get_hash_type(self):
def get_hash_type(self): """Return the type of hash used for this entry."""
"""Return the type of hash used for this entry.""" last_item = self.items[-1]
last_item = self.items[-1] if not all(c in string.hexdigits for c in last_item):
if not all(c in string.hexdigits for c in last_item): print(
print( '"{}" doesn\'t appear to be a hash.'.format(last_item),
'"{}" doesn\'t appear to be a hash.'.format(last_item), file=sys.stderr)
file=sys.stderr) raise UnknownHash()
raise UnknownHash() elif len(last_item) == 40:
elif len(last_item) == 40: return 'sha1'
return 'sha1' elif len(last_item) == 64:
elif len(last_item) == 64: return 'sha3'
return 'sha3' else:
else: raise UnknownHash('Incorrect length {} for {}'.format(
print( len(last_item), last_item))
'Incorrect length {} for {}'.format(len(last_item), last_item),
file=sys.stderr) @staticmethod
raise UnknownHash() def calc_hash(data, method):
"""Return the string sha1 or sha3 hash digest for the given data."""
@staticmethod if method == 'sha3':
def calc_hash(fname, method): h = hashlib.sha3_256()
"""Return the string sha3 hash digest for the given file.""" elif method == 'sha1':
with open(fname, 'rb') as input_file: h = hashlib.sha1()
if method == 'sha3': else:
h = hashlib.sha3_256() assert False
elif method == 'sha1': h.update(data)
h = hashlib.sha1() return h.hexdigest()
else:
assert False @staticmethod
h.update(input_file.read()) def calc_file_hash(fname, method):
return h.hexdigest() """Return the string sha1 or sha3 hash digest for the given file."""
with open(fname, 'rb') as input_file:
def update_file_hash(self): return ManifestEntry.calc_hash(input_file.read(), method)
"""Calculates a new file hash for this entry."""
self.items[1] = ManifestEntry.calc_hash(self.items[0], self.get_hash_type()) def update_file_hash(self):
"""Calculates a new file hash for this entry."""
def __str__(self): self.items[1] = ManifestEntry.calc_file_hash(self.items[0],
return '{} {}'.format(self.entry_type, ' '.join(self.items)) self.get_hash_type())
def __str__(self):
return '{} {}'.format(self.entry_type, ' '.join(self.items))
class Manifest(object): class Manifest(object):
"""A deserialized SQLite manifest.""" """A deserialized SQLite manifest."""
def __init__(self): def __init__(self):
self.entries = [] self.entries = []
def find_file_entry(self, fname): def find_file_entry(self, fname):
"""Given a file path return the entry. Returns None if none found.""" """Given a file path return the entry. Returns None if none found."""
for entry in self.entries: for entry in self.entries:
if entry.entry_type == 'F' and entry.items[0] == fname: if entry.entry_type == 'F' and entry.items[0] == fname:
return entry return entry
return None return None
class ManifestSerializer(object): class ManifestSerializer(object):
"""De/serialize SQLite manifests.""" """De/serialize SQLite manifests."""
@staticmethod @staticmethod
def read(fname): def read_stream(input_stream):
"""Deserialze a manifest file and return a Manifest object.""" """Deserialize a manifest from an input stream and return a Manifest
_manifest = Manifest() object."""
with open(fname) as input_file: _manifest = Manifest()
for line in input_file.readlines(): for line in input_stream.readlines():
items = line.split() items = line.split()
if not items: if not items:
continue continue
_manifest.entries.append(ManifestEntry(items[0], items[1:])) _manifest.entries.append(ManifestEntry(items[0], items[1:]))
return _manifest return _manifest
@staticmethod @staticmethod
def write(fname, manifest): def read_file(fname):
"""Serialize the given manifest to the specified file.""" """Deserialize a manifest file and return a Manifest object."""
with open(fname, 'w') as output_file: with open(fname) as input_stream:
for entry in manifest.entries: return ManifestSerializer.read_stream(input_stream)
print(str(entry), file=output_file)
@staticmethod
def write_stream(manifest, output_stream):
"""Serialize the given manifest to the given stream."""
for entry in manifest.entries:
print(str(entry), file=output_stream)
@staticmethod
def write_file(manifest, fname):
"""Serialize the given manifest to the specified file."""
with open(fname, 'w') as output_stream:
ManifestSerializer.write_stream(manifest, output_stream)
class Git(object): class Git(object):
@staticmethod
@staticmethod def _get_status():
def _get_status(): changes = []
changes = [] for line in subprocess.check_output(['git', 'status',
for line in subprocess.check_output(['git', 'status', '--porcelain']).splitlines():
'--porcelain']).splitlines(): changes.append(line.decode('utf-8'))
changes.append(line.decode('utf-8')) return changes
return changes
@staticmethod
@staticmethod def get_staged_changes():
def get_staged_changes(): changes = []
changes = [] for line in Git._get_status():
for line in Git._get_status(): entry = line[0:2]
entry = line[0:2] if entry == 'M ':
if entry == 'M ': changes.append(line.split()[1])
changes.append(line.split()[1]) return changes
return changes
@staticmethod
@staticmethod def get_unstaged_changes():
def get_unstaged_changes(): changes = []
changes = [] for line in Git._get_status():
for line in Git._get_status(): entry = line[0:2]
entry = line[0:2] if entry == ' M':
if entry == ' M': changes.append(line.split()[1])
changes.append(line.split()[1]) return changes
return changes
@staticmethod
@staticmethod def get_unmerged_changes():
def get_unmerged_changes(): changes = []
changes = [] for line in Git._get_status():
for line in Git._get_status(): entry = line[0:2]
entry = line[0:2] if entry == 'UU':
if entry == 'UU': changes.append(line.split()[1])
changes.append(line.split()[1]) return changes
return changes
class CherryPicker(object): class CherryPicker(object):
"""Class to cherry pick commits in a SQLite Git repository.""" """Class to cherry pick commits in a SQLite Git repository."""
def __init__(self): # The binary file extenions for files committed to the SQLite repository.
self._print_cmds = True # This is used as a simple way of detecting files that cannot (simply) be
self._update_amangamation = True # resolved in a merge conflict. This script will automatically ignore
# all conflicted files with any of these extensions. If, in the future, new
def _take_head_version(self, file_path): # binary types are added then a conflict will arise during cherry-pick and
subprocess.call( # the user will need to resolve it.
'git show HEAD:{} > {}'.format(file_path, file_path), shell=True) binary_extensions = (
subprocess.call('git add {}'.format(file_path), shell=True) '.data',
'.db',
@staticmethod '.ico',
def _is_binary_file(file_path): '.jpg',
_, file_extension = os.path.splitext(file_path) '.png',
return file_extension == '.db' )
@staticmethod def __init__(self):
def _append_cherry_pick_comments(comments): self._print_cmds = True
# TODO(cmumford): Figure out how to append comments on cherry picks self._update_amangamation = True
pass
def _take_head_version(self, file_path):
subprocess.call(
'git show HEAD:{} > {}'.format(file_path, file_path), shell=True)
subprocess.call('git add {}'.format(file_path), shell=True)
@staticmethod
def _is_binary_file(file_path):
_, file_extension = os.path.splitext(file_path)
return file_extension in CherryPicker.binary_extensions
@staticmethod
def _append_cherry_pick_comments(comments):
# TODO(cmumford): Figure out how to append comments on cherry picks
pass
def _cherry_pick_git_commit(self, commit_id):
"""Cherry-pick a given Git commit into the current branch."""
cmd = ['git', 'cherry-pick', '-x', commit_id]
if self._print_cmds:
_print_command(' '.join(cmd))
returncode = subprocess.call(cmd)
# The manifest and manifest.uuid contain Fossil hashes. Restore to
# HEAD version and update only when all conflicts have been resolved.
comments = None
self._take_head_version('manifest')
self._take_head_version('manifest.uuid')
for unmerged_file in Git.get_unmerged_changes():
if CherryPicker._is_binary_file(unmerged_file):
print('{} is a binary file, keeping branch version.'.format(
unmerged_file))
self._take_head_version(unmerged_file)
if not comments:
comments = [
'Cherry-pick notes', '=============================='
]
comments.append(
'{} is binary file (with conflict). Keeping branch version'
.format(unmerged_file))
if comments:
CherryPicker._append_cherry_pick_comments(comments)
self.continue_cherry_pick()
@staticmethod
def _is_git_commit_id(commit_id):
return len(commit_id) == 40
def _find_git_commit_id(self, fossil_commit_id):
cmd = [
'git', '--no-pager', 'log', '--color=never', '--all',
'--pretty=format:%H', '--grep={}'.format(fossil_commit_id),
'origin/master'
]
if self._print_cmds:
_print_command(' '.join(cmd))
for line in subprocess.check_output(cmd).splitlines():
return line.decode('utf-8')
# Not found.
assert False
def _cherry_pick_git_commit(self, commit_id): def cherry_pick(self, commit_id):
"""Cherry-pick a given Git commit into the current branch.""" """Cherry-pick a given commit into the current branch.
cmd = ['git', 'cherry-pick', '-x', commit_id]
if self._print_cmds:
_print_command(' '.join(cmd))
returncode = subprocess.call(cmd)
# The manifest and manifest.uuid contain Fossil hashes. Restore to
# HEAD version and update only when all conflicts have been resolved.
comments = None
self._take_head_version('manifest')
self._take_head_version('manifest.uuid')
for unmerged_file in Git.get_unmerged_changes():
if CherryPicker._is_binary_file(unmerged_file):
print('{} is a binary file, keeping branch version.'.format(
unmerged_file))
self._take_head_version(unmerged_file)
if not comments:
comments = ['Cherry-pick notes', '==============================']
comments.append(
'{} is binary file (with conflict). Keeping branch version'.format(
unmerged_file))
if comments:
CherryPicker._append_cherry_pick_comments(comments)
self.continue_cherry_pick()
@staticmethod
def _is_git_commit_id(commit_id):
return len(commit_id) == 40
def _find_git_commit_id(self, fossil_commit_id):
cmd = [
'git', '--no-pager', 'log', '--color=never', '--all',
'--pretty=format:%H', '--grep={}'.format(fossil_commit_id),
'origin/master'
]
if self._print_cmds:
_print_command(' '.join(cmd))
for line in subprocess.check_output(cmd).splitlines():
return line.decode('utf-8')
# Not found.
assert False
def cherry_pick(self, commit_id):
"""Cherry-pick a given commit into the current branch.
Can cherry-pick a given Git or a Fossil commit. Can cherry-pick a given Git or a Fossil commit.
""" """
if not CherryPicker._is_git_commit_id(commit_id): if not CherryPicker._is_git_commit_id(commit_id):
commit_id = self._find_git_commit_id(commit_id) commit_id = self._find_git_commit_id(commit_id)
self._cherry_pick_git_commit(commit_id) self._cherry_pick_git_commit(commit_id)
def _generate_amalgamation(self): def _generate_amalgamation(self):
for config_name in ['chromium', 'dev']: for config_name in ['chromium', 'dev']:
generate_amalgamation.make_aggregate(config_name) generate_amalgamation.make_aggregate(config_name)
generate_amalgamation.extract_sqlite_api(config_name) generate_amalgamation.extract_sqlite_api(config_name)
def _add_amalgamation(self): def _add_amalgamation(self):
os.chdir(generate_amalgamation._SQLITE_SRC_DIR) os.chdir(generate_amalgamation._SQLITE_SRC_DIR)
for config_name in ['chromium', 'dev']: for config_name in ['chromium', 'dev']:
cmd = [ cmd = [
'git', 'add', 'git', 'add',
generate_amalgamation.get_amalgamation_dir(config_name) generate_amalgamation.get_amalgamation_dir(config_name)
] ]
if self._print_cmds: if self._print_cmds:
_print_command(' '.join(cmd)) _print_command(' '.join(cmd))
subprocess.check_call(cmd) subprocess.check_call(cmd)
def _update_manifests(self): def _update_manifests(self):
"""Update the SQLite's Fossil manifest files. """Update the SQLite's Fossil manifest files.
This isn't strictly necessary as the manifest isn't used during This isn't strictly necessary as the manifest isn't used during
any build, and manifest.uuid is the Fossil commit ID (which any build, and manifest.uuid is the Fossil commit ID (which
has no meaning in a Git repo). However, keeping these updated has no meaning in a Git repo). However, keeping these updated
helps make it more obvious that a commit originated in helps make it more obvious that a commit originated in
Git and not Fossil. Git and not Fossil.
""" """
manifest = ManifestSerializer.read('manifest') manifest = ManifestSerializer.read_file('manifest')
files_not_in_manifest = ('manifest', 'manifest.uuid') files_not_in_manifest = ('manifest', 'manifest.uuid')
for fname in Git.get_staged_changes(): for fname in Git.get_staged_changes():
if fname in files_not_in_manifest: if fname in files_not_in_manifest:
continue continue
entry = manifest.find_file_entry(fname) entry = manifest.find_file_entry(fname)
if not entry: if not entry:
print( print(
'Cannot find manifest entry for "{}"'.format(fname), 'Cannot find manifest entry for "{}"'.format(fname),
file=sys.stderr) file=sys.stderr)
sys.exit(1) sys.exit(1)
manifest.find_file_entry(fname).update_file_hash() manifest.find_file_entry(fname).update_file_hash()
ManifestSerializer.write('manifest', manifest) ManifestSerializer.write_file(manifest, 'manifest')
cmd = ['git', 'add', 'manifest'] cmd = ['git', 'add', 'manifest']
if self._print_cmds: if self._print_cmds:
_print_command(' '.join(cmd)) _print_command(' '.join(cmd))
subprocess.check_call(cmd) subprocess.check_call(cmd)
# manifest.uuid contains the hash from the Fossil repository which # manifest.uuid contains the hash from the Fossil repository which
# doesn't make sense in a Git branch. Just write all zeros. # doesn't make sense in a Git branch. Just write all zeros.
with open('manifest.uuid', 'w') as output_file: with open('manifest.uuid', 'w') as output_file:
print('0' * 64, file=output_file) print('0' * 64, file=output_file)
cmd = ['git', 'add', 'manifest.uuid'] cmd = ['git', 'add', 'manifest.uuid']
if self._print_cmds: if self._print_cmds:
_print_command(' '.join(cmd)) _print_command(' '.join(cmd))
subprocess.check_call(cmd) subprocess.check_call(cmd)
def continue_cherry_pick(self): def continue_cherry_pick(self):
if Git.get_unstaged_changes() or Git.get_unmerged_changes(): if Git.get_unstaged_changes() or Git.get_unmerged_changes():
raise UnstagedFiles() raise UnstagedFiles()
self._update_manifests() self._update_manifests()
if self._update_amangamation: if self._update_amangamation:
self._generate_amalgamation() self._generate_amalgamation()
self._add_amalgamation() self._add_amalgamation()
cmd = ['git', 'cherry-pick', '--continue'] cmd = ['git', 'cherry-pick', '--continue']
if self._print_cmds: if self._print_cmds:
_print_command(' '.join(cmd)) _print_command(' '.join(cmd))
subprocess.check_call(cmd) subprocess.check_call(cmd)
if __name__ == '__main__': if __name__ == '__main__':
desc = 'A script for cherry-picking commits from the SQLite repo.' desc = 'A script for cherry-picking commits from the SQLite repo.'
parser = argparse.ArgumentParser(description=desc) parser = argparse.ArgumentParser(description=desc)
parser.add_argument( parser.add_argument(
'commit', nargs='*', help='The commit ids to cherry pick (in order)') 'commit', nargs='*', help='The commit ids to cherry pick (in order)')
parser.add_argument( parser.add_argument(
'--continue', '--continue',
dest='cont', dest='cont',
action='store_true', action='store_true',
help='Continue the cherry-pick once conflicts have been resolved') help='Continue the cherry-pick once conflicts have been resolved')
namespace = parser.parse_args() namespace = parser.parse_args()
cherry_picker = CherryPicker() cherry_picker = CherryPicker()
if namespace.cont: if namespace.cont:
try: try:
cherry_picker.continue_cherry_pick() cherry_picker.continue_cherry_pick()
sys.exit(0) sys.exit(0)
except UnstagedFiles: except UnstagedFiles:
print('There are still unstaged files to resolve before continuing.') print(
sys.exit(1) 'There are still unstaged files to resolve before continuing.')
num_picked = 0 sys.exit(1)
for commit_id in namespace.commit: num_picked = 0
try: for commit_id in namespace.commit:
cherry_picker.cherry_pick(commit_id) try:
num_picked += 1 cherry_picker.cherry_pick(commit_id)
except UnstagedFiles: num_picked += 1
print('\nThis cherry-pick contains conflicts. Please resolve them ') except UnstagedFiles:
print('(e.g git mergetool) and rerun this script ' print(
'`sqlite_cherry_picker.py --continue`') '\nThis cherry-pick contains conflicts. Please resolve them ')
print('or `git cherry-pick --abort`.') print('(e.g git mergetool) and rerun this script '
if commit_id != namespace.commit[-1]: '`sqlite_cherry_picker.py --continue`')
msg = ('NOTE: You have only successfully cherry-picked {} out of {} ' print('or `git cherry-pick --abort`.')
'commits.') if commit_id != namespace.commit[-1]:
print(msg.format(num_picked, len(namespace.commit))) msg = (
sys.exit(1) 'NOTE: You have only successfully cherry-picked {} out of '
'{} commits.')
print(msg.format(num_picked, len(namespace.commit)))
sys.exit(1)
#!/usr/bin/env python3
# Copyright 2020 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Tests for sqlite_cherry_picker.py.
These tests should be getting picked up by the PRESUBMIT.py in the parent
directory.
"""
from pathlib import Path
import io
import os
import shutil
import tempfile
import unittest
import sqlite_cherry_picker
# pylint: disable=W0212,C0103,C0115,C0116
class CherryPickerUnittest(unittest.TestCase):
def setUp(self):
self.test_root = tempfile.mkdtemp()
def tearDown(self):
if self.test_root:
shutil.rmtree(self.test_root)
def testManifestEntryConstructor(self):
with self.assertRaises(sqlite_cherry_picker.IncorrectType):
entry = sqlite_cherry_picker.ManifestEntry('DD', [
'vsixtest/vsixtest.tcl',
'6a9a6ab600c25a91a7acc6293828957a386a8a93'
])
del entry # unused because exception will be raised.
def testManifestEntryHashType(self):
entry = sqlite_cherry_picker.ManifestEntry('F', [
'vsixtest/vsixtest.tcl', '6a9a6ab600c25a91a7acc6293828957a386a8a93'
])
self.assertEqual(entry.get_hash_type(), 'sha1')
entry = sqlite_cherry_picker.ManifestEntry('F', [
'tool/warnings-clang.sh',
'bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7'
])
self.assertEqual(entry.get_hash_type(), 'sha3')
# test a bad hash which is too short and cannot be identified as either
# a sha1 or sha3 hash.
with self.assertRaises(sqlite_cherry_picker.UnknownHash):
entry = sqlite_cherry_picker.ManifestEntry(
'F', ['file/path.sh', '12345678'])
entry.get_hash_type()
def testManifestEntryHashCalc(self):
data = 'abcdefghijklmnopqrstuvwxyDEFGHIJKLMUVWXYZ0123456789'.encode(
'utf-8')
self.assertEqual(
sqlite_cherry_picker.ManifestEntry.calc_hash(data, 'sha1'),
'e117bfe4bcb1429cf8a0f72f8f4ea322a9a500eb')
self.assertEqual(
sqlite_cherry_picker.ManifestEntry.calc_hash(data, 'sha3'),
'2cb59ee01402c45e7c56008b7ca33d006dbd980a5e222fac3a584f5639a450d7')
def testManifestFindEntry(self):
manifest = sqlite_cherry_picker.Manifest()
self.assertIsNone(manifest.find_file_entry('nonexistent'))
manifest.entries = [
sqlite_cherry_picker.ManifestEntry('F', [
'tool/warnings-clang.sh',
'bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7'
]),
sqlite_cherry_picker.ManifestEntry('T',
['+bgcolor', '*', '#d0c0ff'])
]
entry = manifest.find_file_entry('tool/warnings-clang.sh')
self.assertIsNotNone(entry)
self.assertEqual(entry.items[0], 'tool/warnings-clang.sh')
# Should only find files.
self.assertIsNone(manifest.find_file_entry('+bgcolor'))
def testManifestDeserialize(self):
manfest_path = os.path.join(
os.path.dirname(os.path.realpath(__file__)), 'test_data',
'test_manifest')
manifest_data = Path(manfest_path).read_text()
input_stream = io.StringIO(manifest_data)
manifest = sqlite_cherry_picker.ManifestSerializer.read_stream(
input_stream)
self.assertEqual(len(manifest.entries), 9)
output_stream = io.StringIO()
sqlite_cherry_picker.ManifestSerializer.write_stream(
manifest, output_stream)
self.assertEqual(output_stream.getvalue(), manifest_data)
def testGitHash(self):
valid_git_commit_id = '61c3ca1b1c77bbcaa35f9326decf3658bdb5626a'
self.assertTrue(
sqlite_cherry_picker.CherryPicker._is_git_commit_id(
valid_git_commit_id))
invalid_git_commit_id = 'f3658bdb5626a'
self.assertFalse(
sqlite_cherry_picker.CherryPicker._is_git_commit_id(
invalid_git_commit_id))
def testIsBinaryFile(self):
self.assertTrue(
sqlite_cherry_picker.CherryPicker._is_binary_file(
'path/test.data'))
self.assertTrue(
sqlite_cherry_picker.CherryPicker._is_binary_file('path/test.db'))
self.assertTrue(
sqlite_cherry_picker.CherryPicker._is_binary_file('path/test.ico'))
self.assertTrue(
sqlite_cherry_picker.CherryPicker._is_binary_file('path/test.jpg'))
self.assertTrue(
sqlite_cherry_picker.CherryPicker._is_binary_file('path/test.png'))
self.assertFalse(
sqlite_cherry_picker.CherryPicker._is_binary_file('path/test.c'))
self.assertFalse(
sqlite_cherry_picker.CherryPicker._is_binary_file('path/test.h'))
if __name__ == '__main__':
unittest.main()
C Version\s3.31.1
D 2020-01-27T19:55:54.490
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
R bf075f6bcc1758c5c1ecd13052997456
T +bgcolor * #d0c0ff
T +sym-release *
T +sym-version-3.31.1 *
U drh
Z 7c50801eed3eaef969e028ef5a0a641a
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment