From: AKASHI Takahiro <takahiro.akashi@linaro.org>
To: u-boot@lists.denx.de
Subject: [U-Boot] [RFC 1/3] test/py: convert fs-test.sh to pytest
Date: Thu, 30 Aug 2018 15:52:46 +0900 [thread overview]
Message-ID: <20180830065244.GK12252@linaro.org> (raw)
In-Reply-To: <f4ef9cd7-ff41-5455-1095-b1dfc49653a5@gmx.de>
On Wed, Aug 29, 2018 at 11:36:51PM +0200, Heinrich Schuchardt wrote:
> On 08/23/2018 09:25 AM, AKASHI Takahiro wrote:
> > In this commit, the same set of test cases as in test/fs/fs-test.sh
> > is provided using pytest framework.
> > Actually, fs-test.sh provides three variants:"sb" (sb command), "nonfs"
> > (fatxx and etc.) and "fs" (hostfs), and this patch currently supports
> > only "nonfs" variant; So it is not a replacement of fs-test.sh for now.
> >
> > Simple usage:
> > $ py.test test/py/tests/test_fs [<other options>]
> >
> > You may also specify filesystem types to be tested:
> > $ py.test test/py/tests/test_fs --fs-type fat32 [<other options>]
> >
> > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > ---
> > test/py/tests/test_fs/conftest.py | 175 +++++++++++++++++++
> > test/py/tests/test_fs/fstest_defs.py | 10 ++
> > test/py/tests/test_fs/test_basic.py | 246 +++++++++++++++++++++++++++
> > 3 files changed, 431 insertions(+)
> > create mode 100644 test/py/tests/test_fs/conftest.py
> > create mode 100644 test/py/tests/test_fs/fstest_defs.py
> > create mode 100644 test/py/tests/test_fs/test_basic.py
> >
> > diff --git a/test/py/tests/test_fs/conftest.py b/test/py/tests/test_fs/conftest.py
> > new file mode 100644
> > index 000000000000..fefeb4c9663f
> > --- /dev/null
> > +++ b/test/py/tests/test_fs/conftest.py
> > @@ -0,0 +1,175 @@
> > +# SPDX-License-Identifier: GPL-2.0+
> > +# Copyright (c) 2018, Linaro Limited
> > +# Author: Takahiro Akashi <takahiro.akashi@linaro.org>
> > +
> > +import pytest
> > +import re
> > +from subprocess import call, check_call, check_output, CalledProcessError
> > +from fstest_defs import *
> > +
> > +supported_fs_basic = ['fat16', 'fat32', 'ext4']
> > +
> > +#
> > +# Filesystem test specific setup
> > +#
> > +def pytest_addoption(parser):
> > + parser.addoption('--fs-type', action='append', default=None,
> > + help='Targeting Filesystem Types')
> > +
> > +def pytest_configure(config):
> > + global supported_fs_basic
> > +
> > + def intersect(listA, listB):
> > + return [x for x in listA if x in listB]
> > +
> > + supported_fs = config.getoption('fs_type')
> > + if supported_fs:
> > + print("*** FS TYPE modified: %s" % supported_fs)
> > + supported_fs_basic = intersect(supported_fs, supported_fs_basic)
> > +
> > +def pytest_generate_tests(metafunc):
> > + if 'fs_obj_basic' in metafunc.fixturenames:
> > + metafunc.parametrize('fs_obj_basic', supported_fs_basic,
> > + indirect=True, scope='module')
> > +
> > +#
> > +# Helper functions
> > +#
> > +def fstype_to_ubname(fs_type):
> > + if re.match('fat', fs_type):
> > + return 'fat'
> > + else:
> > + return fs_type
> > +
> > +def check_ubconfig(config, fs_type):
> > + if not config.buildconfig.get('config_cmd_%s' % fs_type, None):
> > + pytest.skip('.config feature "CMD_%s" not enabled' % fs_type.upper())
> > + if not config.buildconfig.get('config_%s_write' % fs_type, None):
> > + pytest.skip('.config feature "%s_WRITE" not enabled'
> > + % fs_type.upper())
> > +
> > +def mk_fs(config, fs_type, size, id):
> > + fs_img = '%s.%s.img' % (id, fs_type)
> > + fs_img = config.persistent_data_dir + '/' + fs_img
> > +
> > + if fs_type == 'fat16':
> > + mkfs_opt = '-F 16'
> > + elif fs_type == 'fat32':
> > + mkfs_opt = '-F 32'
> > + else:
> > + mkfs_opt = ''
> > +
> > + if re.match('fat', fs_type):
> > + fs_lnxtype = 'vfat'
> > + else:
> > + fs_lnxtype = fs_type
> > +
> > + count = (size + 1023) / 1024
> > +
> > + try:
> > + check_call('rm -f %s' % fs_img, shell=True)
> > + check_call('dd if=/dev/zero of=%s bs=1K count=%d'
> > + % (fs_img, count), shell=True)
> > + check_call('mkfs.%s %s %s'
> > + % (fs_lnxtype, mkfs_opt, fs_img), shell=True)
> > + return fs_img
> > + except CalledProcessError:
> > + call('rm -f %s' % fs_img, shell=True)
> > + raise
> > +
> > +#
> > +# Fixture for basic fs test
> > +# derived from test/fs/fs-test.sh
> > +#
> > +# NOTE: yield_fixture was deprecated since pytest-3.0
> > + at pytest.yield_fixture()
> > +def fs_obj_basic(request, u_boot_config):
> > + fs_type = request.param
> > + fs_img = ''
> > +
> > + fs_ubtype = fstype_to_ubname(fs_type)
> > + check_ubconfig(u_boot_config, fs_ubtype)
> > +
> > + mount_dir = u_boot_config.persistent_data_dir + '/mnt'
> > + small_file = mount_dir + '/' + SMALL_FILE
> > + big_file = mount_dir + '/' + BIG_FILE
> > + try:
> > +
> > + # 3GiB volume
> > + fs_img = mk_fs(u_boot_config, fs_type, 0xc0000000, '3GB')
> > +
> > + # Mount the image so we can populate it.
> > + check_call('mkdir -p %s' % mount_dir, shell=True)
> > + check_call('sudo mount -o loop,rw %s %s'
> > + % (fs_img, mount_dir), shell=True)
>
> Should I grant sudo to anybody who can commit to U-Boot?
I don't get your point.
I think using "sudo" solely in testing should be allowed.
> Just use exfat-fuse and fuse2fs.
It will not be a good idea to use those tools which are not
provided in every distribution.
I'd like to leave "sudo mount" statement as a backstop.
> > +
> > + # Create a subdirectory.
> > + check_call('sudo mkdir %s/SUBDIR' % mount_dir, shell=True)
> > +
> > + # Create big file in this image.
> > + # Note that we work only on the start 1MB, couple MBs in the 2GB range
> > + # and the last 1 MB of the huge 2.5GB file.
> > + # So, just put random values only in those areas.
> > + check_call('sudo dd if=/dev/urandom of=%s bs=1M count=1'
> > + % big_file, shell=True)
>
>
> No need to use sudo to create a file.
Along with a change above, succeeding "sudo's" will be removed.
Thanks,
-Takahiro AKASHI
> Best regards
>
> Heinrich
>
> > + check_call('sudo dd if=/dev/urandom of=%s bs=1M count=2 seek=2047'
> > + % big_file, shell=True)
> > + check_call('sudo dd if=/dev/urandom of=%s bs=1M count=1 seek=2499'
> > + % big_file, shell=True)
> > +
> > + # Create a small file in this image.
> > + check_call('sudo dd if=/dev/urandom of=%s bs=1M count=1'
> > + % small_file, shell=True)
> > +
> > + # Delete the small file copies which possibly are written as part of a
> > + # previous test.
> > + # check_call('sudo rm -f "%s.w"' % MB1, shell=True)
> > + # check_call('sudo rm -f "%s.w2"' % MB1, shell=True)
> > +
> > + # Generate the md5sums of reads that we will test against small file
> > + out = check_output(
> > + 'dd if=%s bs=1M skip=0 count=1 2> /dev/null | md5sum'
> > + % small_file, shell=True)
> > + md5val = [ out.split()[0] ]
> > +
> > + # Generate the md5sums of reads that we will test against big file
> > + # One from beginning of file.
> > + out = check_output(
> > + 'dd if=%s bs=1M skip=0 count=1 2> /dev/null | md5sum'
> > + % big_file, shell=True)
> > + md5val.append(out.split()[0])
> > +
> > + # One from end of file.
> > + out = check_output(
> > + 'dd if=%s bs=1M skip=2499 count=1 2> /dev/null | md5sum'
> > + % big_file, shell=True)
> > + md5val.append(out.split()[0])
> > +
> > + # One from the last 1MB chunk of 2GB
> > + out = check_output(
> > + 'dd if=%s bs=1M skip=2047 count=1 2> /dev/null | md5sum'
> > + % big_file, shell=True)
> > + md5val.append(out.split()[0])
> > +
> > + # One from the start 1MB chunk from 2GB
> > + out = check_output(
> > + 'dd if=%s bs=1M skip=2048 count=1 2> /dev/null | md5sum'
> > + % big_file, shell=True)
> > + md5val.append(out.split()[0])
> > +
> > + # One 1MB chunk crossing the 2GB boundary
> > + out = check_output(
> > + 'dd if=%s bs=512K skip=4095 count=2 2> /dev/null | md5sum'
> > + % big_file, shell=True)
> > + md5val.append(out.split()[0])
> > +
> > + check_call('sudo umount %s' % mount_dir, shell=True)
> > + except CalledProcessError:
> > + pytest.skip('Setup failed for filesystem: ' + fs_type)
> > + else:
> > + yield [fs_ubtype, fs_img, md5val]
> > + finally:
> > + call('sudo umount %s' % mount_dir, shell=True)
> > + call('rmdir -rf %s' % mount_dir, shell=True)
> > +# if fs_img:
> > +# call('rm -f %s' % fs_img, shell=True)
> > diff --git a/test/py/tests/test_fs/fstest_defs.py b/test/py/tests/test_fs/fstest_defs.py
> > new file mode 100644
> > index 000000000000..f26dd06cacf2
> > --- /dev/null
> > +++ b/test/py/tests/test_fs/fstest_defs.py
> > @@ -0,0 +1,10 @@
> > +# SPDX-License-Identifier: GPL-2.0+
> > +
> > +# $SMALL_FILE is the name of the 1MB file in the file system image
> > +SMALL_FILE='1MB.file'
> > +
> > +# $BIG_FILE is the name of the 2.5GB file in the file system image
> > +BIG_FILE='2.5GB.file'
> > +
> > +ADDR=0x01000008
> > +LENGTH=0x00100000
> > diff --git a/test/py/tests/test_fs/test_basic.py b/test/py/tests/test_fs/test_basic.py
> > new file mode 100644
> > index 000000000000..5258d98d42a9
> > --- /dev/null
> > +++ b/test/py/tests/test_fs/test_basic.py
> > @@ -0,0 +1,246 @@
> > +# SPDX-License-Identifier: GPL-2.0+
> > +# Copyright (c) 2018, Linaro Limited
> > +# Author: Takahiro Akashi <takahiro.akashi@linaro.org>
> > +#
> > +# U-Boot File System:Basic Test
> > +
> > +"""
> > +This test verifies basic read/write operation on file system.
> > +"""
> > +
> > +import pytest
> > +import re
> > +from fstest_defs import *
> > +
> > + at pytest.mark.boardspec('sandbox')
> > +class TestFsBasic(object):
> > + def test_fs1(self, u_boot_console, fs_obj_basic):
> > + fs_type,fs_img,md5val = fs_obj_basic
> > + with u_boot_console.log.section('Test Case 1 - ls'):
> > + # Test Case 1 - ls
> > + output = u_boot_console.run_command_list([
> > + 'host bind 0 %s' % fs_img,
> > + '%sls host 0:0' % fs_type])
> > + assert(re.search('2621440000 *%s' % BIG_FILE, ''.join(output)))
> > + assert(re.search('1048576 *%s' % SMALL_FILE, ''.join(output)))
> > +
> > + # In addition, test with a nonexistent directory to see if we crash.
> > + output2 = u_boot_console.run_command(
> > + '%sls host 0:0 invalid_d' % fs_type)
> > + if fs_type == 'ext4':
> > + assert('Can not find directory' in output2)
> > + else:
> > + assert('' == output2)
> > +
> > + def test_fs2(self, u_boot_console, fs_obj_basic):
> > + fs_type,fs_img,md5val = fs_obj_basic
> > + with u_boot_console.log.section('Test Case 2 - size (small)'):
> > + # 1MB is 0x0010 0000
> > + # Test Case 2a - size of small file
> > + output = u_boot_console.run_command_list([
> > + 'host bind 0 %s' % fs_img,
> > + '%ssize host 0:0 /%s' % (fs_type, SMALL_FILE),
> > + 'printenv filesize',
> > + 'setenv filesize'])
> > + assert('filesize=100000' in ''.join(output))
> > +
> > + # Test Case 2b - size of small file via a path using '..'
> > + output = u_boot_console.run_command_list([
> > + '%ssize host 0:0 /SUBDIR/../%s' % (fs_type, SMALL_FILE),
> > + 'printenv filesize',
> > + 'setenv filesize'])
> > + assert('filesize=100000' in ''.join(output))
> > +
> > + def test_fs3(self, u_boot_console, fs_obj_basic):
> > + fs_type,fs_img,md5val = fs_obj_basic
> > + with u_boot_console.log.section('Test Case 3 - size (big)'):
> > + # 2.5GB (1024*1024*2500) is 0x9C40 0000
> > + # Test Case 3 - size of big file
> > + output = u_boot_console.run_command_list([
> > + 'host bind 0 %s' % fs_img,
> > + '%ssize host 0:0 /%s' % (fs_type, BIG_FILE),
> > + 'printenv filesize',
> > + 'setenv filesize'])
> > + assert('filesize=9c400000' in ''.join(output))
> > +
> > + def test_fs4(self, u_boot_console, fs_obj_basic):
> > + fs_type,fs_img,md5val = fs_obj_basic
> > + with u_boot_console.log.section('Test Case 4 - load (small)'):
> > + # Test Case 4a - Read full 1MB of small file
> > + output = u_boot_console.run_command_list([
> > + 'host bind 0 %s' % fs_img,
> > + '%sload host 0:0 %x /%s' % (fs_type, ADDR, SMALL_FILE),
> > + 'printenv filesize'])
> > + assert('filesize=100000' in ''.join(output))
> > +
> > + # Test Case 4b - Read full 1MB of small file
> > + output = u_boot_console.run_command_list([
> > + 'md5sum %x $filesize' % ADDR,
> > + 'setenv filesize'])
> > + assert(md5val[0] in ''.join(output))
> > +
> > + def test_fs5(self, u_boot_console, fs_obj_basic):
> > + fs_type,fs_img,md5val = fs_obj_basic
> > + with u_boot_console.log.section('Test Case 5 - load (big)'):
> > + # Test Case 5a - First 1MB of big file
> > + output = u_boot_console.run_command_list([
> > + 'host bind 0 %s' % fs_img,
> > + '%sload host 0:0 %x /%s %x 0x0' % (fs_type, ADDR, BIG_FILE, LENGTH),
> > + 'printenv filesize'])
> > + assert('filesize=100000' in ''.join(output))
> > +
> > + # Test Case 5b - First 1MB of big file
> > + output = u_boot_console.run_command_list([
> > + 'md5sum %x $filesize' % ADDR,
> > + 'setenv filesize'])
> > + assert(md5val[1] in ''.join(output))
> > +
> > + def test_fs6(self, u_boot_console, fs_obj_basic):
> > + fs_type,fs_img,md5val = fs_obj_basic
> > + with u_boot_console.log.section('Test Case 6 - load (big from last 1MB)'):
> > + # fails for ext as no offset support
> > + # Test Case 6a - Last 1MB of big file
> > + output = u_boot_console.run_command_list([
> > + 'host bind 0 %s' % fs_img,
> > + '%sload host 0:0 %x /%s %x 0x9c300000'
> > + % (fs_type, ADDR, BIG_FILE, LENGTH),
> > + 'printenv filesize'])
> > + assert('filesize=100000' in ''.join(output))
> > +
> > + # Test Case 6b - Last 1MB of big file
> > + output = u_boot_console.run_command_list([
> > + 'md5sum %x $filesize' % ADDR,
> > + 'setenv filesize'])
> > + assert(md5val[2] in ''.join(output))
> > +
> > + def test_fs7(self, u_boot_console, fs_obj_basic):
> > + fs_type,fs_img,md5val = fs_obj_basic
> > + with u_boot_console.log.section('Test Case 7 - load (big from last 1MB in 2GB)'):
> > + # fails for ext as no offset support
> > + # Test Case 7a - One from the last 1MB chunk of 2GB
> > + output = u_boot_console.run_command_list([
> > + 'host bind 0 %s' % fs_img,
> > + '%sload host 0:0 %x /%s %x 0x7ff00000'
> > + % (fs_type, ADDR, BIG_FILE, LENGTH),
> > + 'printenv filesize'])
> > + assert('filesize=100000' in ''.join(output))
> > +
> > + # Test Case 7b - One from the last 1MB chunk of 2GB
> > + output = u_boot_console.run_command_list([
> > + 'md5sum %x $filesize' % ADDR,
> > + 'setenv filesize'])
> > + assert(md5val[3] in ''.join(output))
> > +
> > + def test_fs8(self, u_boot_console, fs_obj_basic):
> > + fs_type,fs_img,md5val = fs_obj_basic
> > + with u_boot_console.log.section('Test Case 8 - load (big from first 1MB in 2GB)'):
> > + # fails for ext as no offset support
> > + # Test Case 8a - One from the start 1MB chunk from 2GB
> > + output = u_boot_console.run_command_list([
> > + 'host bind 0 %s' % fs_img,
> > + '%sload host 0:0 %x /%s %x 0x80000000'
> > + % (fs_type, ADDR, BIG_FILE, LENGTH),
> > + 'printenv filesize'])
> > + assert('filesize=100000' in ''.join(output))
> > +
> > + # Test Case 8b - One from the start 1MB chunk from 2GB
> > + output = u_boot_console.run_command_list([
> > + 'md5sum %x $filesize' % ADDR,
> > + 'setenv filesize'])
> > + assert(md5val[4] in ''.join(output))
> > +
> > + def test_fs9(self, u_boot_console, fs_obj_basic):
> > + fs_type,fs_img,md5val = fs_obj_basic
> > + with u_boot_console.log.section('Test Case 9 - load (crossing 2GB boundary)'):
> > + # fails for ext as no offset support
> > + # Test Case 9a - One 1MB chunk crossing the 2GB boundary
> > + output = u_boot_console.run_command_list([
> > + 'host bind 0 %s' % fs_img,
> > + '%sload host 0:0 %x /%s %x 0x7ff80000'
> > + % (fs_type, ADDR, BIG_FILE, LENGTH),
> > + 'printenv filesize'])
> > + assert('filesize=100000' in ''.join(output))
> > +
> > + # Test Case 9b - One 1MB chunk crossing the 2GB boundary
> > + output = u_boot_console.run_command_list([
> > + 'md5sum %x $filesize' % ADDR,
> > + 'setenv filesize'])
> > + assert(md5val[5] in ''.join(output))
> > +
> > + def test_fs10(self, u_boot_console, fs_obj_basic):
> > + fs_type,fs_img,md5val = fs_obj_basic
> > + with u_boot_console.log.section('Test Case 10 - load (over file end)'):
> > + # Generic failure case
> > + # Test Case 10 - 2MB chunk from the last 1MB of big file
> > + output = u_boot_console.run_command_list([
> > + 'host bind 0 %s' % fs_img,
> > + '%sload host 0:0 %x /%s 0x00200000 0x9c300000'
> > + % (fs_type, ADDR, BIG_FILE),
> > + 'printenv filesize',
> > + 'md5sum %x $filesize' % ADDR,
> > + 'setenv filesize'])
> > + assert('filesize=100000' in ''.join(output))
> > +
> > + def test_fs11(self, u_boot_console, fs_obj_basic):
> > + fs_type,fs_img,md5val = fs_obj_basic
> > + with u_boot_console.log.section('Test Case 11 - write (small)'):
> > + # Read 1MB from small file
> > + # Write it back to test the writes
> > + # Test Case 11a - Check that the write succeeded
> > + output = u_boot_console.run_command_list([
> > + 'host bind 0 %s' % fs_img,
> > + '%sload host 0:0 %x /%s' % (fs_type, ADDR, SMALL_FILE),
> > + '%swrite host 0:0 %x /%s.w $filesize'
> > + % (fs_type, ADDR, SMALL_FILE)])
> > + assert('1048576 bytes written' in ''.join(output))
> > +
> > + # Test Case 11b - Check md5 of written to is same
> > + # as the one read from
> > + output = u_boot_console.run_command_list([
> > + '%sload host 0:0 %x /%s.w' % (fs_type, ADDR, SMALL_FILE),
> > + 'md5sum %x $filesize' % ADDR,
> > + 'setenv filesize'])
> > + assert(md5val[0] in ''.join(output))
> > +
> > + def test_fs12(self, u_boot_console, fs_obj_basic):
> > + fs_type,fs_img,md5val = fs_obj_basic
> > + with u_boot_console.log.section('Test Case 12 - write (".")'):
> > + # Next test case checks writing a file whose dirent
> > + # is the first in the block, which is always true for "."
> > + # The write should fail, but the lookup should work
> > + # Test Case 12 - Check directory traversal
> > + output = u_boot_console.run_command_list([
> > + 'host bind 0 %s' % fs_img,
> > + '%swrite host 0:0 %x /. 0x10' % (fs_type, ADDR)])
> > + assert('Unable to write' in ''.join(output))
> > +
> > + def test_fs13(self, u_boot_console, fs_obj_basic):
> > + fs_type,fs_img,md5val = fs_obj_basic
> > + with u_boot_console.log.section('Test Case 13 - write (small w/ "./")'):
> > + # Read 1MB from small file
> > + # Write it via "same directory", i.e. "." dirent
> > + # Test Case 13a - Check directory traversal
> > + output = u_boot_console.run_command_list([
> > + 'host bind 0 %s' % fs_img,
> > + '%sload host 0:0 %x /%s' % (fs_type, ADDR, SMALL_FILE),
> > + '%swrite host 0:0 %x /./%s2 $filesize'
> > + % (fs_type, ADDR, SMALL_FILE)])
> > + assert('1048576 bytes written' in ''.join(output))
> > +
> > + # Test Case 13b - Check md5 of written to is same
> > + # as the one read from
> > + output = u_boot_console.run_command_list([
> > + 'mw.b %x 00 100' % ADDR,
> > + '%sload host 0:0 %x /./%s2' % (fs_type, ADDR, SMALL_FILE),
> > + 'md5sum %x $filesize' % ADDR,
> > + 'setenv filesize'])
> > + assert(md5val[0] in ''.join(output))
> > +
> > + # Test Case 13c - Check md5 of written to is same
> > + # as the one read from
> > + output = u_boot_console.run_command_list([
> > + 'mw.b %x 00 100' % ADDR,
> > + '%sload host 0:0 %x /%s2' % (fs_type, ADDR, SMALL_FILE),
> > + 'md5sum %x $filesize' % ADDR,
> > + 'setenv filesize'])
> > + assert(md5val[0] in ''.join(output))
> >
>
next prev parent reply other threads:[~2018-08-30 6:52 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2018-08-23 7:25 [U-Boot] [RFC 0/3] test/py: add filesystem test scripts AKASHI Takahiro
2018-08-23 7:25 ` [U-Boot] [RFC 1/3] test/py: convert fs-test.sh to pytest AKASHI Takahiro
2018-08-29 21:36 ` Heinrich Schuchardt
2018-08-30 6:52 ` AKASHI Takahiro [this message]
2018-08-30 10:01 ` Heinrich Schuchardt
2018-08-30 10:26 ` AKASHI Takahiro
2018-08-30 10:56 ` Tuomas Tynkkynen
2018-08-31 7:22 ` AKASHI Takahiro
2018-09-04 19:26 ` Tuomas Tynkkynen
2018-08-31 7:31 ` AKASHI Takahiro
2018-08-31 8:26 ` Alexander Graf
2018-08-23 7:25 ` [U-Boot] [RFC 2/3] test/py: fs: add extended write operation test AKASHI Takahiro
2018-08-23 7:25 ` [U-Boot] [RFC 3/3] test/py: fs: add fstest/mkdir test AKASHI Takahiro
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20180830065244.GK12252@linaro.org \
--to=takahiro.akashi@linaro.org \
--cc=u-boot@lists.denx.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.