Patman changelog enhancements
Sandbox SPI flash doc update
This commit is contained in:
Tom Rini
2020-05-30 11:37:32 -04:00
16 changed files with 300 additions and 135 deletions

View File

@@ -80,7 +80,6 @@ CONFIG_OF_CONTROL=y
CONFIG_OF_LIVE=y
CONFIG_OF_HOSTFILE=y
CONFIG_DEFAULT_DEVICE_TREE="sandbox64"
CONFIG_SYS_RELOC_GD_ENV_ADDR=y
CONFIG_NETCONSOLE=y
CONFIG_IP_DEFRAG=y
CONFIG_REGMAP=y

View File

@@ -89,7 +89,6 @@ CONFIG_OF_CONTROL=y
CONFIG_OF_LIVE=y
CONFIG_OF_HOSTFILE=y
CONFIG_DEFAULT_DEVICE_TREE="sandbox"
CONFIG_SYS_RELOC_GD_ENV_ADDR=y
CONFIG_NETCONSOLE=y
CONFIG_IP_DEFRAG=y
CONFIG_REGMAP=y

View File

@@ -63,7 +63,6 @@ CONFIG_AMIGA_PARTITION=y
CONFIG_OF_CONTROL=y
CONFIG_OF_HOSTFILE=y
CONFIG_DEFAULT_DEVICE_TREE="sandbox"
CONFIG_SYS_RELOC_GD_ENV_ADDR=y
CONFIG_NETCONSOLE=y
CONFIG_IP_DEFRAG=y
CONFIG_REGMAP=y

View File

@@ -79,7 +79,6 @@ CONFIG_SPL_OF_CONTROL=y
CONFIG_OF_HOSTFILE=y
CONFIG_DEFAULT_DEVICE_TREE="sandbox"
CONFIG_SPL_OF_PLATDATA=y
CONFIG_SYS_RELOC_GD_ENV_ADDR=y
CONFIG_NETCONSOLE=y
CONFIG_IP_DEFRAG=y
CONFIG_SPL_DM=y

View File

@@ -2,59 +2,37 @@ Sandbox SPI/SPI Flash Implementation
====================================
U-Boot supports SPI and SPI flash emulation in sandbox. This must be enabled
using the --spi_sf paramter when starting U-Boot.
via a device tree.
For example:
$ make O=sandbox sandbox_config
$ make O=sandbox
$ ./sandbox/u-boot --spi_sf 0:0:W25Q128:b/chromeos_peach/out/image.bin
The four parameters to spi_sf are:
SPI bus number (typically 0)
SPI chip select number (typically 0)
SPI chip to emulate
File containing emulated data
spi@0 {
#address-cells = <1>;
#size-cells = <0>;
reg = <0 1>;
compatible = "sandbox,spi";
cs-gpios = <0>, <&gpio_a 0>;
spi.bin@0 {
reg = <0>;
compatible = "spansion,m25p16", "jedec,spi-nor";
spi-max-frequency = <40000000>;
sandbox,filename = "spi.bin";
};
};
Supported chips are W25Q16 (2MB), W25Q32 (4MB) and W25Q128 (16MB). Once
U-Boot it started you can use 'sf' commands as normal. For example:
$ ./b/sandbox/u-boot --spi_sf 0:0:W25Q128:b/chromeos_peach/out/image.bin \
-c "sf probe; sf test 0 100000; sf read 0 1000 1000; \
sf erase 1000 1000; sf write 0 1000 1000"
U-Boot 2013.10-00237-gd4e0fdb (Nov 07 2013 - 20:08:15)
DRAM: 128 MiB
Using default environment
In: serial
Out: serial
Err: serial
SF: Detected W25Q128BV with page size 256 Bytes, erase size 4 KiB, total 16 MiB
SPI flash test:
0 erase: 1 ticks, 1024000 KiB/s 8192.000 Mbps
1 check: 2 ticks, 512000 KiB/s 4096.000 Mbps
2 write: 6 ticks, 170666 KiB/s 1365.328 Mbps
3 read: 0 ticks, 1048576000 KiB/s -201326.-592 Mbps
Test passed
0 erase: 1 ticks, 1024000 KiB/s 8192.000 Mbps
1 check: 2 ticks, 512000 KiB/s 4096.000 Mbps
2 write: 6 ticks, 170666 KiB/s 1365.328 Mbps
3 read: 0 ticks, 1048576000 KiB/s -201326.-592 Mbps
SF: 4096 bytes @ 0x1000 Read: OK
SF: 4096 bytes @ 0x1000 Erased: OK
SF: 4096 bytes @ 0x1000 Written: OK
$ dd if=/dev/zero of=spi.bin bs=1M count=2
$ u-boot -T
Since the SPI bus is fully implemented as well as the SPI flash connected to
it, you can also use low-level SPI commands to access the flash. For example
this reads the device ID from the emulated chip:
=> sspi 0 32 9f
FFEF4018
SF: Detected m25p16 with page size 256 Bytes, erase size 64 KiB, total 2 MiB
FF202015
Simon Glass

View File

@@ -316,19 +316,29 @@ SPI Emulation
Sandbox supports SPI and SPI flash emulation.
This is controlled by the spi_sf argument, the format of which is::
The device can be enabled via a device tree, for example::
bus:cs:device:file
spi@0 {
#address-cells = <1>;
#size-cells = <0>;
reg = <0 1>;
compatible = "sandbox,spi";
cs-gpios = <0>, <&gpio_a 0>;
spi.bin@0 {
reg = <0>;
compatible = "spansion,m25p16", "jedec,spi-nor";
spi-max-frequency = <40000000>;
sandbox,filename = "spi.bin";
};
};
bus - SPI bus number
cs - SPI chip select number
device - SPI device emulation name
file - File on disk containing the data
The file must be created in advance::
For example::
$ dd if=/dev/zero of=spi.bin bs=1M count=2
$ u-boot -T
dd if=/dev/zero of=spi.bin bs=1M count=4
./u-boot --spi_sf 0:0:M25P16:spi.bin
Here, you can use "-T" or "-D" option to specify test.dtb or u-boot.dtb,
respectively, or "-d <file>" for your own dtb.
With this setup you can issue SPI flash commands as normal::
@@ -346,22 +356,6 @@ also use low-level SPI commands::
This is issuing a READ_ID command and getting back 20 (ST Micro) part
0x2015 (the M25P16).
Drivers are connected to a particular bus/cs using sandbox's state
structure (see the 'spi' member). A set of operations must be provided
for each driver.
Configuration settings for the curious are:
CONFIG_SANDBOX_SPI_MAX_BUS:
The maximum number of SPI buses supported by the driver (default 1).
CONFIG_SANDBOX_SPI_MAX_CS:
The maximum number of chip selects supported by the driver (default 10).
CONFIG_SPI_IDLE_VAL:
The idle value on the SPI bus
Block Device Emulation
----------------------

View File

@@ -4,12 +4,12 @@
* Written by Simon Glass <sjg@chromium.org>
*/
#include <asm/types.h>
#include <asm/io.h>
#include <common.h>
#include <dm.h>
#include <mapmem.h>
#include <dm/of_access.h>
#include <mapmem.h>
#include <asm/types.h>
#include <asm/io.h>
int dev_read_u32(const struct udevice *dev, const char *propname, u32 *outp)
{

View File

@@ -247,14 +247,37 @@ Series-changes: n
to update the log there and then, knowing that the script will
do the rest.
Commit-changes: n
- This line will not appear in the cover-letter changelog
<blank line>
This tag is like Series-changes, except changes in this changelog will
only appear in the changelog of the commit this tag is in. This is
useful when you want to add notes which may not make sense in the cover
letter. For example, you can have short changes such as "New" or
"Lint".
Cover-changes: n
- This line will only appear in the cover letter
<blank line>
This tag is like Series-changes, except changes in this changelog will
only appear in the cover-letter changelog. This is useful to summarize
changes made with Commit-changes, or to add additional context to
changes.
Patch-cc: Their Name <email>
This copies a single patch to another email address. Note that the
Cc: used by git send-email is ignored by patman, but will be
interpreted by git send-email if you use it.
Series-process-log: sort, uniq
This tells patman to sort and/or uniq the change logs. It is
assumed that each change log entry is only a single line long.
This tells patman to sort and/or uniq the change logs. Changes may be
multiple lines long, as long as each subsequent line of a change begins
with a whitespace character. For example,
- This change
continues onto the next line
- But this change is separate
Use 'sort' to sort the entries, and 'uniq' to include only
unique entries. If omitted, no change log processing is done.
Separate each tag with a comma.
@@ -474,6 +497,33 @@ print out the command line patman would have used.
not later when you can't remember which patch you changed. You can always
go back and change or remove logs from commits.
7. Some mailing lists have size limits and when we add binary contents to
our patches it's easy to exceed the size limits. Use "--no-binary" to
generate patches without any binary contents. You are supposed to include
a link to a git repository in your "Commit-notes", "Series-notes" or
"Cover-letter" for maintainers to fetch the original commit.
8. Patches will have no changelog entries for revisions where they did not
change. For clarity, if there are no changes for this patch in the most
recent revision of the series, a note will be added. For example, a patch
with the following tags in the commit
Series-version: 5
Series-changes: 2
- Some change
Series-changes: 4
- Another change
would have a changelog of
(no changes since v4)
Changes in v4:
- Another change
Changes in v2:
- Some change
Other thoughts
==============

View File

@@ -82,19 +82,33 @@ class TestFunctional(unittest.TestCase):
Series-prefix: RFC
Series-cc: Stefan Brüns <stefan.bruens@rwth-aachen.de>
Cover-letter-cc: Lord Mëlchett <clergy@palace.gov>
Series-version: 2
Series-version: 3
Patch-cc: fred
Series-process-log: sort, uniq
Series-changes: 4
- Some changes
- Multi
line
change
Commit-changes: 2
- Changes only for this commit
Cover-changes: 4
- Some notes for the cover letter
Cover-letter:
test: A test patch series
This is a test of how the cover
leter
letter
works
END
and this in the first commit:
Commit-changes: 2
- second revision change
Series-notes:
some notes
about some things
@@ -202,7 +216,7 @@ class TestFunctional(unittest.TestCase):
expected = '''
This is a test of how the cover
leter
letter
works
some notes
@@ -210,7 +224,11 @@ about some things
from the first commit
Changes in v4:
- Multi
line
change
- Some changes
- Some notes for the cover letter
Simon Glass (2):
pci: Correct cast for sandbox
@@ -237,8 +255,34 @@ Simon Glass (2):
subject = [line for line in lines if line.startswith('Subject')]
self.assertEqual('Subject: [RFC %d/%d]' % (i + 1, count),
subject[0][:18])
# Check that we got our commit notes
start = 0
expected = ''
if i == 0:
# Check that we got our commit notes
self.assertEqual('---', lines[17])
self.assertEqual('Some notes about', lines[18])
self.assertEqual('the first commit', lines[19])
start = 17
expected = '''---
Some notes about
the first commit
(no changes since v2)
Changes in v2:
- second revision change'''
elif i == 1:
start = 17
expected = '''---
Changes in v4:
- Multi
line
change
- Some changes
Changes in v2:
- Changes only for this commit'''
if expected:
expected = expected.splitlines()
self.assertEqual(expected, lines[start:(start+len(expected))])

View File

@@ -254,7 +254,7 @@ def Fetch(git_dir=None, work_tree=None):
if result.return_code != 0:
raise OSError('git fetch: %s' % result.stderr)
def CreatePatches(start, count, series):
def CreatePatches(start, count, ignore_binary, series):
"""Create a series of patches from the top of the current branch.
The patch files are written to the current directory using
@@ -270,6 +270,8 @@ def CreatePatches(start, count, series):
if series.get('version'):
version = '%s ' % series['version']
cmd = ['git', 'format-patch', '-M', '--signoff']
if ignore_binary:
cmd.append('--no-binary')
if series.get('cover'):
cmd.append('--cover-letter')
prefix = series.GetPatchPrefix()

View File

@@ -36,11 +36,11 @@ parser.add_option('-c', '--count', dest='count', type='int',
parser.add_option('-i', '--ignore-errors', action='store_true',
dest='ignore_errors', default=False,
help='Send patches email even if patch errors are found')
parser.add_option('-l', '--limit-cc', dest='limit', type='int',
default=None, help='Limit the cc list to LIMIT entries [default: %default]')
parser.add_option('-m', '--no-maintainers', action='store_false',
dest='add_maintainers', default=True,
help="Don't cc the file maintainers automatically")
parser.add_option('-l', '--limit-cc', dest='limit', type='int',
default=None, help='Limit the cc list to LIMIT entries [default: %default]')
parser.add_option('-n', '--dry-run', action='store_true', dest='dry_run',
default=False, help="Do a dry run (create but don't email patches)")
parser.add_option('-p', '--project', default=project.DetectProject(),
@@ -52,21 +52,24 @@ parser.add_option('-s', '--start', dest='start', type='int',
default=0, help='Commit to start creating patches from (0 = HEAD)')
parser.add_option('-t', '--ignore-bad-tags', action='store_true',
default=False, help='Ignore bad tags / aliases')
parser.add_option('--test', action='store_true', dest='test',
default=False, help='run tests')
parser.add_option('-v', '--verbose', action='store_true', dest='verbose',
default=False, help='Verbose output of errors and warnings')
parser.add_option('-T', '--thread', action='store_true', dest='thread',
default=False, help='Create patches as a single thread')
parser.add_option('--cc-cmd', dest='cc_cmd', type='string', action='store',
default=None, help='Output cc list for patch file (used by git)')
parser.add_option('--no-binary', action='store_true', dest='ignore_binary',
default=False,
help="Do not output contents of changes in binary files")
parser.add_option('--no-check', action='store_false', dest='check_patch',
default=True,
help="Don't check for patch compliance")
parser.add_option('--no-tags', action='store_false', dest='process_tags',
default=True, help="Don't process subject tags as aliaes")
default=True, help="Don't process subject tags as aliases")
parser.add_option('--smtp-server', type='str',
help="Specify the SMTP server to 'git send-email'")
parser.add_option('-T', '--thread', action='store_true', dest='thread',
default=False, help='Create patches as a single thread')
parser.add_option('--test', action='store_true', dest='test',
default=False, help='run tests')
parser.usage += """
@@ -144,7 +147,7 @@ else:
if options.count:
series = patchstream.GetMetaData(options.start, options.count)
cover_fname, args = gitutil.CreatePatches(options.start, options.count,
series)
options.ignore_binary, series)
# Fix up the patch files to our liking, and insert the cover letter
patchstream.FixPatches(series, args)

View File

@@ -24,11 +24,8 @@ re_allowed_after_test = re.compile('^Signed-off-by:')
# Signoffs
re_signoff = re.compile('^Signed-off-by: *(.*)')
# The start of the cover letter
re_cover = re.compile('^Cover-letter:')
# A cover letter Cc
re_cover_cc = re.compile('^Cover-letter-cc: *(.*)')
# Cover letter tag
re_cover = re.compile('^Cover-([a-z-]*): *(.*)')
# Patch series tag
re_series_tag = re.compile('^Series-([a-z-]*): *(.*)')
@@ -48,6 +45,9 @@ re_commit = re.compile('^commit ([0-9a-f]*)$')
# We detect these since checkpatch doesn't always do it
re_space_before_tab = re.compile('^[+].* \t')
# Match indented lines for changes
re_leading_whitespace = re.compile('^\s')
# States we can be in - can we use range() and still have comments?
STATE_MSG_HEADER = 0 # Still in the message header
STATE_PATCH_SUBJECT = 1 # In patch subject (first line of log for a commit)
@@ -65,7 +65,7 @@ class PatchStream:
def __init__(self, series, name=None, is_log=False):
self.skip_blank = False # True to skip a single blank line
self.found_test = False # Found a TEST= line
self.lines_after_test = 0 # MNumber of lines found after TEST=
self.lines_after_test = 0 # Number of lines found after TEST=
self.warn = [] # List of warnings we have collected
self.linenum = 1 # Output line number we are up to
self.in_section = None # Name of start...END section we are in
@@ -73,7 +73,9 @@ class PatchStream:
self.section = [] # The current section...END section
self.series = series # Info about the patch series
self.is_log = is_log # True if indent like git log
self.in_change = 0 # Non-zero if we are in a change list
self.in_change = None # Name of the change list we are in
self.change_version = 0 # Non-zero if we are in a change list
self.change_lines = [] # Lines of the current change
self.blank_count = 0 # Number of blank lines stored up
self.state = STATE_MSG_HEADER # What state are we in?
self.signoff = [] # Contents of signoff line
@@ -124,6 +126,36 @@ class PatchStream:
self.skip_blank = True
self.section = []
def ParseVersion(self, value, line):
"""Parse a version from a *-changes tag
Args:
value: Tag value (part after 'xxx-changes: '
line: Source line containing tag
Returns:
The version as an integer
"""
try:
return int(value)
except ValueError as str:
raise ValueError("%s: Cannot decode version info '%s'" %
(self.commit.hash, line))
def FinalizeChange(self):
"""Finalize a (multi-line) change and add it to the series or commit"""
if not self.change_lines:
return
change = '\n'.join(self.change_lines)
if self.in_change == 'Series':
self.series.AddChange(self.change_version, self.commit, change)
elif self.in_change == 'Cover':
self.series.AddChange(self.change_version, None, change)
elif self.in_change == 'Commit':
self.commit.AddChange(self.change_version, change)
self.change_lines = []
def ProcessLine(self, line):
"""Process a single line of a patch file or commit log
@@ -163,8 +195,8 @@ class PatchStream:
change_id_match = re_change_id.match(line)
commit_tag_match = re_commit_tag.match(line)
cover_match = re_cover.match(line)
cover_cc_match = re_cover_cc.match(line)
signoff_match = re_signoff.match(line)
leading_whitespace_match = re_leading_whitespace.match(line)
tag_match = None
if self.state == STATE_PATCH_HEADER:
tag_match = re_tag.match(line)
@@ -183,8 +215,7 @@ class PatchStream:
# If a tag is detected, or a new commit starts
if series_tag_match or commit_tag_match or change_id_match or \
cover_match or cover_cc_match or signoff_match or \
self.state == STATE_MSG_HEADER:
cover_match or signoff_match or self.state == STATE_MSG_HEADER:
# but we are already in a section, this means 'END' is missing
# for that section, fix it up.
if self.in_section:
@@ -205,8 +236,10 @@ class PatchStream:
# but we are already in a change list, that means a blank line
# is missing, fix it up.
if self.in_change:
self.warn.append("Missing 'blank line' in section 'Series-changes'")
self.in_change = 0
self.warn.append("Missing 'blank line' in section '%s-changes'" % self.in_change)
self.FinalizeChange()
self.in_change = None
self.change_version = 0
# If we are in a section, keep collecting lines until we see END
if self.in_section:
@@ -242,26 +275,35 @@ class PatchStream:
elif self.skip_blank and is_blank:
self.skip_blank = False
# Detect the start of a cover letter section
# Detect Cover-xxx tags
elif cover_match:
self.in_section = 'cover'
self.skip_blank = False
elif cover_cc_match:
value = cover_cc_match.group(1)
self.AddToSeries(line, 'cover-cc', value)
name = cover_match.group(1)
value = cover_match.group(2)
if name == 'letter':
self.in_section = 'cover'
self.skip_blank = False
elif name == 'letter-cc':
self.AddToSeries(line, 'cover-cc', value)
elif name == 'changes':
self.in_change = 'Cover'
self.change_version = self.ParseVersion(value, line)
# If we are in a change list, key collected lines until a blank one
elif self.in_change:
if is_blank:
# Blank line ends this change list
self.in_change = 0
self.FinalizeChange()
self.in_change = None
self.change_version = 0
elif line == '---':
self.in_change = 0
self.FinalizeChange()
self.in_change = None
self.change_version = 0
out = self.ProcessLine(line)
else:
if self.is_log:
self.series.AddChange(self.in_change, self.commit, line)
elif self.is_log:
if not leading_whitespace_match:
self.FinalizeChange()
self.change_lines.append(line)
self.skip_blank = False
# Detect Series-xxx tags
@@ -270,12 +312,8 @@ class PatchStream:
value = series_tag_match.group(2)
if name == 'changes':
# value is the version number: e.g. 1, or 2
try:
value = int(value)
except ValueError as str:
raise ValueError("%s: Cannot decode version info '%s'" %
(self.commit.hash, line))
self.in_change = int(value)
self.in_change = 'Series'
self.change_version = self.ParseVersion(value, line)
else:
self.AddToSeries(line, name, value)
self.skip_blank = True
@@ -297,6 +335,9 @@ class PatchStream:
if name == 'notes':
self.AddToCommit(line, name, value)
self.skip_blank = True
elif name == 'changes':
self.in_change = 'Commit'
self.change_version = self.ParseVersion(value, line)
# Detect the start of a new commit
elif commit_match:
@@ -340,7 +381,7 @@ class PatchStream:
elif line == '---':
self.state = STATE_DIFFS
# Output the tags (signeoff first), then change list
# Output the tags (signoff first), then change list
out = []
log = self.series.MakeChangeLog(self.commit)
out += [line]
@@ -355,6 +396,7 @@ class PatchStream:
def Finalize(self):
"""Close out processing of this patch stream"""
self.FinalizeChange()
self.CloseCommit()
if self.lines_after_test:
self.warn.append('Found %d lines after TEST=' %

View File

@@ -2,6 +2,9 @@
# Copyright (c) 2011 The Chromium OS Authors.
#
from __future__ import print_function
import collections
import itertools
import os
@@ -144,38 +147,65 @@ class Series(dict):
Changes in v4:
- Jog the dial back closer to the widget
Changes in v3: None
Changes in v2:
- Fix the widget
- Jog the dial
etc.
If there are no new changes in a patch, a note will be added
(no changes since v2)
Changes in v2:
- Fix the widget
- Jog the dial
"""
# Collect changes from the series and this commit
changes = collections.defaultdict(list)
for version, changelist in self.changes.items():
changes[version] += changelist
if commit:
for version, changelist in commit.changes.items():
changes[version] += [[commit, text] for text in changelist]
versions = sorted(changes, reverse=True)
newest_version = 1
if 'version' in self:
newest_version = max(newest_version, int(self.version))
if versions:
newest_version = max(newest_version, versions[0])
final = []
process_it = self.get('process_log', '').split(',')
process_it = [item.strip() for item in process_it]
need_blank = False
for change in sorted(self.changes, reverse=True):
for version in versions:
out = []
for this_commit, text in self.changes[change]:
for this_commit, text in changes[version]:
if commit and this_commit != commit:
continue
if 'uniq' not in process_it or text not in out:
out.append(text)
line = 'Changes in v%d:' % change
have_changes = len(out) > 0
if 'sort' in process_it:
out = sorted(out)
have_changes = len(out) > 0
line = 'Changes in v%d:' % version
if have_changes:
out.insert(0, line)
else:
out = [line + ' None']
if need_blank:
out.insert(0, '')
if version < newest_version and len(final) == 0:
out.insert(0, '')
out.insert(0, '(no changes since v%d)' % version)
newest_version = 0
# Only add a new line if we output something
if need_blank:
out.insert(0, '')
need_blank = False
final += out
need_blank = have_changes
if self.changes:
need_blank = need_blank or have_changes
if len(final) > 0:
final.append('')
elif newest_version != 1:
final = ['(no changes since v1)', '']
return final
def DoChecks(self):

View File

@@ -15,6 +15,9 @@ cmd/pci.c:152:11: warning: format %llx expects argument of type
Fix it with a cast.
Signed-off-by: Simon Glass <sjg@chromium.org>
Commit-changes: 2
- Changes only for this commit
Series-notes:
some notes
about some things

View File

@@ -21,13 +21,23 @@ Series-cc: Stefan Brüns <stefan.bruens@rwth-aachen.de>
Cover-letter-cc: Lord Mëlchett <clergy@palace.gov>
Series-version: 3
Patch-cc: fred
Series-process-log: sort, uniq
Series-changes: 4
- Some changes
- Multi
line
change
Commit-changes: 2
- Changes only for this commit
Cover-changes: 4
- Some notes for the cover letter
Cover-letter:
test: A test patch series
This is a test of how the cover
leter
letter
works
END
---

View File

@@ -13,6 +13,9 @@ Date: Sat Apr 15 15:39:08 2017 -0600
Fix it with a cast.
Signed-off-by: Simon Glass <sjg@chromium.org>
Commit-changes: 2
- second revision change
Series-notes:
some notes
about some things
@@ -45,12 +48,22 @@ Date: Sat Apr 15 15:39:08 2017 -0600
Cover-letter-cc: Lord Mëlchett <clergy@palace.gov>
Series-version: 3
Patch-cc: fred
Series-process-log: sort, uniq
Series-changes: 4
- Some changes
- Multi
line
change
Commit-changes: 2
- Changes only for this commit
Cover-changes: 4
- Some notes for the cover letter
Cover-letter:
test: A test patch series
This is a test of how the cover
leter
letter
works
END