Files
deb-python-reno/reno/tests/test_scanner.py
Doug Hellmann f8fc8f97ff teach the scanner to look at uncommitted files
Update the Scanner to look at staged and unstaged changes to files in
the local copy of the repository. We can't detect unknown files, yet.

Closes-Bug: #1553155
Change-Id: I77ed60e6f8b8f819aabb361f34cf779623907f7b
Signed-off-by: Doug Hellmann <doug@doughellmann.com>
2017-01-04 16:00:26 -05:00

1800 lines
62 KiB
Python

# -*- coding: utf-8 -*-
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from __future__ import unicode_literals
import itertools
import logging
import os.path
import re
import subprocess
import time
import unittest
from dulwich import diff_tree
from dulwich import objects
import fixtures
import mock
from testtools.content import text_content
from reno import config
from reno import create
from reno import scanner
from reno.tests import base
from reno import utils
_SETUP_TEMPLATE = """
import setuptools
try:
import multiprocessing # noqa
except ImportError:
pass
setuptools.setup(
setup_requires=['pbr'],
pbr=True)
"""
_CFG_TEMPLATE = """
[metadata]
name = testpkg
summary = Test Package
[files]
packages =
testpkg
"""
class GPGKeyFixture(fixtures.Fixture):
"""Creates a GPG key for testing.
It's recommended that this be used in concert with a unique home
directory.
"""
def setUp(self):
super(GPGKeyFixture, self).setUp()
tempdir = self.useFixture(fixtures.TempDir())
gnupg_version_re = re.compile('^gpg\s.*\s([\d+])\.([\d+])\.([\d+])')
gnupg_version = utils.check_output(['gpg', '--version'],
cwd=tempdir.path)
for line in gnupg_version.split('\n'):
gnupg_version = gnupg_version_re.match(line)
if gnupg_version:
gnupg_version = (int(gnupg_version.group(1)),
int(gnupg_version.group(2)),
int(gnupg_version.group(3)))
break
else:
if gnupg_version is None:
gnupg_version = (0, 0, 0)
config_file = tempdir.path + '/key-config'
f = open(config_file, 'wt')
try:
if gnupg_version[0] == 2 and gnupg_version[1] >= 1:
f.write("""
%no-protection
%transient-key
""")
f.write("""
%no-ask-passphrase
Key-Type: RSA
Name-Real: Example Key
Name-Comment: N/A
Name-Email: example@example.com
Expire-Date: 2d
Preferences: (setpref)
%commit
""")
finally:
f.close()
# Note that --quick-random (--debug-quick-random in GnuPG 2.x)
# does not have a corresponding preferences file setting and
# must be passed explicitly on the command line instead
if gnupg_version[0] == 1:
gnupg_random = '--quick-random'
elif gnupg_version[0] >= 2:
gnupg_random = '--debug-quick-random'
else:
gnupg_random = ''
cmd = ['gpg', '--gen-key', '--batch']
if gnupg_random:
cmd.append(gnupg_random)
cmd.append(config_file)
subprocess.check_call(
cmd,
cwd=tempdir.path,
# Direct stderr to its own pipe, from which we don't read,
# to quiet the commands.
stderr=subprocess.PIPE,
)
class GitRepoFixture(fixtures.Fixture):
logger = logging.getLogger('git')
def __init__(self, reporoot):
self.reporoot = reporoot
super(GitRepoFixture, self).__init__()
def setUp(self):
super(GitRepoFixture, self).setUp()
self.useFixture(GPGKeyFixture())
os.makedirs(self.reporoot)
self.git('init', '.')
self.git('config', '--local', 'user.email', 'example@example.com')
self.git('config', '--local', 'user.name', 'reno developer')
self.git('config', '--local', 'user.signingkey',
'example@example.com')
def git(self, *args):
self.logger.debug('$ git %s', ' '.join(args))
output = utils.check_output(
['git'] + list(args),
cwd=self.reporoot,
)
self.logger.debug(output)
return output
def commit(self, message='commit message'):
self.git('add', '.')
self.git('commit', '-m', message)
self.git('show', '--pretty=format:%H')
time.sleep(0.1) # force a delay between commits
def add_file(self, name):
with open(os.path.join(self.reporoot, name), 'w') as f:
f.write('adding %s\n' % name)
self.commit('add %s' % name)
class Base(base.TestCase):
logger = logging.getLogger('test')
def _add_notes_file(self, slug='slug', commit=True, legacy=False,
contents='i-am-also-a-template'):
n = self.get_note_num()
if legacy:
basename = '%016x-%s.yaml' % (n, slug)
else:
basename = '%s-%016x.yaml' % (slug, n)
filename = os.path.join(self.reporoot, 'releasenotes', 'notes',
basename)
create._make_note_file(filename, contents)
self.repo.commit('add %s' % basename)
return os.path.join('releasenotes', 'notes', basename)
def _make_python_package(self):
setup_name = os.path.join(self.reporoot, 'setup.py')
with open(setup_name, 'w') as f:
f.write(_SETUP_TEMPLATE)
cfg_name = os.path.join(self.reporoot, 'setup.cfg')
with open(cfg_name, 'w') as f:
f.write(_CFG_TEMPLATE)
pkgdir = os.path.join(self.reporoot, 'testpkg')
os.makedirs(pkgdir)
init = os.path.join(pkgdir, '__init__.py')
with open(init, 'w') as f:
f.write("Test package")
self.repo.commit('add test package')
def setUp(self):
super(Base, self).setUp()
self.fake_logger = self.useFixture(
fixtures.FakeLogger(
format='%(levelname)8s %(name)s %(message)s',
level=logging.DEBUG,
nuke_handlers=True,
)
)
# Older git does not have config --local, so create a temporary home
# directory to permit using git config --global without stepping on
# developer configuration.
self.useFixture(fixtures.TempHomeDir())
self.useFixture(fixtures.NestedTempfile())
self.temp_dir = self.useFixture(fixtures.TempDir()).path
self.reporoot = os.path.join(self.temp_dir, 'reporoot')
self.repo = self.useFixture(GitRepoFixture(self.reporoot))
self.c = config.Config(self.reporoot)
self._counter = itertools.count(1)
self.get_note_num = lambda: next(self._counter)
class BasicTest(Base):
def test_non_python_no_tags(self):
filename = self._add_notes_file()
self.scanner = scanner.Scanner(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
}
self.assertEqual(
{'0.0.0': [filename]},
results,
)
def test_python_no_tags(self):
self._make_python_package()
filename = self._add_notes_file()
self.scanner = scanner.Scanner(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
}
self.assertEqual(
{'0.0.0': [filename]},
results,
)
def test_note_before_tag(self):
filename = self._add_notes_file()
self.repo.add_file('not-a-release-note.txt')
self.repo.git('tag', '-s', '-m', 'first tag', '1.0.0')
self.scanner = scanner.Scanner(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
}
self.assertEqual(
{'1.0.0': [filename]},
results,
)
def test_note_commit_tagged(self):
filename = self._add_notes_file()
self.repo.git('tag', '-s', '-m', 'first tag', '1.0.0')
self.scanner = scanner.Scanner(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
}
self.assertEqual(
{'1.0.0': [filename]},
results,
)
def test_note_commit_after_tag(self):
self._make_python_package()
self.repo.git('tag', '-s', '-m', 'first tag', '1.0.0')
filename = self._add_notes_file()
self.scanner = scanner.Scanner(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
}
self.assertEqual(
{'1.0.0-1': [filename]},
results,
)
def test_other_commit_after_tag(self):
filename = self._add_notes_file()
self.repo.add_file('ignore-1.txt')
self.repo.git('tag', '-s', '-m', 'first tag', '1.0.0')
self.repo.add_file('ignore-2.txt')
self.scanner = scanner.Scanner(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
}
self.assertEqual(
{'1.0.0': [filename]},
results,
)
def test_multiple_notes_after_tag(self):
self._make_python_package()
self.repo.git('tag', '-s', '-m', 'first tag', '1.0.0')
f1 = self._add_notes_file()
f2 = self._add_notes_file()
self.scanner = scanner.Scanner(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
}
self.assertEqual(
{'1.0.0-2': [f1, f2]},
results,
)
def test_multiple_notes_within_tag(self):
self._make_python_package()
f1 = self._add_notes_file(commit=False)
f2 = self._add_notes_file()
self.repo.git('tag', '-s', '-m', 'first tag', '1.0.0')
self.scanner = scanner.Scanner(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
}
self.assertEqual(
{'1.0.0': [f1, f2]},
results,
)
def test_multiple_tags(self):
self._make_python_package()
self.repo.git('tag', '-s', '-m', 'first tag', '1.0.0')
f1 = self._add_notes_file()
self.repo.git('tag', '-s', '-m', 'first tag', '2.0.0')
f2 = self._add_notes_file()
self.scanner = scanner.Scanner(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
}
self.assertEqual(
{'2.0.0': [f1],
'2.0.0-1': [f2],
},
results,
)
def test_rename_file(self):
self._make_python_package()
self.repo.git('tag', '-s', '-m', 'first tag', '1.0.0')
f1 = self._add_notes_file('slug1')
self.repo.git('tag', '-s', '-m', 'first tag', '2.0.0')
f2 = f1.replace('slug1', 'slug2')
self.repo.git('mv', f1, f2)
self.repo.commit('rename note file')
self.scanner = scanner.Scanner(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
}
self.assertEqual(
{'2.0.0': [f2],
},
results,
)
def test_rename_file_sort_earlier(self):
self._make_python_package()
self.repo.git('tag', '-s', '-m', 'first tag', '1.0.0')
f1 = self._add_notes_file('slug1')
self.repo.git('tag', '-s', '-m', 'first tag', '2.0.0')
f2 = f1.replace('slug1', 'slug0')
self.repo.git('mv', f1, f2)
self.repo.commit('rename note file')
self.scanner = scanner.Scanner(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
}
self.assertEqual(
{'2.0.0': [f2],
},
results,
)
def test_edit_file(self):
self._make_python_package()
self.repo.git('tag', '-s', '-m', 'first tag', '1.0.0')
f1 = self._add_notes_file()
self.repo.git('tag', '-s', '-m', 'first tag', '2.0.0')
with open(os.path.join(self.reporoot, f1), 'w') as f:
f.write('---\npreamble: new contents for file')
self.repo.commit('edit note file')
self.scanner = scanner.Scanner(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
}
self.assertEqual(
{'2.0.0': [f1],
},
results,
)
def test_legacy_file(self):
self._make_python_package()
self.repo.git('tag', '-s', '-m', 'first tag', '1.0.0')
f1 = self._add_notes_file('slug1', legacy=True)
self.repo.git('tag', '-s', '-m', 'first tag', '2.0.0')
f2 = f1.replace('slug1', 'slug2')
self.repo.git('mv', f1, f2)
self.repo.commit('rename note file')
self.scanner = scanner.Scanner(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
}
self.assertEqual(
{'2.0.0': [f2],
},
results,
)
def test_rename_legacy_file_to_new(self):
self._make_python_package()
self.repo.git('tag', '-s', '-m', 'first tag', '1.0.0')
f1 = self._add_notes_file('slug1', legacy=True)
self.repo.git('tag', '-s', '-m', 'first tag', '2.0.0')
# Rename the file with the new convention of placing the UUID
# after the slug instead of before.
f2 = f1.replace('0000000000000001-slug1',
'slug1-0000000000000001')
self.repo.git('mv', f1, f2)
self.repo.commit('rename note file')
self.scanner = scanner.Scanner(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
}
self.assertEqual(
{'2.0.0': [f2],
},
results,
)
def test_limit_by_earliest_version(self):
self._make_python_package()
self._add_notes_file()
self.repo.git('tag', '-s', '-m', 'first tag', '1.0.0')
f2 = self._add_notes_file()
self.repo.git('tag', '-s', '-m', 'middle tag', '2.0.0')
f3 = self._add_notes_file()
self.repo.git('tag', '-s', '-m', 'last tag', '3.0.0')
self.c.override(
earliest_version='2.0.0',
)
self.scanner = scanner.Scanner(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
}
self.assertEqual(
{'2.0.0': [f2],
'3.0.0': [f3],
},
results,
)
def test_delete_file(self):
self._make_python_package()
self.repo.git('tag', '-s', '-m', 'first tag', '1.0.0')
f1 = self._add_notes_file('slug1')
f2 = self._add_notes_file('slug2')
self.repo.git('rm', f1)
self.repo.commit('remove note file')
self.repo.git('tag', '-s', '-m', 'first tag', '2.0.0')
self.scanner = scanner.Scanner(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
}
self.assertEqual(
{'2.0.0': [f2],
},
results,
)
def test_rename_then_delete_file(self):
self._make_python_package()
self.repo.git('tag', '-s', '-m', 'first tag', '1.0.0')
f1 = self._add_notes_file('slug1')
f2 = f1.replace('slug1', 'slug2')
self.repo.git('mv', f1, f2)
self.repo.git('status')
self.repo.commit('rename note file')
self.repo.git('rm', f2)
self.repo.commit('remove note file')
f3 = self._add_notes_file('slug3')
self.repo.git('tag', '-s', '-m', 'first tag', '2.0.0')
log_results = self.repo.git('log', '--topo-order',
'--pretty=%H %d',
'--name-only')
self.addDetail('git log', text_content(log_results))
self.scanner = scanner.Scanner(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
}
self.assertEqual(
{'2.0.0': [f3],
},
results,
)
def test_staged_file(self):
# Prove that we can get a file we have staged.
# Start with a standard commit and tag
self._make_python_package()
self.repo.git('tag', '-s', '-m', 'first tag', '1.0.0')
# Now stage a release note
n = self.get_note_num()
basename = 'staged-note-%016x.yaml' % n
filename = os.path.join(self.reporoot, 'releasenotes', 'notes',
basename)
create._make_note_file(filename, 'staged note')
self.repo.git('add', filename)
status_results = self.repo.git('status')
self.addDetail('git status', text_content(status_results))
# Now run the scanner
self.scanner = scanner.Scanner(self.c)
raw_results = self.scanner.get_notes_by_version()
self.assertEqual(
{'*working-copy*': [
(os.path.join('releasenotes', 'notes', basename),
None)],
},
raw_results,
)
@unittest.skip('dulwich does not know how to identify new files')
def test_added_tagged_not_staged(self):
# Prove that we can get a file we have created but not staged.
# Start with a standard commit and tag
self._make_python_package()
self.repo.git('tag', '-s', '-m', 'first tag', '1.0.0')
# Now create a note without staging it
n = self.get_note_num()
basename = 'staged-note-%016x.yaml' % n
filename = os.path.join(self.reporoot, 'releasenotes', 'notes',
basename)
create._make_note_file(filename, 'staged note')
status_results = self.repo.git('status')
self.addDetail('git status', text_content(status_results))
# Now run the scanner
self.scanner = scanner.Scanner(self.c)
raw_results = self.scanner.get_notes_by_version()
# Take the staged version of the file, but associate it with
# tagged version 1.0.0 because the file was added before that
# version.
self.assertEqual(
{'1.0.0': [(os.path.join('releasenotes', 'notes', basename),
None)],
},
raw_results,
)
def test_modified_tagged_not_staged(self):
# Prove that we can get a file we have changed but not staged.
# Start with a standard commit and tag
self._make_python_package()
f1 = self._add_notes_file('slug1')
self.repo.git('tag', '-s', '-m', 'first tag', '1.0.0')
# Now modify the note
fullpath = os.path.join(self.repo.reporoot, f1)
with open(fullpath, 'w') as f:
f.write('modified first note')
status_results = self.repo.git('status')
self.addDetail('git status', text_content(status_results))
# Now run the scanner
self.scanner = scanner.Scanner(self.c)
raw_results = self.scanner.get_notes_by_version()
# Take the staged version of the file, but associate it with
# tagged version 1.0.0 because the file was added before that
# version.
self.assertEqual(
{'1.0.0': [(f1, None)],
},
raw_results,
)
class FileContentsTest(Base):
def test_basic_file(self):
# Prove that we can get a file we have committed.
f1 = self._add_notes_file(contents='well-known-contents')
r = scanner.RenoRepo(self.reporoot)
contents = r.get_file_at_commit(f1, 'HEAD')
self.assertEqual(
b'well-known-contents',
contents,
)
def test_no_such_file(self):
# Returns None when the file does not exist at all.
# (we have to commit something, otherwise there is no HEAD)
self._add_notes_file(contents='well-known-contents')
r = scanner.RenoRepo(self.reporoot)
contents = r.get_file_at_commit('no-such-dir/no-such-file', 'HEAD')
self.assertEqual(
None,
contents,
)
def test_edit_file_and_commit(self):
# Prove that we can edit a file and see the changes.
f1 = self._add_notes_file(contents='initial-contents')
with open(os.path.join(self.reporoot, f1), 'w') as f:
f.write('new contents for file')
self.repo.commit('edit note file')
r = scanner.RenoRepo(self.reporoot)
contents = r.get_file_at_commit(f1, 'HEAD')
self.assertEqual(
b'new contents for file',
contents,
)
def test_earlier_version_of_edited_file(self):
# Prove that we are not always just returning the most current
# version of a file.
f1 = self._add_notes_file(contents='initial-contents')
with open(os.path.join(self.reporoot, f1), 'w') as f:
f.write('new contents for file')
self.repo.commit('edit note file')
self.scanner = scanner.Scanner(self.c)
r = scanner.RenoRepo(self.reporoot)
head = r.head()
parent = r.get_parents(head)[0]
parent = parent.decode('ascii')
contents = r.get_file_at_commit(f1, parent)
self.assertEqual(
b'initial-contents',
contents,
)
def test_edit_file_without_commit(self):
# Prove we are not picking up the contents from the local
# filesystem outside of the git history.
f1 = self._add_notes_file(contents='initial-contents')
with open(os.path.join(self.reporoot, f1), 'w') as f:
f.write('new contents for file')
r = scanner.RenoRepo(self.reporoot)
contents = r.get_file_at_commit(f1, 'HEAD')
self.assertEqual(
b'initial-contents',
contents,
)
def test_staged_file(self):
# Prove we are not picking up the contents from the local
# filesystem outside of the git history.
f1 = self._add_notes_file(contents='initial-contents')
with open(os.path.join(self.reporoot, f1), 'w') as f:
f.write('new contents for file')
r = scanner.RenoRepo(self.reporoot)
contents = r.get_file_at_commit(f1, None)
self.assertEqual(
'new contents for file',
contents,
)
class PreReleaseTest(Base):
def test_alpha(self):
self._make_python_package()
self.repo.git('tag', '-s', '-m', 'first tag', '1.0.0.0a1')
f1 = self._add_notes_file('slug1')
self.repo.git('tag', '-s', '-m', 'first tag', '1.0.0.0a2')
self.scanner = scanner.Scanner(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
}
self.assertEqual(
{'1.0.0.0a2': [f1],
},
results,
)
def test_beta(self):
self._make_python_package()
self.repo.git('tag', '-s', '-m', 'first tag', '1.0.0.0b1')
f1 = self._add_notes_file('slug1')
self.repo.git('tag', '-s', '-m', 'first tag', '1.0.0.0b2')
self.scanner = scanner.Scanner(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
}
self.assertEqual(
{'1.0.0.0b2': [f1],
},
results,
)
def test_release_candidate(self):
self._make_python_package()
self.repo.git('tag', '-s', '-m', 'first tag', '1.0.0.0rc1')
f1 = self._add_notes_file('slug1')
self.repo.git('tag', '-s', '-m', 'first tag', '1.0.0.0rc2')
self.scanner = scanner.Scanner(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
}
self.assertEqual(
{'1.0.0.0rc2': [f1],
},
results,
)
def test_collapse(self):
files = []
self._make_python_package()
files.append(self._add_notes_file('slug1'))
self.repo.git('tag', '-s', '-m', 'alpha tag', '1.0.0.0a1')
files.append(self._add_notes_file('slug2'))
self.repo.git('tag', '-s', '-m', 'beta tag', '1.0.0.0b1')
files.append(self._add_notes_file('slug3'))
self.repo.git('tag', '-s', '-m', 'release candidate tag', '1.0.0.0rc1')
files.append(self._add_notes_file('slug4'))
self.repo.git('tag', '-s', '-m', 'full release tag', '1.0.0')
self.c.override(
collapse_pre_releases=True,
)
self.scanner = scanner.Scanner(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
}
self.assertEqual(
{'1.0.0': files,
},
results,
)
def test_collapse_without_full_release(self):
self._make_python_package()
f1 = self._add_notes_file('slug1')
self.repo.git('tag', '-s', '-m', 'alpha tag', '1.0.0.0a1')
f2 = self._add_notes_file('slug2')
self.repo.git('tag', '-s', '-m', 'beta tag', '1.0.0.0b1')
f3 = self._add_notes_file('slug3')
self.repo.git('tag', '-s', '-m', 'release candidate tag', '1.0.0.0rc1')
self.c.override(
collapse_pre_releases=True,
)
self.scanner = scanner.Scanner(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
}
self.assertEqual(
{'1.0.0.0a1': [f1],
'1.0.0.0b1': [f2],
'1.0.0.0rc1': [f3],
},
results,
)
def test_collapse_without_notes(self):
self._make_python_package()
self.repo.git('tag', '-s', '-m', 'earlier tag', '0.1.0')
f1 = self._add_notes_file('slug1')
self.repo.git('tag', '-s', '-m', 'alpha tag', '1.0.0.0a1')
f2 = self._add_notes_file('slug2')
self.repo.git('tag', '-s', '-m', 'beta tag', '1.0.0.0b1')
f3 = self._add_notes_file('slug3')
self.repo.git('tag', '-s', '-m', 'release candidate tag', '1.0.0.0rc1')
self.c.override(
collapse_pre_releases=True,
)
self.scanner = scanner.Scanner(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
}
self.assertEqual(
{'1.0.0.0a1': [f1],
'1.0.0.0b1': [f2],
'1.0.0.0rc1': [f3],
},
results,
)
class MergeCommitTest(Base):
def test_1(self):
# Create changes on master and in the branch
# in order so the history is "normal"
n1 = self._add_notes_file()
self.repo.git('tag', '-s', '-m', 'first tag', '1.0.0')
self.repo.git('checkout', '-b', 'test_merge_commit')
n2 = self._add_notes_file()
self.repo.git('checkout', 'master')
self.repo.add_file('ignore-1.txt')
# Merge the branch into master.
self.repo.git('merge', '--no-ff', 'test_merge_commit')
time.sleep(0.1) # force a delay between commits
self.repo.add_file('ignore-2.txt')
self.repo.git('tag', '-s', '-m', 'second tag', '2.0.0')
self.scanner = scanner.Scanner(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
}
self.assertEqual(
{'1.0.0': [n1],
'2.0.0': [n2]},
results,
)
self.assertEqual(
['2.0.0', '1.0.0'],
list(raw_results.keys()),
)
def test_2(self):
# Create changes on the branch before the tag into which it is
# actually merged.
self.repo.add_file('ignore-0.txt')
self.repo.git('checkout', '-b', 'test_merge_commit')
n1 = self._add_notes_file()
self.repo.git('checkout', 'master')
n2 = self._add_notes_file()
self.repo.git('tag', '-s', '-m', 'first tag', '1.0.0')
self.repo.add_file('ignore-1.txt')
# Merge the branch into master.
self.repo.git('merge', '--no-ff', 'test_merge_commit')
time.sleep(0.1) # force a delay between commits
self.repo.git('show')
self.repo.add_file('ignore-2.txt')
self.repo.git('tag', '-s', '-m', 'second tag', '2.0.0')
self.scanner = scanner.Scanner(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
}
self.assertEqual(
{'1.0.0': [n2],
'2.0.0': [n1]},
results,
)
self.assertEqual(
['2.0.0', '1.0.0'],
list(raw_results.keys()),
)
def test_3(self):
# Create changes on the branch before the tag into which it is
# actually merged, with another tag in between the time of the
# commit and the time of the merge. This should reflect the
# order of events described in bug #1522153.
self.repo.add_file('ignore-0.txt')
self.repo.git('checkout', '-b', 'test_merge_commit')
n1 = self._add_notes_file()
self.repo.git('checkout', 'master')
n2 = self._add_notes_file()
self.repo.git('tag', '-s', '-m', 'first tag', '1.0.0')
self.repo.add_file('ignore-1.txt')
self.repo.git('tag', '-s', '-m', 'second tag', '1.1.0')
self.repo.git('merge', '--no-ff', 'test_merge_commit')
time.sleep(0.1) # force a delay between commits
self.repo.add_file('ignore-2.txt')
self.repo.git('tag', '-s', '-m', 'third tag', '2.0.0')
self.repo.add_file('ignore-3.txt')
self.scanner = scanner.Scanner(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
}
# Since the 1.1.0 tag has no notes files, it does not appear
# in the output. It's only there to trigger the bug as it was
# originally reported.
self.assertEqual(
{'1.0.0': [n2],
'2.0.0': [n1]},
results,
)
self.assertEqual(
['2.0.0', '1.0.0'],
list(raw_results.keys()),
)
def test_4(self):
# Create changes on the branch before the tag into which it is
# actually merged, with another tag in between the time of the
# commit and the time of the merge. This should reflect the
# order of events described in bug #1522153.
self.repo.add_file('ignore-0.txt')
self.repo.git('checkout', '-b', 'test_merge_commit')
n1 = self._add_notes_file()
self.repo.git('checkout', 'master')
n2 = self._add_notes_file()
self.repo.git('tag', '-s', '-m', 'first tag', '1.0.0')
self.repo.add_file('ignore-1.txt')
n3 = self._add_notes_file()
self.repo.git('tag', '-s', '-m', 'second tag', '1.1.0')
self.repo.git('merge', '--no-ff', 'test_merge_commit')
time.sleep(0.1) # force a delay between commits
self.repo.add_file('ignore-2.txt')
self.repo.git('tag', '-s', '-m', 'third tag', '2.0.0')
self.repo.add_file('ignore-3.txt')
self.scanner = scanner.Scanner(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
}
self.assertEqual(
{'1.0.0': [n2],
'1.1.0': [n3],
'2.0.0': [n1]},
results,
)
self.assertEqual(
['2.0.0', '1.1.0', '1.0.0'],
list(raw_results.keys()),
)
class UniqueIdTest(Base):
def test_legacy(self):
uid = scanner._get_unique_id(
'releasenotes/notes/0000000000000001-slug1.yaml'
)
self.assertEqual('0000000000000001', uid)
def test_modern(self):
uid = scanner._get_unique_id(
'releasenotes/notes/slug1-0000000000000001.yaml'
)
self.assertEqual('0000000000000001', uid)
class BranchBaseTest(Base):
def setUp(self):
super(BranchBaseTest, self).setUp()
self._make_python_package()
self._add_notes_file('slug1')
self.repo.git('tag', '-s', '-m', 'first tag', '1.0.0')
self._add_notes_file('slug2')
self.repo.git('tag', '-s', '-m', 'first tag', '2.0.0')
self._add_notes_file('slug3')
self.repo.git('tag', '-s', '-m', 'first tag', '3.0.0')
self.repo.git('checkout', '2.0.0')
self.repo.git('branch', 'not-master')
self.repo.git('checkout', 'master')
self.scanner = scanner.Scanner(self.c)
def test_current_branch_no_extra_commits(self):
# checkout the branch and then ask for its base
self.repo.git('checkout', 'not-master')
self.assertEqual(
'2.0.0',
self.scanner._get_branch_base('not-master'),
)
def test_current_branch_extra_commit(self):
# checkout the branch and then ask for its base
self.repo.git('checkout', 'not-master')
self._add_notes_file('slug4')
self.assertEqual(
'2.0.0',
self.scanner._get_branch_base('not-master'),
)
def test_alternate_branch_no_extra_commits(self):
# checkout master and then ask for the alternate branch base
self.repo.git('checkout', 'master')
self.assertEqual(
'2.0.0',
self.scanner._get_branch_base('not-master'),
)
def test_alternate_branch_extra_commit(self):
# checkout master and then ask for the alternate branch base
self.repo.git('checkout', 'not-master')
self._add_notes_file('slug4')
self.repo.git('checkout', 'master')
self.assertEqual(
'2.0.0',
self.scanner._get_branch_base('not-master'),
)
def test_no_tag_at_base(self):
# remove the tag at the branch point
self.repo.git('tag', '-d', '2.0.0')
self._add_notes_file('slug4')
self.repo.git('checkout', 'master')
self.assertIsNone(
self.scanner._get_branch_base('not-master')
)
class BranchTest(Base):
def setUp(self):
super(BranchTest, self).setUp()
self._make_python_package()
self.f1 = self._add_notes_file('slug1')
self.repo.git('tag', '-s', '-m', 'first tag', '1.0.0')
self.f2 = self._add_notes_file('slug2')
self.repo.git('tag', '-s', '-m', 'first tag', '2.0.0')
self._add_notes_file('slug3')
self.repo.git('tag', '-s', '-m', 'first tag', '3.0.0')
def test_files_current_branch(self):
self.repo.git('checkout', '2.0.0')
self.repo.git('checkout', '-b', 'stable/2')
f21 = self._add_notes_file('slug21')
log_text = self.repo.git('log', '--decorate')
self.addDetail('git log', text_content(log_text))
self.scanner = scanner.Scanner(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
}
self.assertEqual(
{
'1.0.0': [self.f1],
'2.0.0': [self.f2],
'2.0.0-1': [f21],
},
results,
)
def test_files_stable_from_master(self):
self.repo.git('checkout', '2.0.0')
self.repo.git('checkout', '-b', 'stable/2')
f21 = self._add_notes_file('slug21')
self.repo.git('checkout', 'master')
log_text = self.repo.git('log', '--pretty=%x00%H %d', '--name-only',
'stable/2')
self.addDetail('git log', text_content(log_text))
self.c.override(
branch='stable/2',
)
self.scanner = scanner.Scanner(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
}
self.assertEqual(
{
'2.0.0': [self.f2],
'2.0.0-1': [f21],
},
results,
)
def test_files_stable_from_master_no_stop_base(self):
self.repo.git('checkout', '2.0.0')
self.repo.git('checkout', '-b', 'stable/2')
f21 = self._add_notes_file('slug21')
self.repo.git('checkout', 'master')
log_text = self.repo.git('log', '--pretty=%x00%H %d', '--name-only',
'stable/2')
self.addDetail('git log', text_content(log_text))
self.c.override(
branch='stable/2',
)
self.c.override(
stop_at_branch_base=False,
)
self.scanner = scanner.Scanner(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
}
self.assertEqual(
{
'1.0.0': [self.f1],
'2.0.0': [self.f2],
'2.0.0-1': [f21],
},
results,
)
def test_pre_release_branch_no_collapse(self):
f4 = self._add_notes_file('slug4')
self.repo.git('tag', '-s', '-m', 'pre-release', '4.0.0.0rc1')
# Add a commit on master after the tag
self._add_notes_file('slug5')
# Move back to the tag and create the branch
self.repo.git('checkout', '4.0.0.0rc1')
self.repo.git('checkout', '-b', 'stable/4')
# Create a commit on the branch
f41 = self._add_notes_file('slug41')
log_text = self.repo.git(
'log', '--pretty=%x00%H %d', '--name-only', '--graph',
'--all', '--decorate',
)
self.addDetail('git log', text_content(log_text))
rev_list = self.repo.git('rev-list', '--first-parent',
'^stable/4', 'master')
self.addDetail('rev-list', text_content(rev_list))
self.c.override(
branch='stable/4',
collapse_pre_releases=False,
)
self.scanner = scanner.Scanner(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
}
self.assertEqual(
{
'4.0.0.0rc1': [f4],
'4.0.0.0rc1-1': [f41],
},
results,
)
def test_pre_release_branch_collapse(self):
f4 = self._add_notes_file('slug4')
self.repo.git('tag', '-s', '-m', 'pre-release', '4.0.0.0rc1')
# Add a commit on master after the tag
self._add_notes_file('slug5')
# Move back to the tag and create the branch
self.repo.git('checkout', '4.0.0.0rc1')
self.repo.git('checkout', '-b', 'stable/4')
# Create a commit on the branch
f41 = self._add_notes_file('slug41')
self.repo.git('tag', '-s', '-m', 'release', '4.0.0')
log_text = self.repo.git(
'log', '--pretty=%x00%H %d', '--name-only', '--graph',
'--all', '--decorate',
)
self.addDetail('git log', text_content(log_text))
rev_list = self.repo.git('rev-list', '--first-parent',
'^stable/4', 'master')
self.addDetail('rev-list', text_content(rev_list))
self.c.override(
branch='stable/4',
collapse_pre_releases=True,
)
self.scanner = scanner.Scanner(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
}
self.assertEqual(
{
'4.0.0': [f4, f41],
},
results,
)
def test_pre_release_note_before_branch(self):
f4 = self._add_notes_file('slug4')
self.repo.git('tag', '-s', '-m', 'beta', '4.0.0.0b1')
self.repo.add_file('not-a-release-note.txt')
self.repo.git('tag', '-s', '-m', 'pre-release', '4.0.0.0rc1')
# Add a commit on master after the tag
self._add_notes_file('slug5')
# Move back to the tag and create the branch
self.repo.git('checkout', '4.0.0.0rc1')
self.repo.git('checkout', '-b', 'stable/4')
# Create a commit on the branch
f41 = self._add_notes_file('slug41')
self.repo.git('tag', '-s', '-m', 'release', '4.0.0')
log_text = self.repo.git(
'log', '--pretty=%x00%H %d', '--name-only', '--graph',
'--all', '--decorate',
)
self.addDetail('git log', text_content(log_text))
rev_list = self.repo.git('rev-list', '--first-parent',
'^stable/4', 'master')
self.addDetail('rev-list', text_content(rev_list))
self.c.override(
branch='stable/4',
collapse_pre_releases=True,
)
self.scanner = scanner.Scanner(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
}
self.assertEqual(
{
'4.0.0': [f4, f41],
},
results,
)
def test_full_release_branch(self):
f4 = self._add_notes_file('slug4')
self.repo.git('tag', '-s', '-m', 'release', '4.0.0')
# Add a commit on master after the tag
self._add_notes_file('slug5')
# Move back to the tag and create the branch
self.repo.git('checkout', '4.0.0')
self.repo.git('checkout', '-b', 'stable/4')
# Create a commit on the branch
f41 = self._add_notes_file('slug41')
log_text = self.repo.git(
'log', '--pretty=%x00%H %d', '--name-only', '--graph',
'--all', '--decorate',
)
self.addDetail('git log', text_content(log_text))
rev_list = self.repo.git('rev-list', '--first-parent',
'^stable/4', 'master')
self.addDetail('rev-list', text_content(rev_list))
self.c.override(
branch='stable/4',
)
self.scanner = scanner.Scanner(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
}
self.assertEqual(
{
'4.0.0': [f4],
'4.0.0-1': [f41],
},
results,
)
def test_branch_tip_of_master(self):
# We have branched from master, but not added any commits to
# master.
f4 = self._add_notes_file('slug4')
self.repo.git('tag', '-s', '-m', 'release', '4.0.0')
self.repo.git('checkout', '-b', 'stable/4')
# Create a commit on the branch
f41 = self._add_notes_file('slug41')
f42 = self._add_notes_file('slug42')
log_text = self.repo.git(
'log', '--pretty=%x00%H %d', '--name-only', '--graph',
'--all', '--decorate',
)
self.addDetail('git log', text_content(log_text))
rev_list = self.repo.git('rev-list', '--first-parent',
'^stable/4', 'master')
self.addDetail('rev-list', text_content(rev_list))
self.c.override(
branch='stable/4',
)
self.scanner = scanner.Scanner(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
}
self.assertEqual(
{
'4.0.0': [f4],
'4.0.0-2': [f41, f42],
},
results,
)
def test_branch_no_more_commits(self):
# We have branched from master, but not added any commits to
# our branch or to master.
f4 = self._add_notes_file('slug4')
self.repo.git('tag', '-s', '-m', 'release', '4.0.0')
self.repo.git('checkout', '-b', 'stable/4')
# Create a commit on the branch
log_text = self.repo.git(
'log', '--pretty=%x00%H %d', '--name-only', '--graph',
'--all', '--decorate',
)
self.addDetail('git log', text_content(log_text))
rev_list = self.repo.git('rev-list', '--first-parent',
'^stable/4', 'master')
self.addDetail('rev-list', text_content(rev_list))
self.c.override(
branch='stable/4',
)
self.scanner = scanner.Scanner(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
}
self.assertEqual(
{
'4.0.0': [f4],
},
results,
)
def test_remote_branches(self):
self.repo.git('checkout', '2.0.0')
self.repo.git('checkout', '-b', 'stable/2')
self.repo.git('checkout', 'master')
scanner1 = scanner.Scanner(self.c)
head1 = scanner1._get_ref('stable/2')
self.assertIsNotNone(head1)
print('head1', head1)
# Create a second repository by cloning the first.
print(utils.check_output(
['git', 'clone', self.reporoot, 'reporoot2'],
cwd=self.temp_dir,
))
reporoot2 = os.path.join(self.temp_dir, 'reporoot2')
print(utils.check_output(
['git', 'remote', 'update'],
cwd=reporoot2,
))
print(utils.check_output(
['git', 'remote', '-v'],
cwd=reporoot2,
))
print(utils.check_output(
['find', '.git/refs'],
cwd=reporoot2,
))
print(utils.check_output(
['git', 'branch', '-a'],
cwd=reporoot2,
))
c2 = config.Config(reporoot2)
scanner2 = scanner.Scanner(c2)
head2 = scanner2._get_ref('origin/stable/2')
self.assertIsNotNone(head2)
self.assertEqual(head1, head2)
class ScanStopPointTest(Base):
def setUp(self):
super(ScanStopPointTest, self).setUp()
self.scanner = scanner.Scanner(self.c)
def test_invalid_earliest_version(self):
self.assertIsNone(
self.scanner._find_scan_stop_point(
'not.a.numeric.version', [], True),
)
def test_none(self):
self.assertIsNone(
self.scanner._find_scan_stop_point(
None, [], True),
)
def test_unknown_version(self):
self.assertIsNone(
self.scanner._find_scan_stop_point(
'1.0.0', [], True),
)
def test_only_version(self):
self.assertIsNone(
self.scanner._find_scan_stop_point(
'1.0.0', ['1.0.0'], True),
)
def test_beta_collapse(self):
self.assertEqual(
'1.0.0',
self.scanner._find_scan_stop_point(
'2.0.0.0b1', ['2.0.0', '2.0.0.0rc1', '2.0.0.0b1', '1.0.0'],
True),
)
def test_rc_collapse(self):
self.assertEqual(
'1.0.0',
self.scanner._find_scan_stop_point(
'2.0.0.0rc1', ['2.0.0', '2.0.0.0rc1', '2.0.0.0b1', '1.0.0'],
True),
)
def test_rc_no_collapse(self):
self.assertEqual(
'2.0.0.0b1',
self.scanner._find_scan_stop_point(
'2.0.0.0rc1', ['2.0.0', '2.0.0.0rc1', '2.0.0.0b1', '1.0.0'],
False),
)
def test_nova_newton(self):
self.assertEqual(
'13.0.0.0rc3',
self.scanner._find_scan_stop_point(
'14.0.0',
[u'14.0.3', u'14.0.2', u'14.0.1', u'14.0.0.0rc2',
u'14.0.0', u'14.0.0.0rc1', u'14.0.0.0b3', u'14.0.0.0b2',
u'14.0.0.0b1', u'13.0.0.0rc3', u'13.0.0', u'13.0.0.0rc2',
u'13.0.0.0rc1', u'13.0.0.0b3', u'13.0.0.0b2', u'13.0.0.0b1',
u'12.0.0.0rc3', u'12.0.0', u'12.0.0.0rc2', u'12.0.0.0rc1',
u'12.0.0.0b3', u'12.0.0.0b2', u'12.0.0.0b1', u'12.0.0a0',
u'2015.1.0rc3', u'2015.1.0', u'2015.1.0rc2', u'2015.1.0rc1',
u'2015.1.0b3', u'2015.1.0b2', u'2015.1.0b1', u'2014.2.rc2',
u'2014.2', u'2014.2.rc1', u'2014.2.b3', u'2014.2.b2',
u'2014.2.b1', u'2014.1.rc1', u'2014.1.b3', u'2014.1.b2',
u'2014.1.b1', u'2013.2.rc1', u'2013.2.b3', u'2013.1.rc1',
u'folsom-2', u'folsom-1', u'essex-1', u'diablo-2',
u'diablo-1', u'2011.2', u'2011.2rc1', u'2011.2gamma1',
u'2011.1rc1', u'0.9.0'],
True),
)
class GetRefTest(Base):
def setUp(self):
super(GetRefTest, self).setUp()
self._make_python_package()
self.f1 = self._add_notes_file('slug1')
self.repo.git('tag', '-s', '-m', 'first tag', '1.0.0')
self.repo.git('branch', 'stable/foo')
self.repo.git('tag', 'bar-eol')
def test_signed_tag(self):
self.scanner = scanner.Scanner(self.c)
ref = self.scanner._get_ref('1.0.0')
expected = self.scanner._repo.head()
self.assertEqual(expected, ref)
def test_unsigned_tag(self):
self.scanner = scanner.Scanner(self.c)
ref = self.scanner._get_ref('bar-eol')
expected = self.scanner._repo.head()
self.assertEqual(expected, ref)
def test_eol_tag_from_branch(self):
self.scanner = scanner.Scanner(self.c)
ref = self.scanner._get_ref('stable/bar')
expected = self.scanner._repo.head()
self.assertEqual(expected, ref)
def test_head(self):
self.scanner = scanner.Scanner(self.c)
ref = self.scanner._get_ref(None)
expected = self.scanner._repo.head()
self.assertEqual(expected, ref)
class TagsTest(Base):
def setUp(self):
super(TagsTest, self).setUp()
self._make_python_package()
self.f1 = self._add_notes_file('slug1')
self.repo.git('tag', '-s', '-m', 'first tag', '1.0.0')
self.f2 = self._add_notes_file('slug2')
self.repo.git('tag', '-s', '-m', 'first tag', '2.0.0')
self._add_notes_file('slug3')
self.repo.git('tag', '-s', '-m', 'first tag', '3.0.0')
def test_master(self):
self.scanner = scanner.Scanner(self.c)
results = self.scanner._get_tags_on_branch(None)
self.assertEqual(
['3.0.0', '2.0.0', '1.0.0'],
results,
)
def test_get_ref(self):
self.scanner = scanner.Scanner(self.c)
ref = self.scanner._get_ref('3.0.0')
expected = self.scanner._repo.head()
self.assertEqual(expected, ref)
def test_not_master(self):
self.repo.git('checkout', '2.0.0')
self.repo.git('checkout', '-b', 'not-master')
self._add_notes_file('slug4')
self.repo.git('tag', '-s', '-m', 'not on master', '2.0.1')
self.repo.git('checkout', 'master')
self.scanner = scanner.Scanner(self.c)
results = self.scanner._get_tags_on_branch('not-master')
self.assertEqual(
['2.0.1', '2.0.0', '1.0.0'],
results,
)
def test_unsigned(self):
self._add_notes_file('slug4')
self.repo.git('tag', '-m', 'first tag', '4.0.0')
self.scanner = scanner.Scanner(self.c)
results = self.scanner._get_tags_on_branch(None)
self.assertEqual(
['4.0.0', '3.0.0', '2.0.0', '1.0.0'],
results,
)
def test_tagged_tag_annotated(self):
time.sleep(1)
self.repo.git('tag', '-s', '-m', 'fourth tag', '4.0.0', '3.0.0')
self.scanner = scanner.Scanner(self.c)
results = self.scanner._get_tags_on_branch(None)
self.assertEqual(
['3.0.0', '4.0.0', '2.0.0', '1.0.0'],
results,
)
def test_tagged_tag_lightweight(self):
time.sleep(1)
self.repo.git('tag', '-m', 'fourth tag', '4.0.0', '3.0.0')
self.scanner = scanner.Scanner(self.c)
results = self.scanner._get_tags_on_branch(None)
self.assertEqual(
['3.0.0', '4.0.0', '2.0.0', '1.0.0'],
results,
)
class VersionTest(Base):
def setUp(self):
super(VersionTest, self).setUp()
self._make_python_package()
self.f1 = self._add_notes_file('slug1')
self.repo.git('tag', '-s', '-m', 'first tag', '1.0.0')
self.f2 = self._add_notes_file('slug2')
self.repo.git('tag', '-s', '-m', 'second tag', '2.0.0')
self._add_notes_file('slug3')
self.repo.git('tag', '-s', '-m', 'third tag', '3.0.0')
def test_tagged_head(self):
self.scanner = scanner.Scanner(self.c)
results = self.scanner._get_current_version(None)
self.assertEqual(
'3.0.0',
results,
)
def test_head_after_tag(self):
self._add_notes_file('slug4')
self.scanner = scanner.Scanner(self.c)
results = self.scanner._get_current_version(None)
self.assertEqual(
'3.0.0-1',
results,
)
def test_multiple_tags(self):
# The timestamp resolution appears to be 1 second, so sleep to
# ensure distinct timestamps for the 2 tags. In practice it is
# unlikely that anything could apply 2 signed tags within a
# single second (certainly not a person).
time.sleep(1)
self.repo.git('tag', '-s', '-m', 'fourth tag', '4.0.0')
self.scanner = scanner.Scanner(self.c)
results = self.scanner._get_current_version(None)
self.assertEqual(
'4.0.0',
results,
)
class AggregateChangesTest(Base):
def test_ignore(self):
entry = mock.Mock()
n = self.get_note_num()
name = 'prefix/add-%016x' % n # no .yaml extension
entry.commit.id = 'commit-id'
changes = [
diff_tree.TreeChange(
type=diff_tree.CHANGE_ADD,
old=objects.TreeEntry(path=None, mode=None, sha=None),
new=objects.TreeEntry(
path=name.encode('utf-8'),
mode='0222',
sha='not-a-hash',
)
)
]
results = scanner._aggregate_changes(entry, changes, 'prefix')
self.assertEqual(
[],
results,
)
def test_add(self):
entry = mock.Mock()
n = self.get_note_num()
name = 'prefix/add-%016x.yaml' % n
entry.commit.id = 'commit-id'
changes = [
diff_tree.TreeChange(
type=diff_tree.CHANGE_ADD,
old=objects.TreeEntry(path=None, mode=None, sha=None),
new=objects.TreeEntry(
path=name.encode('utf-8'),
mode='0222',
sha='not-a-hash',
)
)
]
results = list(scanner._aggregate_changes(entry, changes, 'prefix'))
self.assertEqual(
[('%016x' % n, 'add', name, 'commit-id')],
results,
)
def test_delete(self):
entry = mock.Mock()
n = self.get_note_num()
name = 'prefix/delete-%016x.yaml' % n
entry.commit.id = 'commit-id'
changes = [
diff_tree.TreeChange(
type=diff_tree.CHANGE_DELETE,
old=objects.TreeEntry(
path=name.encode('utf-8'),
mode='0222',
sha='not-a-hash',
),
new=objects.TreeEntry(path=None, mode=None, sha=None)
)
]
results = list(scanner._aggregate_changes(entry, changes, 'prefix'))
self.assertEqual(
[('%016x' % n, 'delete', name)],
results,
)
def test_change(self):
entry = mock.Mock()
n = self.get_note_num()
name = 'prefix/change-%016x.yaml' % n
entry.commit.id = 'commit-id'
changes = [
diff_tree.TreeChange(
type=diff_tree.CHANGE_MODIFY,
old=objects.TreeEntry(
path=name.encode('utf-8'),
mode='0222',
sha='old-sha',
),
new=objects.TreeEntry(
path=name.encode('utf-8'),
mode='0222',
sha='new-sha',
),
)
]
results = list(scanner._aggregate_changes(entry, changes, 'prefix'))
self.assertEqual(
[('%016x' % n, 'modify', name, 'commit-id')],
results,
)
def test_add_then_delete(self):
entry = mock.Mock()
n = self.get_note_num()
new_name = 'prefix/new-%016x.yaml' % n
old_name = 'prefix/old-%016x.yaml' % n
entry.commit.id = 'commit-id'
changes = [
diff_tree.TreeChange(
type=diff_tree.CHANGE_ADD,
old=objects.TreeEntry(path=None, mode=None, sha=None),
new=objects.TreeEntry(
path=new_name.encode('utf-8'),
mode='0222',
sha='new-hash',
)
),
diff_tree.TreeChange(
type=diff_tree.CHANGE_DELETE,
old=objects.TreeEntry(
path=old_name.encode('utf-8'),
mode='0222',
sha='old-hash',
),
new=objects.TreeEntry(path=None, mode=None, sha=None)
)
]
results = list(scanner._aggregate_changes(entry, changes, 'prefix'))
self.assertEqual(
[('%016x' % n, 'rename', old_name, new_name, 'commit-id')],
results,
)
def test_delete_then_add(self):
entry = mock.Mock()
n = self.get_note_num()
new_name = 'prefix/new-%016x.yaml' % n
old_name = 'prefix/old-%016x.yaml' % n
entry.commit.id = 'commit-id'
changes = [
diff_tree.TreeChange(
type=diff_tree.CHANGE_DELETE,
old=objects.TreeEntry(
path=old_name.encode('utf-8'),
mode='0222',
sha='old-hash',
),
new=objects.TreeEntry(path=None, mode=None, sha=None)
),
diff_tree.TreeChange(
type=diff_tree.CHANGE_ADD,
old=objects.TreeEntry(path=None, mode=None, sha=None),
new=objects.TreeEntry(
path=new_name.encode('utf-8'),
mode='0222',
sha='new-hash',
)
),
]
results = list(scanner._aggregate_changes(entry, changes, 'prefix'))
self.assertEqual(
[('%016x' % n, 'rename', old_name, new_name, 'commit-id')],
results,
)
def test_tree_changes(self):
# Under some conditions when dulwich sees merge commits,
# changes() returns a list with nested lists. See commit
# cc11da6dcfb1dbaa015e9804b6a23f7872380c1b in this repo for an
# example.
entry = mock.Mock()
n = self.get_note_num()
# The files modified by the commit are actually
# reno/scanner.py, but the fake names are used in this test to
# comply with the rest of the configuration for the scanner.
old_name = 'prefix/old-%016x.yaml' % n
entry.commit.id = 'commit-id'
changes = [[
diff_tree.TreeChange(
type='modify',
old=diff_tree.TreeEntry(
path=old_name.encode('utf-8'),
mode=33188,
sha=b'8247dfdd116fd0e3cc4ba32328e4a3eafd227de6',
),
new=diff_tree.TreeEntry(
path=old_name.encode('utf-8'),
mode=33188,
sha=b'611f3663f54afb1f018a6a8680b6488da50ac340',
),
),
diff_tree.TreeChange(
type='modify',
old=diff_tree.TreeEntry(
path=old_name.encode('utf-8'),
mode=33188,
sha=b'ecb7788066eefa9dc8f110b56360efe7b1140b84',
),
new=diff_tree.TreeEntry(
path=old_name.encode('utf-8'),
mode=33188,
sha=b'611f3663f54afb1f018a6a8680b6488da50ac340',
),
),
]]
results = list(scanner._aggregate_changes(entry, changes, 'prefix'))
self.assertEqual(
[('%016x' % n, 'modify', old_name, 'commit-id'),
('%016x' % n, 'modify', old_name, 'commit-id')],
results,
)