All of lore.kernel.org
 help / color / mirror / Atom feed
From: Brendan Higgins <brendanhiggins@google.com>
To: frowand.list@gmail.com, gregkh@linuxfoundation.org,
	keescook@google.com, kieran.bingham@ideasonboard.com,
	mcgrof@kernel.org, robh@kernel.org, sboyd@kernel.org,
	shuah@kernel.org
Cc: devicetree@vger.kernel.org, dri-devel@lists.freedesktop.org,
	kunit-dev@googlegroups.com, linux-doc@vger.kernel.org,
	linux-fsdevel@vger.kernel.org, linux-kbuild@vger.kernel.org,
	linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org,
	linux-nvdimm@lists.01.org, linux-um@lists.infradead.org,
	Alexander.Levin@microsoft.com, Tim.Bird@sony.com,
	amir73il@gmail.com, dan.carpenter@oracle.com,
	dan.j.williams@intel.com, daniel@ffwll.ch, jdike@addtoit.com,
	joel@jms.id.au, julia.lawall@lip6.fr, khilman@baylibre.com,
	knut.omang@oracle.com, logang@deltatee.com, mpe@ellerman.id.au,
	pmladek@suse.com, richard@nod.at, rientjes@google.com,
	rostedt@goodmis.org, wfg@linux.intel.com,
	Felix Guo <felixguoxiuping@gmail.com>,
	Brendan Higgins <brendanhiggins@google.com>
Subject: [PATCH v2 12/17] kunit: tool: add Python wrappers for running KUnit tests
Date: Wed,  1 May 2019 16:01:21 -0700	[thread overview]
Message-ID: <20190501230126.229218-13-brendanhiggins@google.com> (raw)
In-Reply-To: <20190501230126.229218-1-brendanhiggins@google.com>

From: Felix Guo <felixguoxiuping@gmail.com>

The ultimate goal is to create minimal isolated test binaries; in the
meantime we are using UML to provide the infrastructure to run tests, so
define an abstract way to configure and run tests that allow us to
change the context in which tests are built without affecting the user.
This also makes pretty and dynamic error reporting, and a lot of other
nice features easier.

kunit_config.py:
  - parse .config and Kconfig files.

kunit_kernel.py: provides helper functions to:
  - configure the kernel using kunitconfig.
  - build the kernel with the appropriate configuration.
  - provide function to invoke the kernel and stream the output back.

Signed-off-by: Felix Guo <felixguoxiuping@gmail.com>
Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
---
 tools/testing/kunit/.gitignore      |   3 +
 tools/testing/kunit/kunit.py        |  78 +++++++++++++++
 tools/testing/kunit/kunit_config.py |  66 +++++++++++++
 tools/testing/kunit/kunit_kernel.py | 148 ++++++++++++++++++++++++++++
 tools/testing/kunit/kunit_parser.py | 119 ++++++++++++++++++++++
 5 files changed, 414 insertions(+)
 create mode 100644 tools/testing/kunit/.gitignore
 create mode 100755 tools/testing/kunit/kunit.py
 create mode 100644 tools/testing/kunit/kunit_config.py
 create mode 100644 tools/testing/kunit/kunit_kernel.py
 create mode 100644 tools/testing/kunit/kunit_parser.py

diff --git a/tools/testing/kunit/.gitignore b/tools/testing/kunit/.gitignore
new file mode 100644
index 0000000000000..c791ff59a37a9
--- /dev/null
+++ b/tools/testing/kunit/.gitignore
@@ -0,0 +1,3 @@
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
\ No newline at end of file
diff --git a/tools/testing/kunit/kunit.py b/tools/testing/kunit/kunit.py
new file mode 100755
index 0000000000000..7413ec7351a20
--- /dev/null
+++ b/tools/testing/kunit/kunit.py
@@ -0,0 +1,78 @@
+#!/usr/bin/python3
+# SPDX-License-Identifier: GPL-2.0
+#
+# A thin wrapper on top of the KUnit Kernel
+#
+# Copyright (C) 2019, Google LLC.
+# Author: Felix Guo <felixguoxiuping@gmail.com>
+# Author: Brendan Higgins <brendanhiggins@google.com>
+
+import argparse
+import sys
+import os
+import time
+
+import kunit_config
+import kunit_kernel
+import kunit_parser
+
+parser = argparse.ArgumentParser(description='Runs KUnit tests.')
+
+parser.add_argument('--raw_output', help='don\'t format output from kernel',
+		    action='store_true')
+
+parser.add_argument('--timeout', help='maximum number of seconds to allow for '
+		    'all tests to run. This does not include time taken to '
+		    'build the tests.', type=int, default=300,
+		    metavar='timeout')
+
+parser.add_argument('--jobs',
+		    help='As in the make command, "Specifies  the number of '
+		    'jobs (commands) to run simultaneously."',
+		    type=int, default=8, metavar='jobs')
+
+parser.add_argument('--build_dir',
+		    help='As in the make command, it specifies the build '
+		    'directory.',
+		    type=str, default=None, metavar='build_dir')
+
+cli_args = parser.parse_args()
+
+linux = kunit_kernel.LinuxSourceTree()
+
+build_dir = None
+if cli_args.build_dir:
+	build_dir = cli_args.build_dir
+
+config_start = time.time()
+success = linux.build_reconfig(build_dir)
+config_end = time.time()
+if not success:
+	quit()
+
+kunit_parser.print_with_timestamp('Building KUnit Kernel ...')
+
+build_start = time.time()
+
+success = linux.build_um_kernel(jobs=cli_args.jobs, build_dir=build_dir)
+build_end = time.time()
+if not success:
+	quit()
+
+kunit_parser.print_with_timestamp('Starting KUnit Kernel ...')
+test_start = time.time()
+
+if cli_args.raw_output:
+	kunit_parser.raw_output(linux.run_kernel(timeout=cli_args.timeout,
+						 build_dir=build_dir))
+else:
+	kunit_parser.parse_run_tests(linux.run_kernel(timeout=cli_args.timeout,
+						      build_dir=build_dir))
+
+test_end = time.time()
+
+kunit_parser.print_with_timestamp((
+	"Elapsed time: %.3fs total, %.3fs configuring, %.3fs " +
+	"building, %.3fs running.\n") % (test_end - config_start,
+	config_end - config_start, build_end - build_start,
+	test_end - test_start))
diff --git a/tools/testing/kunit/kunit_config.py b/tools/testing/kunit/kunit_config.py
new file mode 100644
index 0000000000000..167f47d9ab8e4
--- /dev/null
+++ b/tools/testing/kunit/kunit_config.py
@@ -0,0 +1,66 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Builds a .config from a kunitconfig.
+#
+# Copyright (C) 2019, Google LLC.
+# Author: Felix Guo <felixguoxiuping@gmail.com>
+# Author: Brendan Higgins <brendanhiggins@google.com>
+
+import collections
+import re
+
+CONFIG_IS_NOT_SET_PATTERN = r'^# CONFIG_\w+ is not set$'
+CONFIG_PATTERN = r'^CONFIG_\w+=\S+$'
+
+KconfigEntryBase = collections.namedtuple('KconfigEntry', ['raw_entry'])
+
+
+class KconfigEntry(KconfigEntryBase):
+
+	def __str__(self) -> str:
+		return self.raw_entry
+
+
+class KconfigParseError(Exception):
+	"""Error parsing Kconfig defconfig or .config."""
+
+
+class Kconfig(object):
+	"""Represents defconfig or .config specified using the Kconfig language."""
+
+	def __init__(self):
+		self._entries = []
+
+	def entries(self):
+		return set(self._entries)
+
+	def add_entry(self, entry: KconfigEntry) -> None:
+		self._entries.append(entry)
+
+	def is_subset_of(self, other: "Kconfig") -> bool:
+		return self.entries().issubset(other.entries())
+
+	def write_to_file(self, path: str) -> None:
+		with open(path, 'w') as f:
+			for entry in self.entries():
+				f.write(str(entry) + '\n')
+
+	def parse_from_string(self, blob: str) -> None:
+		"""Parses a string containing KconfigEntrys and populates this Kconfig."""
+		self._entries = []
+		is_not_set_matcher = re.compile(CONFIG_IS_NOT_SET_PATTERN)
+		config_matcher = re.compile(CONFIG_PATTERN)
+		for line in blob.split('\n'):
+			line = line.strip()
+			if not line:
+				continue
+			elif config_matcher.match(line) or is_not_set_matcher.match(line):
+				self._entries.append(KconfigEntry(line))
+			elif line[0] == '#':
+				continue
+			else:
+				raise KconfigParseError('Failed to parse: ' + line)
+
+	def read_from_file(self, path: str) -> None:
+		with open(path, 'r') as f:
+			self.parse_from_string(f.read())
diff --git a/tools/testing/kunit/kunit_kernel.py b/tools/testing/kunit/kunit_kernel.py
new file mode 100644
index 0000000000000..07c0abf2f47df
--- /dev/null
+++ b/tools/testing/kunit/kunit_kernel.py
@@ -0,0 +1,148 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Runs UML kernel, collects output, and handles errors.
+#
+# Copyright (C) 2019, Google LLC.
+# Author: Felix Guo <felixguoxiuping@gmail.com>
+# Author: Brendan Higgins <brendanhiggins@google.com>
+
+
+import logging
+import subprocess
+import os
+
+import kunit_config
+
+KCONFIG_PATH = '.config'
+
+class ConfigError(Exception):
+	"""Represents an error trying to configure the Linux kernel."""
+
+
+class BuildError(Exception):
+	"""Represents an error trying to build the Linux kernel."""
+
+
+class LinuxSourceTreeOperations(object):
+	"""An abstraction over command line operations performed on a source tree."""
+
+	def make_mrproper(self):
+		try:
+			subprocess.check_output(['make', 'mrproper'])
+		except OSError as e:
+			raise ConfigError('Could not call make command: ' + e)
+		except subprocess.CalledProcessError as e:
+			raise ConfigError(e.output)
+
+	def make_olddefconfig(self, build_dir):
+		command = ['make', 'ARCH=um', 'olddefconfig']
+		if build_dir:
+			command += ['O=' + build_dir]
+		try:
+			subprocess.check_output(command)
+		except OSError as e:
+			raise ConfigError('Could not call make command: ' + e)
+		except subprocess.CalledProcessError as e:
+			raise ConfigError(e.output)
+
+	def make(self, jobs, build_dir):
+		command = ['make', 'ARCH=um', '--jobs=' + str(jobs)]
+		if build_dir:
+			command += ['O=' + build_dir]
+		try:
+			subprocess.check_output(command)
+		except OSError as e:
+			raise BuildError('Could not call execute make: ' + e)
+		except subprocess.CalledProcessError as e:
+			raise BuildError(e.output)
+
+	def linux_bin(self, params, timeout, build_dir):
+		"""Runs the Linux UML binary. Must be named 'linux'."""
+		linux_bin = './linux'
+		if build_dir:
+			linux_bin = os.path.join(build_dir, 'linux')
+		process = subprocess.Popen(
+			[linux_bin] + params,
+			stdin=subprocess.PIPE,
+			stdout=subprocess.PIPE,
+			stderr=subprocess.PIPE)
+		process.wait(timeout=timeout)
+		return process
+
+
+def get_kconfig_path(build_dir):
+	kconfig_path = KCONFIG_PATH
+	if build_dir:
+		kconfig_path = os.path.join(build_dir, KCONFIG_PATH)
+	return kconfig_path
+
+class LinuxSourceTree(object):
+	"""Represents a Linux kernel source tree with KUnit tests."""
+
+	def __init__(self):
+		self._kconfig = kunit_config.Kconfig()
+		self._kconfig.read_from_file('kunitconfig')
+		self._ops = LinuxSourceTreeOperations()
+
+	def clean(self):
+		try:
+			self._ops.make_mrproper()
+		except ConfigError as e:
+			logging.error(e)
+			return False
+		return True
+
+	def build_config(self, build_dir):
+		kconfig_path = get_kconfig_path(build_dir)
+		if build_dir and not os.path.exists(build_dir):
+			os.mkdir(build_dir)
+		self._kconfig.write_to_file(kconfig_path)
+		try:
+			self._ops.make_olddefconfig(build_dir)
+		except ConfigError as e:
+			logging.error(e)
+			return False
+		validated_kconfig = kunit_config.Kconfig()
+		validated_kconfig.read_from_file(kconfig_path)
+		if not self._kconfig.is_subset_of(validated_kconfig):
+			logging.error('Provided Kconfig is not contained in validated .config!')
+			return False
+		return True
+
+	def build_reconfig(self, build_dir):
+		"""Creates a new .config if it is not a subset of the kunitconfig."""
+		kconfig_path = get_kconfig_path(build_dir)
+		if os.path.exists(kconfig_path):
+			existing_kconfig = kunit_config.Kconfig()
+			existing_kconfig.read_from_file(kconfig_path)
+			if not self._kconfig.is_subset_of(existing_kconfig):
+				print('Regenerating .config ...')
+				os.remove(kconfig_path)
+				return self.build_config(build_dir)
+			else:
+				return True
+		else:
+			print('Generating .config ...')
+			return self.build_config(build_dir)
+
+	def build_um_kernel(self, jobs, build_dir):
+		try:
+			self._ops.make_olddefconfig(build_dir)
+			self._ops.make(jobs, build_dir)
+		except (ConfigError, BuildError) as e:
+			logging.error(e)
+			return False
+		used_kconfig = kunit_config.Kconfig()
+		used_kconfig.read_from_file(get_kconfig_path(build_dir))
+		if not self._kconfig.is_subset_of(used_kconfig):
+			logging.error('Provided Kconfig is not contained in final config!')
+			return False
+		return True
+
+	def run_kernel(self, args=[], timeout=None, build_dir=None):
+		args.extend(['mem=256M'])
+		process = self._ops.linux_bin(args, timeout, build_dir)
+		with open('test.log', 'w') as f:
+			for line in process.stdout:
+				f.write(line.rstrip().decode('ascii') + '\n')
+				yield line.rstrip().decode('ascii')
diff --git a/tools/testing/kunit/kunit_parser.py b/tools/testing/kunit/kunit_parser.py
new file mode 100644
index 0000000000000..6c81d4dcfabb5
--- /dev/null
+++ b/tools/testing/kunit/kunit_parser.py
@@ -0,0 +1,119 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Parses test results from a kernel dmesg log.
+#
+# Copyright (C) 2019, Google LLC.
+# Author: Felix Guo <felixguoxiuping@gmail.com>
+# Author: Brendan Higgins <brendanhiggins@google.com>
+
+import re
+from datetime import datetime
+
+kunit_start_re = re.compile('printk: console .* enabled')
+kunit_end_re = re.compile('List of all partitions:')
+
+def isolate_kunit_output(kernel_output):
+	started = False
+	for line in kernel_output:
+		if kunit_start_re.match(line):
+			started = True
+		elif kunit_end_re.match(line):
+			break
+		elif started:
+			yield line
+
+def raw_output(kernel_output):
+	for line in kernel_output:
+		print(line)
+
+DIVIDER = "=" * 30
+
+RESET = '\033[0;0m'
+
+def red(text):
+	return '\033[1;31m' + text + RESET
+
+def yellow(text):
+	return '\033[1;33m' + text + RESET
+
+def green(text):
+	return '\033[1;32m' + text + RESET
+
+def print_with_timestamp(message):
+	print('[%s] %s' % (datetime.now().strftime('%H:%M:%S'), message))
+
+def print_log(log):
+	for m in log:
+		print_with_timestamp(m)
+
+def parse_run_tests(kernel_output):
+	test_case_output = re.compile('^kunit .*?: (.*)$')
+
+	test_module_success = re.compile('^kunit .*: all tests passed')
+	test_module_fail = re.compile('^kunit .*: one or more tests failed')
+
+	test_case_success = re.compile('^kunit (.*): (.*) passed')
+	test_case_fail = re.compile('^kunit (.*): (.*) failed')
+	test_case_crash = re.compile('^kunit (.*): (.*) crashed')
+
+	total_tests = set()
+	failed_tests = set()
+	crashed_tests = set()
+
+	def get_test_name(match):
+		return match.group(1) + ":" + match.group(2)
+
+	current_case_log = []
+	def end_one_test(match, log):
+		log.clear()
+		total_tests.add(get_test_name(match))
+
+	print_with_timestamp(DIVIDER)
+	for line in isolate_kunit_output(kernel_output):
+		# Ignore module output:
+		if (test_module_success.match(line) or
+		    test_module_fail.match(line)):
+			print_with_timestamp(DIVIDER)
+			continue
+
+		match = re.match(test_case_success, line)
+		if match:
+			print_with_timestamp(green("[PASSED] ") +
+					     get_test_name(match))
+			end_one_test(match, current_case_log)
+			continue
+
+		match = re.match(test_case_fail, line)
+		# Crashed tests will report as both failed and crashed. We only
+		# want to show and count it once.
+		if match and get_test_name(match) not in crashed_tests:
+			failed_tests.add(get_test_name(match))
+			print_with_timestamp(red("[FAILED] " +
+						 get_test_name(match)))
+			print_log(map(yellow, current_case_log))
+			print_with_timestamp("")
+			end_one_test(match, current_case_log)
+			continue
+
+		match = re.match(test_case_crash, line)
+		if match:
+			crashed_tests.add(get_test_name(match))
+			print_with_timestamp(yellow("[CRASH] " +
+						    get_test_name(match)))
+			print_log(current_case_log)
+			print_with_timestamp("")
+			end_one_test(match, current_case_log)
+			continue
+
+		# Strip off the `kunit module-name:` prefix
+		match = re.match(test_case_output, line)
+		if match:
+			current_case_log.append(match.group(1))
+		else:
+			current_case_log.append(line)
+
+	fmt = green if (len(failed_tests) + len(crashed_tests) == 0) else red
+	print_with_timestamp(
+		fmt("Testing complete. %d tests run. %d failed. %d crashed." %
+		    (len(total_tests), len(failed_tests), len(crashed_tests))))
+
-- 
2.21.0.593.g511ec345e18-goog

WARNING: multiple messages have this Message-ID (diff)
From: brendanhiggins at google.com (Brendan Higgins)
Subject: [PATCH v2 12/17] kunit: tool: add Python wrappers for running KUnit tests
Date: Wed,  1 May 2019 16:01:21 -0700	[thread overview]
Message-ID: <20190501230126.229218-13-brendanhiggins@google.com> (raw)
In-Reply-To: <20190501230126.229218-1-brendanhiggins@google.com>

From: Felix Guo <felixguoxiuping at gmail.com>

The ultimate goal is to create minimal isolated test binaries; in the
meantime we are using UML to provide the infrastructure to run tests, so
define an abstract way to configure and run tests that allow us to
change the context in which tests are built without affecting the user.
This also makes pretty and dynamic error reporting, and a lot of other
nice features easier.

kunit_config.py:
  - parse .config and Kconfig files.

kunit_kernel.py: provides helper functions to:
  - configure the kernel using kunitconfig.
  - build the kernel with the appropriate configuration.
  - provide function to invoke the kernel and stream the output back.

Signed-off-by: Felix Guo <felixguoxiuping at gmail.com>
Signed-off-by: Brendan Higgins <brendanhiggins at google.com>
---
 tools/testing/kunit/.gitignore      |   3 +
 tools/testing/kunit/kunit.py        |  78 +++++++++++++++
 tools/testing/kunit/kunit_config.py |  66 +++++++++++++
 tools/testing/kunit/kunit_kernel.py | 148 ++++++++++++++++++++++++++++
 tools/testing/kunit/kunit_parser.py | 119 ++++++++++++++++++++++
 5 files changed, 414 insertions(+)
 create mode 100644 tools/testing/kunit/.gitignore
 create mode 100755 tools/testing/kunit/kunit.py
 create mode 100644 tools/testing/kunit/kunit_config.py
 create mode 100644 tools/testing/kunit/kunit_kernel.py
 create mode 100644 tools/testing/kunit/kunit_parser.py

diff --git a/tools/testing/kunit/.gitignore b/tools/testing/kunit/.gitignore
new file mode 100644
index 0000000000000..c791ff59a37a9
--- /dev/null
+++ b/tools/testing/kunit/.gitignore
@@ -0,0 +1,3 @@
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
\ No newline at end of file
diff --git a/tools/testing/kunit/kunit.py b/tools/testing/kunit/kunit.py
new file mode 100755
index 0000000000000..7413ec7351a20
--- /dev/null
+++ b/tools/testing/kunit/kunit.py
@@ -0,0 +1,78 @@
+#!/usr/bin/python3
+# SPDX-License-Identifier: GPL-2.0
+#
+# A thin wrapper on top of the KUnit Kernel
+#
+# Copyright (C) 2019, Google LLC.
+# Author: Felix Guo <felixguoxiuping at gmail.com>
+# Author: Brendan Higgins <brendanhiggins at google.com>
+
+import argparse
+import sys
+import os
+import time
+
+import kunit_config
+import kunit_kernel
+import kunit_parser
+
+parser = argparse.ArgumentParser(description='Runs KUnit tests.')
+
+parser.add_argument('--raw_output', help='don\'t format output from kernel',
+		    action='store_true')
+
+parser.add_argument('--timeout', help='maximum number of seconds to allow for '
+		    'all tests to run. This does not include time taken to '
+		    'build the tests.', type=int, default=300,
+		    metavar='timeout')
+
+parser.add_argument('--jobs',
+		    help='As in the make command, "Specifies  the number of '
+		    'jobs (commands) to run simultaneously."',
+		    type=int, default=8, metavar='jobs')
+
+parser.add_argument('--build_dir',
+		    help='As in the make command, it specifies the build '
+		    'directory.',
+		    type=str, default=None, metavar='build_dir')
+
+cli_args = parser.parse_args()
+
+linux = kunit_kernel.LinuxSourceTree()
+
+build_dir = None
+if cli_args.build_dir:
+	build_dir = cli_args.build_dir
+
+config_start = time.time()
+success = linux.build_reconfig(build_dir)
+config_end = time.time()
+if not success:
+	quit()
+
+kunit_parser.print_with_timestamp('Building KUnit Kernel ...')
+
+build_start = time.time()
+
+success = linux.build_um_kernel(jobs=cli_args.jobs, build_dir=build_dir)
+build_end = time.time()
+if not success:
+	quit()
+
+kunit_parser.print_with_timestamp('Starting KUnit Kernel ...')
+test_start = time.time()
+
+if cli_args.raw_output:
+	kunit_parser.raw_output(linux.run_kernel(timeout=cli_args.timeout,
+						 build_dir=build_dir))
+else:
+	kunit_parser.parse_run_tests(linux.run_kernel(timeout=cli_args.timeout,
+						      build_dir=build_dir))
+
+test_end = time.time()
+
+kunit_parser.print_with_timestamp((
+	"Elapsed time: %.3fs total, %.3fs configuring, %.3fs " +
+	"building, %.3fs running.\n") % (test_end - config_start,
+	config_end - config_start, build_end - build_start,
+	test_end - test_start))
diff --git a/tools/testing/kunit/kunit_config.py b/tools/testing/kunit/kunit_config.py
new file mode 100644
index 0000000000000..167f47d9ab8e4
--- /dev/null
+++ b/tools/testing/kunit/kunit_config.py
@@ -0,0 +1,66 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Builds a .config from a kunitconfig.
+#
+# Copyright (C) 2019, Google LLC.
+# Author: Felix Guo <felixguoxiuping at gmail.com>
+# Author: Brendan Higgins <brendanhiggins at google.com>
+
+import collections
+import re
+
+CONFIG_IS_NOT_SET_PATTERN = r'^# CONFIG_\w+ is not set$'
+CONFIG_PATTERN = r'^CONFIG_\w+=\S+$'
+
+KconfigEntryBase = collections.namedtuple('KconfigEntry', ['raw_entry'])
+
+
+class KconfigEntry(KconfigEntryBase):
+
+	def __str__(self) -> str:
+		return self.raw_entry
+
+
+class KconfigParseError(Exception):
+	"""Error parsing Kconfig defconfig or .config."""
+
+
+class Kconfig(object):
+	"""Represents defconfig or .config specified using the Kconfig language."""
+
+	def __init__(self):
+		self._entries = []
+
+	def entries(self):
+		return set(self._entries)
+
+	def add_entry(self, entry: KconfigEntry) -> None:
+		self._entries.append(entry)
+
+	def is_subset_of(self, other: "Kconfig") -> bool:
+		return self.entries().issubset(other.entries())
+
+	def write_to_file(self, path: str) -> None:
+		with open(path, 'w') as f:
+			for entry in self.entries():
+				f.write(str(entry) + '\n')
+
+	def parse_from_string(self, blob: str) -> None:
+		"""Parses a string containing KconfigEntrys and populates this Kconfig."""
+		self._entries = []
+		is_not_set_matcher = re.compile(CONFIG_IS_NOT_SET_PATTERN)
+		config_matcher = re.compile(CONFIG_PATTERN)
+		for line in blob.split('\n'):
+			line = line.strip()
+			if not line:
+				continue
+			elif config_matcher.match(line) or is_not_set_matcher.match(line):
+				self._entries.append(KconfigEntry(line))
+			elif line[0] == '#':
+				continue
+			else:
+				raise KconfigParseError('Failed to parse: ' + line)
+
+	def read_from_file(self, path: str) -> None:
+		with open(path, 'r') as f:
+			self.parse_from_string(f.read())
diff --git a/tools/testing/kunit/kunit_kernel.py b/tools/testing/kunit/kunit_kernel.py
new file mode 100644
index 0000000000000..07c0abf2f47df
--- /dev/null
+++ b/tools/testing/kunit/kunit_kernel.py
@@ -0,0 +1,148 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Runs UML kernel, collects output, and handles errors.
+#
+# Copyright (C) 2019, Google LLC.
+# Author: Felix Guo <felixguoxiuping at gmail.com>
+# Author: Brendan Higgins <brendanhiggins at google.com>
+
+
+import logging
+import subprocess
+import os
+
+import kunit_config
+
+KCONFIG_PATH = '.config'
+
+class ConfigError(Exception):
+	"""Represents an error trying to configure the Linux kernel."""
+
+
+class BuildError(Exception):
+	"""Represents an error trying to build the Linux kernel."""
+
+
+class LinuxSourceTreeOperations(object):
+	"""An abstraction over command line operations performed on a source tree."""
+
+	def make_mrproper(self):
+		try:
+			subprocess.check_output(['make', 'mrproper'])
+		except OSError as e:
+			raise ConfigError('Could not call make command: ' + e)
+		except subprocess.CalledProcessError as e:
+			raise ConfigError(e.output)
+
+	def make_olddefconfig(self, build_dir):
+		command = ['make', 'ARCH=um', 'olddefconfig']
+		if build_dir:
+			command += ['O=' + build_dir]
+		try:
+			subprocess.check_output(command)
+		except OSError as e:
+			raise ConfigError('Could not call make command: ' + e)
+		except subprocess.CalledProcessError as e:
+			raise ConfigError(e.output)
+
+	def make(self, jobs, build_dir):
+		command = ['make', 'ARCH=um', '--jobs=' + str(jobs)]
+		if build_dir:
+			command += ['O=' + build_dir]
+		try:
+			subprocess.check_output(command)
+		except OSError as e:
+			raise BuildError('Could not call execute make: ' + e)
+		except subprocess.CalledProcessError as e:
+			raise BuildError(e.output)
+
+	def linux_bin(self, params, timeout, build_dir):
+		"""Runs the Linux UML binary. Must be named 'linux'."""
+		linux_bin = './linux'
+		if build_dir:
+			linux_bin = os.path.join(build_dir, 'linux')
+		process = subprocess.Popen(
+			[linux_bin] + params,
+			stdin=subprocess.PIPE,
+			stdout=subprocess.PIPE,
+			stderr=subprocess.PIPE)
+		process.wait(timeout=timeout)
+		return process
+
+
+def get_kconfig_path(build_dir):
+	kconfig_path = KCONFIG_PATH
+	if build_dir:
+		kconfig_path = os.path.join(build_dir, KCONFIG_PATH)
+	return kconfig_path
+
+class LinuxSourceTree(object):
+	"""Represents a Linux kernel source tree with KUnit tests."""
+
+	def __init__(self):
+		self._kconfig = kunit_config.Kconfig()
+		self._kconfig.read_from_file('kunitconfig')
+		self._ops = LinuxSourceTreeOperations()
+
+	def clean(self):
+		try:
+			self._ops.make_mrproper()
+		except ConfigError as e:
+			logging.error(e)
+			return False
+		return True
+
+	def build_config(self, build_dir):
+		kconfig_path = get_kconfig_path(build_dir)
+		if build_dir and not os.path.exists(build_dir):
+			os.mkdir(build_dir)
+		self._kconfig.write_to_file(kconfig_path)
+		try:
+			self._ops.make_olddefconfig(build_dir)
+		except ConfigError as e:
+			logging.error(e)
+			return False
+		validated_kconfig = kunit_config.Kconfig()
+		validated_kconfig.read_from_file(kconfig_path)
+		if not self._kconfig.is_subset_of(validated_kconfig):
+			logging.error('Provided Kconfig is not contained in validated .config!')
+			return False
+		return True
+
+	def build_reconfig(self, build_dir):
+		"""Creates a new .config if it is not a subset of the kunitconfig."""
+		kconfig_path = get_kconfig_path(build_dir)
+		if os.path.exists(kconfig_path):
+			existing_kconfig = kunit_config.Kconfig()
+			existing_kconfig.read_from_file(kconfig_path)
+			if not self._kconfig.is_subset_of(existing_kconfig):
+				print('Regenerating .config ...')
+				os.remove(kconfig_path)
+				return self.build_config(build_dir)
+			else:
+				return True
+		else:
+			print('Generating .config ...')
+			return self.build_config(build_dir)
+
+	def build_um_kernel(self, jobs, build_dir):
+		try:
+			self._ops.make_olddefconfig(build_dir)
+			self._ops.make(jobs, build_dir)
+		except (ConfigError, BuildError) as e:
+			logging.error(e)
+			return False
+		used_kconfig = kunit_config.Kconfig()
+		used_kconfig.read_from_file(get_kconfig_path(build_dir))
+		if not self._kconfig.is_subset_of(used_kconfig):
+			logging.error('Provided Kconfig is not contained in final config!')
+			return False
+		return True
+
+	def run_kernel(self, args=[], timeout=None, build_dir=None):
+		args.extend(['mem=256M'])
+		process = self._ops.linux_bin(args, timeout, build_dir)
+		with open('test.log', 'w') as f:
+			for line in process.stdout:
+				f.write(line.rstrip().decode('ascii') + '\n')
+				yield line.rstrip().decode('ascii')
diff --git a/tools/testing/kunit/kunit_parser.py b/tools/testing/kunit/kunit_parser.py
new file mode 100644
index 0000000000000..6c81d4dcfabb5
--- /dev/null
+++ b/tools/testing/kunit/kunit_parser.py
@@ -0,0 +1,119 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Parses test results from a kernel dmesg log.
+#
+# Copyright (C) 2019, Google LLC.
+# Author: Felix Guo <felixguoxiuping at gmail.com>
+# Author: Brendan Higgins <brendanhiggins at google.com>
+
+import re
+from datetime import datetime
+
+kunit_start_re = re.compile('printk: console .* enabled')
+kunit_end_re = re.compile('List of all partitions:')
+
+def isolate_kunit_output(kernel_output):
+	started = False
+	for line in kernel_output:
+		if kunit_start_re.match(line):
+			started = True
+		elif kunit_end_re.match(line):
+			break
+		elif started:
+			yield line
+
+def raw_output(kernel_output):
+	for line in kernel_output:
+		print(line)
+
+DIVIDER = "=" * 30
+
+RESET = '\033[0;0m'
+
+def red(text):
+	return '\033[1;31m' + text + RESET
+
+def yellow(text):
+	return '\033[1;33m' + text + RESET
+
+def green(text):
+	return '\033[1;32m' + text + RESET
+
+def print_with_timestamp(message):
+	print('[%s] %s' % (datetime.now().strftime('%H:%M:%S'), message))
+
+def print_log(log):
+	for m in log:
+		print_with_timestamp(m)
+
+def parse_run_tests(kernel_output):
+	test_case_output = re.compile('^kunit .*?: (.*)$')
+
+	test_module_success = re.compile('^kunit .*: all tests passed')
+	test_module_fail = re.compile('^kunit .*: one or more tests failed')
+
+	test_case_success = re.compile('^kunit (.*): (.*) passed')
+	test_case_fail = re.compile('^kunit (.*): (.*) failed')
+	test_case_crash = re.compile('^kunit (.*): (.*) crashed')
+
+	total_tests = set()
+	failed_tests = set()
+	crashed_tests = set()
+
+	def get_test_name(match):
+		return match.group(1) + ":" + match.group(2)
+
+	current_case_log = []
+	def end_one_test(match, log):
+		log.clear()
+		total_tests.add(get_test_name(match))
+
+	print_with_timestamp(DIVIDER)
+	for line in isolate_kunit_output(kernel_output):
+		# Ignore module output:
+		if (test_module_success.match(line) or
+		    test_module_fail.match(line)):
+			print_with_timestamp(DIVIDER)
+			continue
+
+		match = re.match(test_case_success, line)
+		if match:
+			print_with_timestamp(green("[PASSED] ") +
+					     get_test_name(match))
+			end_one_test(match, current_case_log)
+			continue
+
+		match = re.match(test_case_fail, line)
+		# Crashed tests will report as both failed and crashed. We only
+		# want to show and count it once.
+		if match and get_test_name(match) not in crashed_tests:
+			failed_tests.add(get_test_name(match))
+			print_with_timestamp(red("[FAILED] " +
+						 get_test_name(match)))
+			print_log(map(yellow, current_case_log))
+			print_with_timestamp("")
+			end_one_test(match, current_case_log)
+			continue
+
+		match = re.match(test_case_crash, line)
+		if match:
+			crashed_tests.add(get_test_name(match))
+			print_with_timestamp(yellow("[CRASH] " +
+						    get_test_name(match)))
+			print_log(current_case_log)
+			print_with_timestamp("")
+			end_one_test(match, current_case_log)
+			continue
+
+		# Strip off the `kunit module-name:` prefix
+		match = re.match(test_case_output, line)
+		if match:
+			current_case_log.append(match.group(1))
+		else:
+			current_case_log.append(line)
+
+	fmt = green if (len(failed_tests) + len(crashed_tests) == 0) else red
+	print_with_timestamp(
+		fmt("Testing complete. %d tests run. %d failed. %d crashed." %
+		    (len(total_tests), len(failed_tests), len(crashed_tests))))
+
-- 
2.21.0.593.g511ec345e18-goog

WARNING: multiple messages have this Message-ID (diff)
From: brendanhiggins@google.com (Brendan Higgins)
Subject: [PATCH v2 12/17] kunit: tool: add Python wrappers for running KUnit tests
Date: Wed,  1 May 2019 16:01:21 -0700	[thread overview]
Message-ID: <20190501230126.229218-13-brendanhiggins@google.com> (raw)
Message-ID: <20190501230121.rX7cpHLI9Tb4kfRVfegstliVkv0_-D9L1FCVs7w_poo@z> (raw)
In-Reply-To: <20190501230126.229218-1-brendanhiggins@google.com>

From: Felix Guo <felixguoxiuping@gmail.com>

The ultimate goal is to create minimal isolated test binaries; in the
meantime we are using UML to provide the infrastructure to run tests, so
define an abstract way to configure and run tests that allow us to
change the context in which tests are built without affecting the user.
This also makes pretty and dynamic error reporting, and a lot of other
nice features easier.

kunit_config.py:
  - parse .config and Kconfig files.

kunit_kernel.py: provides helper functions to:
  - configure the kernel using kunitconfig.
  - build the kernel with the appropriate configuration.
  - provide function to invoke the kernel and stream the output back.

Signed-off-by: Felix Guo <felixguoxiuping at gmail.com>
Signed-off-by: Brendan Higgins <brendanhiggins at google.com>
---
 tools/testing/kunit/.gitignore      |   3 +
 tools/testing/kunit/kunit.py        |  78 +++++++++++++++
 tools/testing/kunit/kunit_config.py |  66 +++++++++++++
 tools/testing/kunit/kunit_kernel.py | 148 ++++++++++++++++++++++++++++
 tools/testing/kunit/kunit_parser.py | 119 ++++++++++++++++++++++
 5 files changed, 414 insertions(+)
 create mode 100644 tools/testing/kunit/.gitignore
 create mode 100755 tools/testing/kunit/kunit.py
 create mode 100644 tools/testing/kunit/kunit_config.py
 create mode 100644 tools/testing/kunit/kunit_kernel.py
 create mode 100644 tools/testing/kunit/kunit_parser.py

diff --git a/tools/testing/kunit/.gitignore b/tools/testing/kunit/.gitignore
new file mode 100644
index 0000000000000..c791ff59a37a9
--- /dev/null
+++ b/tools/testing/kunit/.gitignore
@@ -0,0 +1,3 @@
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
\ No newline at end of file
diff --git a/tools/testing/kunit/kunit.py b/tools/testing/kunit/kunit.py
new file mode 100755
index 0000000000000..7413ec7351a20
--- /dev/null
+++ b/tools/testing/kunit/kunit.py
@@ -0,0 +1,78 @@
+#!/usr/bin/python3
+# SPDX-License-Identifier: GPL-2.0
+#
+# A thin wrapper on top of the KUnit Kernel
+#
+# Copyright (C) 2019, Google LLC.
+# Author: Felix Guo <felixguoxiuping at gmail.com>
+# Author: Brendan Higgins <brendanhiggins at google.com>
+
+import argparse
+import sys
+import os
+import time
+
+import kunit_config
+import kunit_kernel
+import kunit_parser
+
+parser = argparse.ArgumentParser(description='Runs KUnit tests.')
+
+parser.add_argument('--raw_output', help='don\'t format output from kernel',
+		    action='store_true')
+
+parser.add_argument('--timeout', help='maximum number of seconds to allow for '
+		    'all tests to run. This does not include time taken to '
+		    'build the tests.', type=int, default=300,
+		    metavar='timeout')
+
+parser.add_argument('--jobs',
+		    help='As in the make command, "Specifies  the number of '
+		    'jobs (commands) to run simultaneously."',
+		    type=int, default=8, metavar='jobs')
+
+parser.add_argument('--build_dir',
+		    help='As in the make command, it specifies the build '
+		    'directory.',
+		    type=str, default=None, metavar='build_dir')
+
+cli_args = parser.parse_args()
+
+linux = kunit_kernel.LinuxSourceTree()
+
+build_dir = None
+if cli_args.build_dir:
+	build_dir = cli_args.build_dir
+
+config_start = time.time()
+success = linux.build_reconfig(build_dir)
+config_end = time.time()
+if not success:
+	quit()
+
+kunit_parser.print_with_timestamp('Building KUnit Kernel ...')
+
+build_start = time.time()
+
+success = linux.build_um_kernel(jobs=cli_args.jobs, build_dir=build_dir)
+build_end = time.time()
+if not success:
+	quit()
+
+kunit_parser.print_with_timestamp('Starting KUnit Kernel ...')
+test_start = time.time()
+
+if cli_args.raw_output:
+	kunit_parser.raw_output(linux.run_kernel(timeout=cli_args.timeout,
+						 build_dir=build_dir))
+else:
+	kunit_parser.parse_run_tests(linux.run_kernel(timeout=cli_args.timeout,
+						      build_dir=build_dir))
+
+test_end = time.time()
+
+kunit_parser.print_with_timestamp((
+	"Elapsed time: %.3fs total, %.3fs configuring, %.3fs " +
+	"building, %.3fs running.\n") % (test_end - config_start,
+	config_end - config_start, build_end - build_start,
+	test_end - test_start))
diff --git a/tools/testing/kunit/kunit_config.py b/tools/testing/kunit/kunit_config.py
new file mode 100644
index 0000000000000..167f47d9ab8e4
--- /dev/null
+++ b/tools/testing/kunit/kunit_config.py
@@ -0,0 +1,66 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Builds a .config from a kunitconfig.
+#
+# Copyright (C) 2019, Google LLC.
+# Author: Felix Guo <felixguoxiuping at gmail.com>
+# Author: Brendan Higgins <brendanhiggins at google.com>
+
+import collections
+import re
+
+CONFIG_IS_NOT_SET_PATTERN = r'^# CONFIG_\w+ is not set$'
+CONFIG_PATTERN = r'^CONFIG_\w+=\S+$'
+
+KconfigEntryBase = collections.namedtuple('KconfigEntry', ['raw_entry'])
+
+
+class KconfigEntry(KconfigEntryBase):
+
+	def __str__(self) -> str:
+		return self.raw_entry
+
+
+class KconfigParseError(Exception):
+	"""Error parsing Kconfig defconfig or .config."""
+
+
+class Kconfig(object):
+	"""Represents defconfig or .config specified using the Kconfig language."""
+
+	def __init__(self):
+		self._entries = []
+
+	def entries(self):
+		return set(self._entries)
+
+	def add_entry(self, entry: KconfigEntry) -> None:
+		self._entries.append(entry)
+
+	def is_subset_of(self, other: "Kconfig") -> bool:
+		return self.entries().issubset(other.entries())
+
+	def write_to_file(self, path: str) -> None:
+		with open(path, 'w') as f:
+			for entry in self.entries():
+				f.write(str(entry) + '\n')
+
+	def parse_from_string(self, blob: str) -> None:
+		"""Parses a string containing KconfigEntrys and populates this Kconfig."""
+		self._entries = []
+		is_not_set_matcher = re.compile(CONFIG_IS_NOT_SET_PATTERN)
+		config_matcher = re.compile(CONFIG_PATTERN)
+		for line in blob.split('\n'):
+			line = line.strip()
+			if not line:
+				continue
+			elif config_matcher.match(line) or is_not_set_matcher.match(line):
+				self._entries.append(KconfigEntry(line))
+			elif line[0] == '#':
+				continue
+			else:
+				raise KconfigParseError('Failed to parse: ' + line)
+
+	def read_from_file(self, path: str) -> None:
+		with open(path, 'r') as f:
+			self.parse_from_string(f.read())
diff --git a/tools/testing/kunit/kunit_kernel.py b/tools/testing/kunit/kunit_kernel.py
new file mode 100644
index 0000000000000..07c0abf2f47df
--- /dev/null
+++ b/tools/testing/kunit/kunit_kernel.py
@@ -0,0 +1,148 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Runs UML kernel, collects output, and handles errors.
+#
+# Copyright (C) 2019, Google LLC.
+# Author: Felix Guo <felixguoxiuping at gmail.com>
+# Author: Brendan Higgins <brendanhiggins at google.com>
+
+
+import logging
+import subprocess
+import os
+
+import kunit_config
+
+KCONFIG_PATH = '.config'
+
+class ConfigError(Exception):
+	"""Represents an error trying to configure the Linux kernel."""
+
+
+class BuildError(Exception):
+	"""Represents an error trying to build the Linux kernel."""
+
+
+class LinuxSourceTreeOperations(object):
+	"""An abstraction over command line operations performed on a source tree."""
+
+	def make_mrproper(self):
+		try:
+			subprocess.check_output(['make', 'mrproper'])
+		except OSError as e:
+			raise ConfigError('Could not call make command: ' + e)
+		except subprocess.CalledProcessError as e:
+			raise ConfigError(e.output)
+
+	def make_olddefconfig(self, build_dir):
+		command = ['make', 'ARCH=um', 'olddefconfig']
+		if build_dir:
+			command += ['O=' + build_dir]
+		try:
+			subprocess.check_output(command)
+		except OSError as e:
+			raise ConfigError('Could not call make command: ' + e)
+		except subprocess.CalledProcessError as e:
+			raise ConfigError(e.output)
+
+	def make(self, jobs, build_dir):
+		command = ['make', 'ARCH=um', '--jobs=' + str(jobs)]
+		if build_dir:
+			command += ['O=' + build_dir]
+		try:
+			subprocess.check_output(command)
+		except OSError as e:
+			raise BuildError('Could not call execute make: ' + e)
+		except subprocess.CalledProcessError as e:
+			raise BuildError(e.output)
+
+	def linux_bin(self, params, timeout, build_dir):
+		"""Runs the Linux UML binary. Must be named 'linux'."""
+		linux_bin = './linux'
+		if build_dir:
+			linux_bin = os.path.join(build_dir, 'linux')
+		process = subprocess.Popen(
+			[linux_bin] + params,
+			stdin=subprocess.PIPE,
+			stdout=subprocess.PIPE,
+			stderr=subprocess.PIPE)
+		process.wait(timeout=timeout)
+		return process
+
+
+def get_kconfig_path(build_dir):
+	kconfig_path = KCONFIG_PATH
+	if build_dir:
+		kconfig_path = os.path.join(build_dir, KCONFIG_PATH)
+	return kconfig_path
+
+class LinuxSourceTree(object):
+	"""Represents a Linux kernel source tree with KUnit tests."""
+
+	def __init__(self):
+		self._kconfig = kunit_config.Kconfig()
+		self._kconfig.read_from_file('kunitconfig')
+		self._ops = LinuxSourceTreeOperations()
+
+	def clean(self):
+		try:
+			self._ops.make_mrproper()
+		except ConfigError as e:
+			logging.error(e)
+			return False
+		return True
+
+	def build_config(self, build_dir):
+		kconfig_path = get_kconfig_path(build_dir)
+		if build_dir and not os.path.exists(build_dir):
+			os.mkdir(build_dir)
+		self._kconfig.write_to_file(kconfig_path)
+		try:
+			self._ops.make_olddefconfig(build_dir)
+		except ConfigError as e:
+			logging.error(e)
+			return False
+		validated_kconfig = kunit_config.Kconfig()
+		validated_kconfig.read_from_file(kconfig_path)
+		if not self._kconfig.is_subset_of(validated_kconfig):
+			logging.error('Provided Kconfig is not contained in validated .config!')
+			return False
+		return True
+
+	def build_reconfig(self, build_dir):
+		"""Creates a new .config if it is not a subset of the kunitconfig."""
+		kconfig_path = get_kconfig_path(build_dir)
+		if os.path.exists(kconfig_path):
+			existing_kconfig = kunit_config.Kconfig()
+			existing_kconfig.read_from_file(kconfig_path)
+			if not self._kconfig.is_subset_of(existing_kconfig):
+				print('Regenerating .config ...')
+				os.remove(kconfig_path)
+				return self.build_config(build_dir)
+			else:
+				return True
+		else:
+			print('Generating .config ...')
+			return self.build_config(build_dir)
+
+	def build_um_kernel(self, jobs, build_dir):
+		try:
+			self._ops.make_olddefconfig(build_dir)
+			self._ops.make(jobs, build_dir)
+		except (ConfigError, BuildError) as e:
+			logging.error(e)
+			return False
+		used_kconfig = kunit_config.Kconfig()
+		used_kconfig.read_from_file(get_kconfig_path(build_dir))
+		if not self._kconfig.is_subset_of(used_kconfig):
+			logging.error('Provided Kconfig is not contained in final config!')
+			return False
+		return True
+
+	def run_kernel(self, args=[], timeout=None, build_dir=None):
+		args.extend(['mem=256M'])
+		process = self._ops.linux_bin(args, timeout, build_dir)
+		with open('test.log', 'w') as f:
+			for line in process.stdout:
+				f.write(line.rstrip().decode('ascii') + '\n')
+				yield line.rstrip().decode('ascii')
diff --git a/tools/testing/kunit/kunit_parser.py b/tools/testing/kunit/kunit_parser.py
new file mode 100644
index 0000000000000..6c81d4dcfabb5
--- /dev/null
+++ b/tools/testing/kunit/kunit_parser.py
@@ -0,0 +1,119 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Parses test results from a kernel dmesg log.
+#
+# Copyright (C) 2019, Google LLC.
+# Author: Felix Guo <felixguoxiuping at gmail.com>
+# Author: Brendan Higgins <brendanhiggins at google.com>
+
+import re
+from datetime import datetime
+
+kunit_start_re = re.compile('printk: console .* enabled')
+kunit_end_re = re.compile('List of all partitions:')
+
+def isolate_kunit_output(kernel_output):
+	started = False
+	for line in kernel_output:
+		if kunit_start_re.match(line):
+			started = True
+		elif kunit_end_re.match(line):
+			break
+		elif started:
+			yield line
+
+def raw_output(kernel_output):
+	for line in kernel_output:
+		print(line)
+
+DIVIDER = "=" * 30
+
+RESET = '\033[0;0m'
+
+def red(text):
+	return '\033[1;31m' + text + RESET
+
+def yellow(text):
+	return '\033[1;33m' + text + RESET
+
+def green(text):
+	return '\033[1;32m' + text + RESET
+
+def print_with_timestamp(message):
+	print('[%s] %s' % (datetime.now().strftime('%H:%M:%S'), message))
+
+def print_log(log):
+	for m in log:
+		print_with_timestamp(m)
+
+def parse_run_tests(kernel_output):
+	test_case_output = re.compile('^kunit .*?: (.*)$')
+
+	test_module_success = re.compile('^kunit .*: all tests passed')
+	test_module_fail = re.compile('^kunit .*: one or more tests failed')
+
+	test_case_success = re.compile('^kunit (.*): (.*) passed')
+	test_case_fail = re.compile('^kunit (.*): (.*) failed')
+	test_case_crash = re.compile('^kunit (.*): (.*) crashed')
+
+	total_tests = set()
+	failed_tests = set()
+	crashed_tests = set()
+
+	def get_test_name(match):
+		return match.group(1) + ":" + match.group(2)
+
+	current_case_log = []
+	def end_one_test(match, log):
+		log.clear()
+		total_tests.add(get_test_name(match))
+
+	print_with_timestamp(DIVIDER)
+	for line in isolate_kunit_output(kernel_output):
+		# Ignore module output:
+		if (test_module_success.match(line) or
+		    test_module_fail.match(line)):
+			print_with_timestamp(DIVIDER)
+			continue
+
+		match = re.match(test_case_success, line)
+		if match:
+			print_with_timestamp(green("[PASSED] ") +
+					     get_test_name(match))
+			end_one_test(match, current_case_log)
+			continue
+
+		match = re.match(test_case_fail, line)
+		# Crashed tests will report as both failed and crashed. We only
+		# want to show and count it once.
+		if match and get_test_name(match) not in crashed_tests:
+			failed_tests.add(get_test_name(match))
+			print_with_timestamp(red("[FAILED] " +
+						 get_test_name(match)))
+			print_log(map(yellow, current_case_log))
+			print_with_timestamp("")
+			end_one_test(match, current_case_log)
+			continue
+
+		match = re.match(test_case_crash, line)
+		if match:
+			crashed_tests.add(get_test_name(match))
+			print_with_timestamp(yellow("[CRASH] " +
+						    get_test_name(match)))
+			print_log(current_case_log)
+			print_with_timestamp("")
+			end_one_test(match, current_case_log)
+			continue
+
+		# Strip off the `kunit module-name:` prefix
+		match = re.match(test_case_output, line)
+		if match:
+			current_case_log.append(match.group(1))
+		else:
+			current_case_log.append(line)
+
+	fmt = green if (len(failed_tests) + len(crashed_tests) == 0) else red
+	print_with_timestamp(
+		fmt("Testing complete. %d tests run. %d failed. %d crashed." %
+		    (len(total_tests), len(failed_tests), len(crashed_tests))))
+
-- 
2.21.0.593.g511ec345e18-goog

WARNING: multiple messages have this Message-ID (diff)
From: Brendan Higgins <brendanhiggins@google.com>
To: frowand.list@gmail.com, gregkh@linuxfoundation.org,
	keescook@google.com, kieran.bingham@ideasonboard.com,
	mcgrof@kernel.org, robh@kernel.org, sboyd@kernel.org,
	shuah@kernel.org
Cc: pmladek@suse.com, linux-doc@vger.kernel.org, amir73il@gmail.com,
	Brendan Higgins <brendanhiggins@google.com>,
	dri-devel@lists.freedesktop.org, Alexander.Levin@microsoft.com,
	linux-kselftest@vger.kernel.org, linux-nvdimm@lists.01.org,
	khilman@baylibre.com, knut.omang@oracle.com,
	Felix Guo <felixguoxiuping@gmail.com>,
	wfg@linux.intel.com, joel@jms.id.au, rientjes@google.com,
	jdike@addtoit.com, dan.carpenter@oracle.com,
	devicetree@vger.kernel.org, linux-kbuild@vger.kernel.org,
	Tim.Bird@sony.com, linux-um@lists.infradead.org,
	rostedt@goodmis.org, julia.lawall@lip6.fr,
	dan.j.williams@intel.com, kunit-dev@googlegroups.com,
	richard@nod.at, linux-kernel@vger.kernel.org, daniel@ffwll.ch,
	mpe@ellerman.id.au, linux-fsdevel@vger.kernel.org,
	logang@deltatee.com
Subject: [PATCH v2 12/17] kunit: tool: add Python wrappers for running KUnit tests
Date: Wed,  1 May 2019 16:01:21 -0700	[thread overview]
Message-ID: <20190501230126.229218-13-brendanhiggins@google.com> (raw)
In-Reply-To: <20190501230126.229218-1-brendanhiggins@google.com>

From: Felix Guo <felixguoxiuping@gmail.com>

The ultimate goal is to create minimal isolated test binaries; in the
meantime we are using UML to provide the infrastructure to run tests, so
define an abstract way to configure and run tests that allow us to
change the context in which tests are built without affecting the user.
This also makes pretty and dynamic error reporting, and a lot of other
nice features easier.

kunit_config.py:
  - parse .config and Kconfig files.

kunit_kernel.py: provides helper functions to:
  - configure the kernel using kunitconfig.
  - build the kernel with the appropriate configuration.
  - provide function to invoke the kernel and stream the output back.

Signed-off-by: Felix Guo <felixguoxiuping@gmail.com>
Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
---
 tools/testing/kunit/.gitignore      |   3 +
 tools/testing/kunit/kunit.py        |  78 +++++++++++++++
 tools/testing/kunit/kunit_config.py |  66 +++++++++++++
 tools/testing/kunit/kunit_kernel.py | 148 ++++++++++++++++++++++++++++
 tools/testing/kunit/kunit_parser.py | 119 ++++++++++++++++++++++
 5 files changed, 414 insertions(+)
 create mode 100644 tools/testing/kunit/.gitignore
 create mode 100755 tools/testing/kunit/kunit.py
 create mode 100644 tools/testing/kunit/kunit_config.py
 create mode 100644 tools/testing/kunit/kunit_kernel.py
 create mode 100644 tools/testing/kunit/kunit_parser.py

diff --git a/tools/testing/kunit/.gitignore b/tools/testing/kunit/.gitignore
new file mode 100644
index 0000000000000..c791ff59a37a9
--- /dev/null
+++ b/tools/testing/kunit/.gitignore
@@ -0,0 +1,3 @@
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
\ No newline at end of file
diff --git a/tools/testing/kunit/kunit.py b/tools/testing/kunit/kunit.py
new file mode 100755
index 0000000000000..7413ec7351a20
--- /dev/null
+++ b/tools/testing/kunit/kunit.py
@@ -0,0 +1,78 @@
+#!/usr/bin/python3
+# SPDX-License-Identifier: GPL-2.0
+#
+# A thin wrapper on top of the KUnit Kernel
+#
+# Copyright (C) 2019, Google LLC.
+# Author: Felix Guo <felixguoxiuping@gmail.com>
+# Author: Brendan Higgins <brendanhiggins@google.com>
+
+import argparse
+import sys
+import os
+import time
+
+import kunit_config
+import kunit_kernel
+import kunit_parser
+
+parser = argparse.ArgumentParser(description='Runs KUnit tests.')
+
+parser.add_argument('--raw_output', help='don\'t format output from kernel',
+		    action='store_true')
+
+parser.add_argument('--timeout', help='maximum number of seconds to allow for '
+		    'all tests to run. This does not include time taken to '
+		    'build the tests.', type=int, default=300,
+		    metavar='timeout')
+
+parser.add_argument('--jobs',
+		    help='As in the make command, "Specifies  the number of '
+		    'jobs (commands) to run simultaneously."',
+		    type=int, default=8, metavar='jobs')
+
+parser.add_argument('--build_dir',
+		    help='As in the make command, it specifies the build '
+		    'directory.',
+		    type=str, default=None, metavar='build_dir')
+
+cli_args = parser.parse_args()
+
+linux = kunit_kernel.LinuxSourceTree()
+
+build_dir = None
+if cli_args.build_dir:
+	build_dir = cli_args.build_dir
+
+config_start = time.time()
+success = linux.build_reconfig(build_dir)
+config_end = time.time()
+if not success:
+	quit()
+
+kunit_parser.print_with_timestamp('Building KUnit Kernel ...')
+
+build_start = time.time()
+
+success = linux.build_um_kernel(jobs=cli_args.jobs, build_dir=build_dir)
+build_end = time.time()
+if not success:
+	quit()
+
+kunit_parser.print_with_timestamp('Starting KUnit Kernel ...')
+test_start = time.time()
+
+if cli_args.raw_output:
+	kunit_parser.raw_output(linux.run_kernel(timeout=cli_args.timeout,
+						 build_dir=build_dir))
+else:
+	kunit_parser.parse_run_tests(linux.run_kernel(timeout=cli_args.timeout,
+						      build_dir=build_dir))
+
+test_end = time.time()
+
+kunit_parser.print_with_timestamp((
+	"Elapsed time: %.3fs total, %.3fs configuring, %.3fs " +
+	"building, %.3fs running.\n") % (test_end - config_start,
+	config_end - config_start, build_end - build_start,
+	test_end - test_start))
diff --git a/tools/testing/kunit/kunit_config.py b/tools/testing/kunit/kunit_config.py
new file mode 100644
index 0000000000000..167f47d9ab8e4
--- /dev/null
+++ b/tools/testing/kunit/kunit_config.py
@@ -0,0 +1,66 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Builds a .config from a kunitconfig.
+#
+# Copyright (C) 2019, Google LLC.
+# Author: Felix Guo <felixguoxiuping@gmail.com>
+# Author: Brendan Higgins <brendanhiggins@google.com>
+
+import collections
+import re
+
+CONFIG_IS_NOT_SET_PATTERN = r'^# CONFIG_\w+ is not set$'
+CONFIG_PATTERN = r'^CONFIG_\w+=\S+$'
+
+KconfigEntryBase = collections.namedtuple('KconfigEntry', ['raw_entry'])
+
+
+class KconfigEntry(KconfigEntryBase):
+
+	def __str__(self) -> str:
+		return self.raw_entry
+
+
+class KconfigParseError(Exception):
+	"""Error parsing Kconfig defconfig or .config."""
+
+
+class Kconfig(object):
+	"""Represents defconfig or .config specified using the Kconfig language."""
+
+	def __init__(self):
+		self._entries = []
+
+	def entries(self):
+		return set(self._entries)
+
+	def add_entry(self, entry: KconfigEntry) -> None:
+		self._entries.append(entry)
+
+	def is_subset_of(self, other: "Kconfig") -> bool:
+		return self.entries().issubset(other.entries())
+
+	def write_to_file(self, path: str) -> None:
+		with open(path, 'w') as f:
+			for entry in self.entries():
+				f.write(str(entry) + '\n')
+
+	def parse_from_string(self, blob: str) -> None:
+		"""Parses a string containing KconfigEntrys and populates this Kconfig."""
+		self._entries = []
+		is_not_set_matcher = re.compile(CONFIG_IS_NOT_SET_PATTERN)
+		config_matcher = re.compile(CONFIG_PATTERN)
+		for line in blob.split('\n'):
+			line = line.strip()
+			if not line:
+				continue
+			elif config_matcher.match(line) or is_not_set_matcher.match(line):
+				self._entries.append(KconfigEntry(line))
+			elif line[0] == '#':
+				continue
+			else:
+				raise KconfigParseError('Failed to parse: ' + line)
+
+	def read_from_file(self, path: str) -> None:
+		with open(path, 'r') as f:
+			self.parse_from_string(f.read())
diff --git a/tools/testing/kunit/kunit_kernel.py b/tools/testing/kunit/kunit_kernel.py
new file mode 100644
index 0000000000000..07c0abf2f47df
--- /dev/null
+++ b/tools/testing/kunit/kunit_kernel.py
@@ -0,0 +1,148 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Runs UML kernel, collects output, and handles errors.
+#
+# Copyright (C) 2019, Google LLC.
+# Author: Felix Guo <felixguoxiuping@gmail.com>
+# Author: Brendan Higgins <brendanhiggins@google.com>
+
+
+import logging
+import subprocess
+import os
+
+import kunit_config
+
+KCONFIG_PATH = '.config'
+
+class ConfigError(Exception):
+	"""Represents an error trying to configure the Linux kernel."""
+
+
+class BuildError(Exception):
+	"""Represents an error trying to build the Linux kernel."""
+
+
+class LinuxSourceTreeOperations(object):
+	"""An abstraction over command line operations performed on a source tree."""
+
+	def make_mrproper(self):
+		try:
+			subprocess.check_output(['make', 'mrproper'])
+		except OSError as e:
+			raise ConfigError('Could not call make command: ' + e)
+		except subprocess.CalledProcessError as e:
+			raise ConfigError(e.output)
+
+	def make_olddefconfig(self, build_dir):
+		command = ['make', 'ARCH=um', 'olddefconfig']
+		if build_dir:
+			command += ['O=' + build_dir]
+		try:
+			subprocess.check_output(command)
+		except OSError as e:
+			raise ConfigError('Could not call make command: ' + e)
+		except subprocess.CalledProcessError as e:
+			raise ConfigError(e.output)
+
+	def make(self, jobs, build_dir):
+		command = ['make', 'ARCH=um', '--jobs=' + str(jobs)]
+		if build_dir:
+			command += ['O=' + build_dir]
+		try:
+			subprocess.check_output(command)
+		except OSError as e:
+			raise BuildError('Could not call execute make: ' + e)
+		except subprocess.CalledProcessError as e:
+			raise BuildError(e.output)
+
+	def linux_bin(self, params, timeout, build_dir):
+		"""Runs the Linux UML binary. Must be named 'linux'."""
+		linux_bin = './linux'
+		if build_dir:
+			linux_bin = os.path.join(build_dir, 'linux')
+		process = subprocess.Popen(
+			[linux_bin] + params,
+			stdin=subprocess.PIPE,
+			stdout=subprocess.PIPE,
+			stderr=subprocess.PIPE)
+		process.wait(timeout=timeout)
+		return process
+
+
+def get_kconfig_path(build_dir):
+	kconfig_path = KCONFIG_PATH
+	if build_dir:
+		kconfig_path = os.path.join(build_dir, KCONFIG_PATH)
+	return kconfig_path
+
+class LinuxSourceTree(object):
+	"""Represents a Linux kernel source tree with KUnit tests."""
+
+	def __init__(self):
+		self._kconfig = kunit_config.Kconfig()
+		self._kconfig.read_from_file('kunitconfig')
+		self._ops = LinuxSourceTreeOperations()
+
+	def clean(self):
+		try:
+			self._ops.make_mrproper()
+		except ConfigError as e:
+			logging.error(e)
+			return False
+		return True
+
+	def build_config(self, build_dir):
+		kconfig_path = get_kconfig_path(build_dir)
+		if build_dir and not os.path.exists(build_dir):
+			os.mkdir(build_dir)
+		self._kconfig.write_to_file(kconfig_path)
+		try:
+			self._ops.make_olddefconfig(build_dir)
+		except ConfigError as e:
+			logging.error(e)
+			return False
+		validated_kconfig = kunit_config.Kconfig()
+		validated_kconfig.read_from_file(kconfig_path)
+		if not self._kconfig.is_subset_of(validated_kconfig):
+			logging.error('Provided Kconfig is not contained in validated .config!')
+			return False
+		return True
+
+	def build_reconfig(self, build_dir):
+		"""Creates a new .config if it is not a subset of the kunitconfig."""
+		kconfig_path = get_kconfig_path(build_dir)
+		if os.path.exists(kconfig_path):
+			existing_kconfig = kunit_config.Kconfig()
+			existing_kconfig.read_from_file(kconfig_path)
+			if not self._kconfig.is_subset_of(existing_kconfig):
+				print('Regenerating .config ...')
+				os.remove(kconfig_path)
+				return self.build_config(build_dir)
+			else:
+				return True
+		else:
+			print('Generating .config ...')
+			return self.build_config(build_dir)
+
+	def build_um_kernel(self, jobs, build_dir):
+		try:
+			self._ops.make_olddefconfig(build_dir)
+			self._ops.make(jobs, build_dir)
+		except (ConfigError, BuildError) as e:
+			logging.error(e)
+			return False
+		used_kconfig = kunit_config.Kconfig()
+		used_kconfig.read_from_file(get_kconfig_path(build_dir))
+		if not self._kconfig.is_subset_of(used_kconfig):
+			logging.error('Provided Kconfig is not contained in final config!')
+			return False
+		return True
+
+	def run_kernel(self, args=[], timeout=None, build_dir=None):
+		args.extend(['mem=256M'])
+		process = self._ops.linux_bin(args, timeout, build_dir)
+		with open('test.log', 'w') as f:
+			for line in process.stdout:
+				f.write(line.rstrip().decode('ascii') + '\n')
+				yield line.rstrip().decode('ascii')
diff --git a/tools/testing/kunit/kunit_parser.py b/tools/testing/kunit/kunit_parser.py
new file mode 100644
index 0000000000000..6c81d4dcfabb5
--- /dev/null
+++ b/tools/testing/kunit/kunit_parser.py
@@ -0,0 +1,119 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Parses test results from a kernel dmesg log.
+#
+# Copyright (C) 2019, Google LLC.
+# Author: Felix Guo <felixguoxiuping@gmail.com>
+# Author: Brendan Higgins <brendanhiggins@google.com>
+
+import re
+from datetime import datetime
+
+kunit_start_re = re.compile('printk: console .* enabled')
+kunit_end_re = re.compile('List of all partitions:')
+
+def isolate_kunit_output(kernel_output):
+	started = False
+	for line in kernel_output:
+		if kunit_start_re.match(line):
+			started = True
+		elif kunit_end_re.match(line):
+			break
+		elif started:
+			yield line
+
+def raw_output(kernel_output):
+	for line in kernel_output:
+		print(line)
+
+DIVIDER = "=" * 30
+
+RESET = '\033[0;0m'
+
+def red(text):
+	return '\033[1;31m' + text + RESET
+
+def yellow(text):
+	return '\033[1;33m' + text + RESET
+
+def green(text):
+	return '\033[1;32m' + text + RESET
+
+def print_with_timestamp(message):
+	print('[%s] %s' % (datetime.now().strftime('%H:%M:%S'), message))
+
+def print_log(log):
+	for m in log:
+		print_with_timestamp(m)
+
+def parse_run_tests(kernel_output):
+	test_case_output = re.compile('^kunit .*?: (.*)$')
+
+	test_module_success = re.compile('^kunit .*: all tests passed')
+	test_module_fail = re.compile('^kunit .*: one or more tests failed')
+
+	test_case_success = re.compile('^kunit (.*): (.*) passed')
+	test_case_fail = re.compile('^kunit (.*): (.*) failed')
+	test_case_crash = re.compile('^kunit (.*): (.*) crashed')
+
+	total_tests = set()
+	failed_tests = set()
+	crashed_tests = set()
+
+	def get_test_name(match):
+		return match.group(1) + ":" + match.group(2)
+
+	current_case_log = []
+	def end_one_test(match, log):
+		log.clear()
+		total_tests.add(get_test_name(match))
+
+	print_with_timestamp(DIVIDER)
+	for line in isolate_kunit_output(kernel_output):
+		# Ignore module output:
+		if (test_module_success.match(line) or
+		    test_module_fail.match(line)):
+			print_with_timestamp(DIVIDER)
+			continue
+
+		match = re.match(test_case_success, line)
+		if match:
+			print_with_timestamp(green("[PASSED] ") +
+					     get_test_name(match))
+			end_one_test(match, current_case_log)
+			continue
+
+		match = re.match(test_case_fail, line)
+		# Crashed tests will report as both failed and crashed. We only
+		# want to show and count it once.
+		if match and get_test_name(match) not in crashed_tests:
+			failed_tests.add(get_test_name(match))
+			print_with_timestamp(red("[FAILED] " +
+						 get_test_name(match)))
+			print_log(map(yellow, current_case_log))
+			print_with_timestamp("")
+			end_one_test(match, current_case_log)
+			continue
+
+		match = re.match(test_case_crash, line)
+		if match:
+			crashed_tests.add(get_test_name(match))
+			print_with_timestamp(yellow("[CRASH] " +
+						    get_test_name(match)))
+			print_log(current_case_log)
+			print_with_timestamp("")
+			end_one_test(match, current_case_log)
+			continue
+
+		# Strip off the `kunit module-name:` prefix
+		match = re.match(test_case_output, line)
+		if match:
+			current_case_log.append(match.group(1))
+		else:
+			current_case_log.append(line)
+
+	fmt = green if (len(failed_tests) + len(crashed_tests) == 0) else red
+	print_with_timestamp(
+		fmt("Testing complete. %d tests run. %d failed. %d crashed." %
+		    (len(total_tests), len(failed_tests), len(crashed_tests))))
+
-- 
2.21.0.593.g511ec345e18-goog


_______________________________________________
linux-um mailing list
linux-um@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-um


  parent reply	other threads:[~2019-05-01 23:01 UTC|newest]

Thread overview: 694+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-05-01 23:01 [PATCH v2 00/17] kunit: introduce KUnit, the Linux kernel unit testing framework Brendan Higgins
2019-05-01 23:01 ` Brendan Higgins
2019-05-01 23:01 ` Brendan Higgins
2019-05-01 23:01 ` brendanhiggins
2019-05-01 23:01 ` [PATCH v2 01/17] kunit: test: add KUnit test runner core Brendan Higgins
2019-05-01 23:01   ` Brendan Higgins
2019-05-01 23:01   ` Brendan Higgins
2019-05-01 23:01   ` brendanhiggins
2019-05-01 23:01   ` Brendan Higgins
2019-05-01 23:01 ` [PATCH v2 02/17] kunit: test: add test resource management API Brendan Higgins
2019-05-01 23:01   ` Brendan Higgins
2019-05-01 23:01   ` Brendan Higgins
2019-05-01 23:01   ` brendanhiggins
2019-05-01 23:01 ` [PATCH v2 03/17] kunit: test: add string_stream a std::stream like string builder Brendan Higgins
2019-05-01 23:01   ` Brendan Higgins
2019-05-01 23:01   ` Brendan Higgins
2019-05-01 23:01   ` brendanhiggins
     [not found]   ` <20190501230126.229218-4-brendanhiggins-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
2019-05-03  1:26     ` shuah
2019-05-03  1:26       ` shuah
2019-05-03  1:26       ` shuah
2019-05-03  1:26       ` shuah
2019-05-03  1:26       ` shuah
2019-05-03  4:37       ` Brendan Higgins
2019-05-03  4:37         ` Brendan Higgins
2019-05-03  4:37         ` Brendan Higgins
2019-05-03  4:37         ` brendanhiggins
2019-05-03  4:37         ` Brendan Higgins
2019-05-01 23:01 ` [PATCH v2 04/17] kunit: test: add kunit_stream a std::stream like logger Brendan Higgins
2019-05-01 23:01   ` Brendan Higgins
2019-05-01 23:01   ` Brendan Higgins
2019-05-01 23:01   ` brendanhiggins
2019-05-02 11:00   ` Greg KH
2019-05-02 11:00     ` Greg KH
2019-05-02 11:00     ` Greg KH
2019-05-02 11:00     ` gregkh
2019-05-02 11:00     ` Greg KH
2019-05-02 20:25     ` Brendan Higgins
2019-05-02 20:25       ` Brendan Higgins
2019-05-02 20:25       ` Brendan Higgins
2019-05-02 20:25       ` brendanhiggins
2019-05-02 20:25       ` Brendan Higgins
2019-05-02 21:18       ` Frank Rowand
2019-05-02 21:18         ` Frank Rowand
2019-05-02 21:18         ` Frank Rowand
2019-05-02 21:18         ` frowand.list
2019-05-02 21:18         ` Frank Rowand
2019-05-02 21:18         ` Frank Rowand
2019-05-03  1:50   ` shuah
2019-05-03  1:50     ` shuah
2019-05-03  1:50     ` shuah
2019-05-03  1:50     ` shuah
2019-05-03  5:48     ` Brendan Higgins
2019-05-03  5:48       ` Brendan Higgins
2019-05-03  5:48       ` Brendan Higgins
2019-05-03  5:48       ` brendanhiggins
2019-05-03  5:48       ` Brendan Higgins
2019-05-01 23:01 ` [PATCH v2 05/17] kunit: test: add the concept of expectations Brendan Higgins
2019-05-01 23:01   ` Brendan Higgins
2019-05-01 23:01   ` Brendan Higgins
2019-05-01 23:01   ` brendanhiggins
2019-05-01 23:01 ` [PATCH v2 06/17] kbuild: enable building KUnit Brendan Higgins
2019-05-01 23:01   ` Brendan Higgins
2019-05-01 23:01   ` brendanhiggins
2019-05-10  3:03   ` Masahiro Yamada
2019-05-10  3:03     ` Masahiro Yamada
2019-05-10  3:03     ` Masahiro Yamada
2019-05-10  3:03     ` Masahiro Yamada
2019-05-10  3:03     ` yamada.masahiro
2019-05-10  3:03     ` Masahiro Yamada
     [not found]     ` <CAK7LNAQ+SRMn8UFjW1dZv_TrL0qjD2v2S=rXgtUpiA-urr1DDA-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2019-05-10 10:27       ` Brendan Higgins
2019-05-10 10:27         ` Brendan Higgins
2019-05-10 10:27         ` Brendan Higgins
2019-05-10 10:27         ` Brendan Higgins
2019-05-10 10:27         ` brendanhiggins
2019-05-10 10:27         ` Brendan Higgins
2019-05-10 10:30         ` Masahiro Yamada
2019-05-10 10:30           ` Masahiro Yamada
2019-05-10 10:30           ` Masahiro Yamada
2019-05-10 10:30           ` Masahiro Yamada
2019-05-10 10:30           ` yamada.masahiro
2019-05-10 10:30           ` Masahiro Yamada
2019-05-10 10:33           ` Brendan Higgins
2019-05-10 10:33             ` Brendan Higgins
2019-05-10 10:33             ` Brendan Higgins
2019-05-10 10:33             ` Brendan Higgins
2019-05-10 10:33             ` brendanhiggins
2019-05-10 10:33             ` Brendan Higgins
2019-05-01 23:01 ` [PATCH v2 07/17] kunit: test: add initial tests Brendan Higgins
2019-05-01 23:01   ` Brendan Higgins
2019-05-01 23:01   ` Brendan Higgins
2019-05-01 23:01   ` brendanhiggins
2019-05-02 10:58   ` Greg KH
2019-05-02 10:58     ` Greg KH
2019-05-02 10:58     ` Greg KH
2019-05-02 10:58     ` gregkh
2019-05-02 10:58     ` Greg KH
2019-05-02 20:30     ` Brendan Higgins
2019-05-02 20:30       ` Brendan Higgins
2019-05-02 20:30       ` Brendan Higgins
2019-05-02 20:30       ` brendanhiggins
2019-05-02 20:30       ` Brendan Higgins
2019-05-03  1:27   ` shuah
2019-05-03  1:27     ` shuah
2019-05-03  1:27     ` shuah
2019-05-03  1:27     ` shuah
     [not found]     ` <d4934565-9b41-880e-3bbe-984224b50fac-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
2019-05-03  5:18       ` Brendan Higgins
2019-05-03  5:18         ` Brendan Higgins
2019-05-03  5:18         ` Brendan Higgins
2019-05-03  5:18         ` brendanhiggins
2019-05-03  5:18         ` Brendan Higgins
2019-05-01 23:01 ` [PATCH v2 08/17] kunit: test: add support for test abort Brendan Higgins
2019-05-01 23:01   ` Brendan Higgins
2019-05-01 23:01   ` brendanhiggins
     [not found]   ` <20190501230126.229218-9-brendanhiggins-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
2019-05-03  3:14     ` Logan Gunthorpe
2019-05-03  3:14       ` Logan Gunthorpe
2019-05-03  3:14       ` Logan Gunthorpe
2019-05-03  3:14       ` logang
2019-05-03  3:14       ` Logan Gunthorpe
2019-05-03  6:48       ` Brendan Higgins
2019-05-03  6:48         ` Brendan Higgins
2019-05-03  6:48         ` Brendan Higgins
2019-05-03  6:48         ` brendanhiggins
2019-05-03  6:48         ` Brendan Higgins
     [not found]         ` <CAFd5g47hxAd=+72xbPJbWPdZCXRXmtLpsGhUh=zc7MSwfcaGJQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2019-05-03 12:33           ` Logan Gunthorpe
2019-05-03 12:33             ` Logan Gunthorpe
2019-05-03 12:33             ` Logan Gunthorpe
2019-05-03 12:33             ` logang
2019-05-03 12:33             ` Logan Gunthorpe
2019-05-06  8:48             ` Brendan Higgins
2019-05-06  8:48               ` Brendan Higgins
2019-05-06  8:48               ` Brendan Higgins
2019-05-06  8:48               ` brendanhiggins
2019-05-06  8:48               ` Brendan Higgins
2019-05-01 23:01 ` [PATCH v2 09/17] kunit: test: add tests for kunit " Brendan Higgins
2019-05-01 23:01   ` Brendan Higgins
2019-05-01 23:01   ` Brendan Higgins
2019-05-01 23:01   ` brendanhiggins
2019-05-01 23:01 ` [PATCH v2 10/17] kunit: test: add the concept of assertions Brendan Higgins
2019-05-01 23:01   ` Brendan Higgins
2019-05-01 23:01   ` Brendan Higgins
2019-05-01 23:01   ` brendanhiggins
2019-05-01 23:01 ` [PATCH v2 11/17] kunit: test: add test managed resource tests Brendan Higgins
2019-05-01 23:01   ` Brendan Higgins
2019-05-01 23:01   ` Brendan Higgins
2019-05-01 23:01   ` brendanhiggins
2019-05-03 14:34   ` shuah
2019-05-03 14:34     ` shuah
2019-05-03 14:34     ` shuah
2019-05-03 14:34     ` shuah
2019-05-06  9:03     ` Brendan Higgins
2019-05-06  9:03       ` Brendan Higgins
2019-05-06  9:03       ` brendanhiggins
2019-05-06  9:03       ` Brendan Higgins
2019-05-01 23:01 ` Brendan Higgins [this message]
2019-05-01 23:01   ` [PATCH v2 12/17] kunit: tool: add Python wrappers for running KUnit tests Brendan Higgins
2019-05-01 23:01   ` Brendan Higgins
2019-05-01 23:01   ` brendanhiggins
2019-05-02 11:02   ` Greg KH
2019-05-02 11:02     ` Greg KH
2019-05-02 11:02     ` Greg KH
2019-05-02 11:02     ` gregkh
2019-05-02 11:02     ` Greg KH
2019-05-02 18:07     ` Brendan Higgins
2019-05-02 18:07       ` Brendan Higgins
2019-05-02 18:07       ` Brendan Higgins
2019-05-02 18:07       ` brendanhiggins
2019-05-02 18:07       ` Brendan Higgins
2019-05-02 21:16       ` Frank Rowand
2019-05-02 21:16         ` Frank Rowand
2019-05-02 21:16         ` Frank Rowand
2019-05-02 21:16         ` frowand.list
2019-05-02 21:16         ` Frank Rowand
     [not found]         ` <a49c5088-a821-210c-66de-f422536f5b01-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2019-05-02 23:45           ` Brendan Higgins
2019-05-02 23:45             ` Brendan Higgins
2019-05-02 23:45             ` Brendan Higgins
2019-05-02 23:45             ` brendanhiggins
2019-05-02 23:45             ` Brendan Higgins
     [not found]             ` <CAFd5g44iWRchQKdJYtjRtPY6e-6e0eXpKXXsx5Ooi6sWE474KA-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2019-05-03  1:45               ` Frank Rowand
2019-05-03  1:45                 ` Frank Rowand
2019-05-03  1:45                 ` Frank Rowand
2019-05-03  1:45                 ` frowand.list
2019-05-03  1:45                 ` Frank Rowand
2019-05-03  5:36                 ` Brendan Higgins
2019-05-03  5:36                   ` Brendan Higgins
2019-05-03  5:36                   ` Brendan Higgins
2019-05-03  5:36                   ` brendanhiggins
2019-05-03  5:36                   ` Brendan Higgins
2019-05-03 18:59                   ` Frank Rowand
2019-05-03 18:59                     ` Frank Rowand
2019-05-03 18:59                     ` Frank Rowand
2019-05-03 18:59                     ` frowand.list
2019-05-03 18:59                     ` Frank Rowand
     [not found]                     ` <052fa196-4ea9-8384-79b7-fe6bacc0ee82-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2019-05-03 23:14                       ` Brendan Higgins
2019-05-03 23:14                         ` Brendan Higgins
2019-05-03 23:14                         ` Brendan Higgins
2019-05-03 23:14                         ` brendanhiggins
2019-05-03 23:14                         ` Brendan Higgins
2019-05-04 10:42                         ` Greg KH
2019-05-04 10:42                           ` Greg KH
2019-05-04 10:42                           ` Greg KH
2019-05-04 10:42                           ` gregkh
2019-05-04 10:42                           ` Greg KH
     [not found]                         ` <CAFd5g47aY-CL+d7DfiyTidY4aAVY+eg1TM1UJ4nYqKSfHOi-0w-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2019-05-06  0:19                           ` Frank Rowand
2019-05-06  0:19                             ` Frank Rowand
2019-05-06  0:19                             ` Frank Rowand
2019-05-06  0:19                             ` frowand.list
2019-05-06  0:19                             ` Frank Rowand
2019-05-06 17:43                             ` Kees Cook
2019-05-06 17:43                               ` Kees Cook
2019-05-06 17:43                               ` Kees Cook
2019-05-06 17:43                               ` Kees Cook
2019-05-06 17:43                               ` keescook
2019-05-06 17:43                               ` Kees Cook
2019-05-06 21:42                               ` Brendan Higgins
2019-05-06 21:42                                 ` Brendan Higgins
2019-05-06 21:42                                 ` Brendan Higgins
2019-05-06 21:42                                 ` Brendan Higgins
2019-05-06 21:42                                 ` brendanhiggins
2019-05-06 21:42                                 ` Brendan Higgins
2019-05-06 21:39                             ` Brendan Higgins
2019-05-06 21:39                               ` Brendan Higgins
2019-05-06 21:39                               ` Brendan Higgins
2019-05-06 21:39                               ` brendanhiggins
2019-05-06 21:39                               ` Brendan Higgins
     [not found]                               ` <CAFd5g46=ZU58uJ=Qhs3soBzJjzJKJFY0_uzZ7fe1CxPfJioNOA-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2019-05-07 19:13                                 ` Tim.Bird-7U/KSKJipcs
2019-05-07 19:13                                   ` Tim.Bird
2019-05-07 19:13                                   ` Tim.Bird
2019-05-07 19:13                                   ` Tim.Bird
2019-05-07 19:13                                   ` Tim.Bird
2019-05-07 19:13                                   ` Tim.Bird
2019-05-03  6:41             ` Greg KH
2019-05-03  6:41               ` Greg KH
2019-05-03  6:41               ` Greg KH
2019-05-03  6:41               ` gregkh
2019-05-03  6:41               ` Greg KH
2019-05-01 23:01 ` [PATCH v2 13/17] kunit: defconfig: add defconfigs for building " Brendan Higgins
2019-05-01 23:01   ` Brendan Higgins
2019-05-01 23:01   ` Brendan Higgins
2019-05-01 23:01   ` brendanhiggins
2019-05-01 23:01   ` Brendan Higgins
     [not found] ` <20190501230126.229218-1-brendanhiggins-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
2019-05-01 23:01   ` [PATCH v2 14/17] Documentation: kunit: add documentation for KUnit Brendan Higgins
2019-05-01 23:01     ` Brendan Higgins
2019-05-01 23:01     ` Brendan Higgins
2019-05-01 23:01     ` brendanhiggins
2019-05-01 23:01     ` Brendan Higgins
     [not found]     ` <20190501230126.229218-15-brendanhiggins-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
2019-05-09  5:08       ` Randy Dunlap
2019-05-09  5:08         ` Randy Dunlap
2019-05-09  5:08         ` Randy Dunlap
2019-05-09  5:08         ` rdunlap
2019-05-09  5:08         ` Randy Dunlap
2019-05-09 17:38         ` Brendan Higgins
2019-05-09 17:38           ` Brendan Higgins
2019-05-09 17:38           ` Brendan Higgins
2019-05-09 17:38           ` Brendan Higgins
2019-05-09 17:38           ` brendanhiggins
2019-05-09 17:38           ` Brendan Higgins
2019-05-01 23:01   ` [PATCH v2 15/17] MAINTAINERS: add entry for KUnit the unit testing framework Brendan Higgins
2019-05-01 23:01     ` Brendan Higgins
2019-05-01 23:01     ` Brendan Higgins
2019-05-01 23:01     ` brendanhiggins
2019-05-01 23:01     ` Brendan Higgins
2019-05-03 14:38     ` shuah
2019-05-03 14:38       ` shuah
2019-05-03 14:38       ` shuah
2019-05-03 14:38       ` shuah
2019-05-03 14:38       ` shuah
2019-05-06  9:18       ` Brendan Higgins
2019-05-06  9:18         ` Brendan Higgins
2019-05-06  9:18         ` Brendan Higgins
2019-05-06  9:18         ` brendanhiggins
2019-05-06  9:18         ` Brendan Higgins
2019-05-01 23:01   ` [PATCH v2 16/17] kernel/sysctl-test: Add null pointer test for sysctl.c:proc_dointvec() Brendan Higgins
2019-05-01 23:01     ` Brendan Higgins
2019-05-01 23:01     ` Brendan Higgins
2019-05-01 23:01     ` brendanhiggins
2019-05-01 23:01     ` Brendan Higgins
2019-05-02 11:03     ` Greg KH
2019-05-02 11:03       ` Greg KH
2019-05-02 11:03       ` Greg KH
2019-05-02 11:03       ` gregkh
2019-05-02 11:03       ` Greg KH
2019-05-02 18:14       ` Tim.Bird
2019-05-02 18:14         ` Tim.Bird
2019-05-02 18:14         ` Tim.Bird
2019-05-02 18:14         ` Tim.Bird
2019-05-02 18:14         ` Tim.Bird
2019-05-02 18:14         ` Tim.Bird
2019-05-02 18:45         ` Brendan Higgins
2019-05-02 18:45           ` Brendan Higgins
2019-05-02 18:45           ` Brendan Higgins
2019-05-02 18:45           ` brendanhiggins
2019-05-02 18:45           ` Brendan Higgins
2019-05-03  6:42           ` Greg KH
2019-05-03  6:42             ` Greg KH
2019-05-03  6:42             ` Greg KH
2019-05-03  6:42             ` gregkh
2019-05-03  6:42             ` Greg KH
2019-05-03 23:41             ` Brendan Higgins
2019-05-03 23:41               ` Brendan Higgins
2019-05-03 23:41               ` Brendan Higgins
2019-05-03 23:41               ` brendanhiggins
2019-05-03 23:41               ` Brendan Higgins
2019-05-04 10:40               ` Greg KH
2019-05-04 10:40                 ` Greg KH
2019-05-04 10:40                 ` Greg KH
2019-05-04 10:40                 ` gregkh
2019-05-04 10:40                 ` Greg KH
2019-05-01 23:01 ` [PATCH v2 17/17] MAINTAINERS: add proc sysctl KUnit test to PROC SYSCTL section Brendan Higgins
2019-05-01 23:01   ` Brendan Higgins
2019-05-01 23:01   ` Brendan Higgins
2019-05-01 23:01   ` brendanhiggins
2019-05-02 10:50 ` [PATCH v2 00/17] kunit: introduce KUnit, the Linux kernel unit testing framework Greg KH
2019-05-02 10:50   ` Greg KH
2019-05-02 10:50   ` Greg KH
2019-05-02 10:50   ` gregkh
2019-05-02 10:50   ` Greg KH
2019-05-02 11:05   ` Greg KH
2019-05-02 11:05     ` Greg KH
2019-05-02 11:05     ` Greg KH
2019-05-02 11:05     ` gregkh
2019-05-02 11:05     ` Greg KH
2019-05-03  0:41     ` Brendan Higgins
2019-05-03  0:41       ` Brendan Higgins
2019-05-03  0:41       ` Brendan Higgins
2019-05-03  0:41       ` brendanhiggins
2019-05-03  0:41       ` Brendan Higgins
2019-05-02 14:04   ` shuah
2019-05-02 14:04     ` shuah
2019-05-02 14:04     ` shuah
2019-05-02 14:04     ` shuah
2019-05-02 14:04     ` shuah
2019-05-02 14:04     ` shuah
2019-05-03  0:44     ` Brendan Higgins
2019-05-03  0:44       ` Brendan Higgins
2019-05-03  0:44       ` Brendan Higgins
2019-05-03  0:44       ` brendanhiggins
2019-05-03  0:44       ` Brendan Higgins
2019-05-03  3:18 ` Logan Gunthorpe
2019-05-03  3:18   ` Logan Gunthorpe
2019-05-03  3:18   ` Logan Gunthorpe
2019-05-03  3:18   ` logang
2019-05-03  3:18   ` Logan Gunthorpe
2019-05-07  3:14 ` Frank Rowand
2019-05-07  3:14   ` Frank Rowand
2019-05-07  3:14   ` Frank Rowand
2019-05-07  3:14   ` frowand.list
     [not found]   ` <54940124-50df-16ec-1a32-ad794ee05da7-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2019-05-07  8:01     ` Greg KH
2019-05-07  8:01       ` Greg KH
2019-05-07  8:01       ` gregkh
2019-05-07  8:01       ` Greg KH
2019-05-07 15:23       ` shuah
2019-05-07 15:23         ` shuah
2019-05-07 15:23         ` shuah
2019-05-07 15:23         ` shuah
2019-05-07 15:23         ` shuah
2019-05-07 15:23         ` shuah
2019-05-09  1:01         ` Frank Rowand
2019-05-09  1:01           ` Frank Rowand
2019-05-09  1:01           ` Frank Rowand
2019-05-09  1:01           ` frowand.list
2019-05-09  1:01           ` Frank Rowand
2019-05-09  1:01           ` Frank Rowand
2019-05-07 17:22       ` Theodore Ts'o
2019-05-07 17:22         ` Theodore Ts'o
2019-05-07 17:22         ` Theodore Ts'o
2019-05-07 17:22         ` tytso
2019-05-07 17:22         ` Theodore Ts'o
2019-05-07 17:22         ` Theodore Ts'o
2019-05-08 19:17         ` Brendan Higgins
2019-05-08 19:17           ` Brendan Higgins
2019-05-08 19:17           ` Brendan Higgins
2019-05-08 19:17           ` Brendan Higgins
2019-05-08 19:17           ` brendanhiggins
2019-05-08 19:17           ` Brendan Higgins
2019-05-09  0:58         ` Frank Rowand
2019-05-09  0:58           ` Frank Rowand
2019-05-09  0:58           ` Frank Rowand
2019-05-09  0:58           ` frowand.list
2019-05-09  0:58           ` Frank Rowand
2019-05-09  0:58           ` Frank Rowand
2019-05-09  1:44           ` Theodore Ts'o
2019-05-09  1:44             ` Theodore Ts'o
2019-05-09  1:44             ` Theodore Ts'o
2019-05-09  1:44             ` tytso
2019-05-09  1:44             ` Theodore Ts'o
2019-05-09  1:44             ` Theodore Ts'o
2019-05-09  2:18             ` Frank Rowand
2019-05-09  2:18               ` Frank Rowand
2019-05-09  2:18               ` Frank Rowand
2019-05-09  2:18               ` frowand.list
2019-05-09  2:18               ` Frank Rowand
2019-05-09  2:18               ` Frank Rowand
2019-05-14  8:22           ` Brendan Higgins
2019-05-14  8:22             ` Brendan Higgins
2019-05-14  8:22             ` brendanhiggins
2019-05-14  8:22             ` Brendan Higgins
2019-05-09  0:43       ` Frank Rowand
2019-05-09  0:43         ` Frank Rowand
2019-05-09  0:43         ` Frank Rowand
2019-05-09  0:43         ` frowand.list
2019-05-09  0:43         ` Frank Rowand
2019-05-09  0:43         ` Frank Rowand
2019-05-09  1:58         ` Theodore Ts'o
2019-05-09  1:58           ` Theodore Ts'o
2019-05-09  1:58           ` Theodore Ts'o
2019-05-09  1:58           ` tytso
2019-05-09  1:58           ` Theodore Ts'o
2019-05-09  1:58           ` Theodore Ts'o
2019-05-09  2:13           ` Frank Rowand
2019-05-09  2:13             ` Frank Rowand
2019-05-09  2:13             ` Frank Rowand
2019-05-09  2:13             ` frowand.list
2019-05-09  2:13             ` Frank Rowand
2019-05-09  2:13             ` Frank Rowand
2019-05-09  3:20             ` Theodore Ts'o
2019-05-09  3:20               ` Theodore Ts'o
2019-05-09  3:20               ` Theodore Ts'o
2019-05-09  3:20               ` tytso
2019-05-09  3:20               ` Theodore Ts'o
2019-05-09  3:20               ` Theodore Ts'o
     [not found]               ` <20190509032017.GA29703-3s7WtUTddSA@public.gmane.org>
2019-05-09 11:52                 ` Knut Omang
2019-05-09 11:52                   ` Knut Omang
2019-05-09 11:52                   ` Knut Omang
2019-05-09 11:52                   ` knut.omang
2019-05-09 11:52                   ` Knut Omang
2019-05-09 13:35                   ` Theodore Ts'o
2019-05-09 13:35                     ` Theodore Ts'o
2019-05-09 13:35                     ` Theodore Ts'o
2019-05-09 13:35                     ` tytso
2019-05-09 13:35                     ` Theodore Ts'o
2019-05-09 13:35                     ` Theodore Ts'o
     [not found]                     ` <20190509133551.GD29703-3s7WtUTddSA@public.gmane.org>
2019-05-09 14:48                       ` Knut Omang
2019-05-09 14:48                         ` Knut Omang
2019-05-09 14:48                         ` Knut Omang
2019-05-09 14:48                         ` knut.omang
2019-05-09 14:48                         ` Knut Omang
2019-05-09 17:00                       ` Tim.Bird-7U/KSKJipcs
2019-05-09 17:00                         ` Tim.Bird
2019-05-09 17:00                         ` Tim.Bird
2019-05-09 17:00                         ` Tim.Bird
2019-05-09 17:00                         ` Tim.Bird
2019-05-09 17:00                         ` Tim.Bird
2019-05-09 17:42                         ` Daniel Vetter
2019-05-09 17:42                           ` Daniel Vetter
2019-05-09 17:42                           ` Daniel Vetter
2019-05-09 17:42                           ` Daniel Vetter
2019-05-09 17:42                           ` daniel
2019-05-09 17:42                           ` Daniel Vetter
2019-05-09 18:12                         ` Frank Rowand
2019-05-09 18:12                           ` Frank Rowand
2019-05-09 18:12                           ` Frank Rowand
2019-05-09 18:12                           ` frowand.list
2019-05-09 18:12                           ` Frank Rowand
2019-05-09 18:12                           ` Frank Rowand
2019-05-09 21:42                           ` Theodore Ts'o
2019-05-09 21:42                             ` Theodore Ts'o
2019-05-09 21:42                             ` Theodore Ts'o
2019-05-09 21:42                             ` tytso
2019-05-09 21:42                             ` Theodore Ts'o
2019-05-09 21:42                             ` Theodore Ts'o
2019-05-09 22:20                             ` Logan Gunthorpe
2019-05-09 22:20                               ` Logan Gunthorpe
2019-05-09 22:20                               ` Logan Gunthorpe
2019-05-09 22:20                               ` logang
2019-05-09 22:20                               ` Logan Gunthorpe
2019-05-09 22:20                               ` Logan Gunthorpe
2019-05-09 23:30                               ` Theodore Ts'o
2019-05-09 23:30                                 ` Theodore Ts'o
2019-05-09 23:30                                 ` Theodore Ts'o
2019-05-09 23:30                                 ` tytso
2019-05-09 23:30                                 ` Theodore Ts'o
2019-05-09 23:30                                 ` Theodore Ts'o
2019-05-09 23:40                                 ` Logan Gunthorpe
2019-05-09 23:40                                   ` Logan Gunthorpe
2019-05-09 23:40                                   ` Logan Gunthorpe
2019-05-09 23:40                                   ` logang
2019-05-09 23:40                                   ` Logan Gunthorpe
2019-05-09 23:40                                   ` Logan Gunthorpe
2019-05-10  4:47                                   ` Theodore Ts'o
2019-05-10  4:47                                     ` Theodore Ts'o
2019-05-10  4:47                                     ` Theodore Ts'o
2019-05-10  4:47                                     ` tytso
2019-05-10  4:47                                     ` Theodore Ts'o
2019-05-10  4:47                                     ` Theodore Ts'o
2019-05-10  5:18                                   ` Frank Rowand
2019-05-10  5:18                                     ` Frank Rowand
2019-05-10  5:18                                     ` Frank Rowand
2019-05-10  5:18                                     ` frowand.list
2019-05-10  5:18                                     ` Frank Rowand
2019-05-10  5:18                                     ` Frank Rowand
     [not found]                                     ` <6d6e91ec-33d3-830b-4895-4d7a20ba7d45-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2019-05-10  5:48                                       ` Knut Omang
2019-05-10  5:48                                         ` Knut Omang
2019-05-10  5:48                                         ` Knut Omang
2019-05-10  5:48                                         ` knut.omang
2019-05-10  5:48                                         ` Knut Omang
2019-05-10  8:12                                         ` Daniel Vetter
2019-05-10  8:12                                           ` Daniel Vetter
2019-05-10  8:12                                           ` Daniel Vetter
2019-05-10  8:12                                           ` Daniel Vetter
2019-05-10  8:12                                           ` daniel
2019-05-10  8:12                                           ` Daniel Vetter
2019-05-10 10:23                                           ` Brendan Higgins
2019-05-10 10:23                                             ` Brendan Higgins
2019-05-10 10:23                                             ` Brendan Higgins
2019-05-10 10:23                                             ` brendanhiggins
2019-05-10 10:23                                             ` Brendan Higgins
     [not found]                                             ` <CAFd5g47Fvafwgh15JNfxSBRf5qqG2z+V+XGAB2cJtNnHFTiFfQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2019-05-10 12:12                                               ` Knut Omang
2019-05-10 12:12                                                 ` Knut Omang
2019-05-10 12:12                                                 ` Knut Omang
2019-05-10 12:12                                                 ` Knut Omang
2019-05-10 12:12                                                 ` knut.omang
2019-05-10 12:12                                                 ` Knut Omang
2019-05-10 20:54                                                 ` Brendan Higgins
2019-05-10 20:54                                                   ` Brendan Higgins
2019-05-10 20:54                                                   ` Brendan Higgins
2019-05-10 20:54                                                   ` Brendan Higgins
2019-05-10 20:54                                                   ` brendanhiggins
2019-05-10 20:54                                                   ` Brendan Higgins
2019-05-10 22:18                                                   ` Frank Rowand
2019-05-10 22:18                                                     ` Frank Rowand
2019-05-10 22:18                                                     ` Frank Rowand
2019-05-10 22:18                                                     ` Frank Rowand
2019-05-10 22:18                                                     ` frowand.list
2019-05-10 22:18                                                     ` Frank Rowand
     [not found]                                                     ` <ccec4818-1b9d-2554-e0e4-433eba659c8e-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2019-05-11  6:17                                                       ` Knut Omang
2019-05-11  6:17                                                         ` Knut Omang
2019-05-11  6:17                                                         ` Knut Omang
2019-05-11  6:17                                                         ` Knut Omang
2019-05-11  6:17                                                         ` knut.omang
2019-05-11  6:17                                                         ` Knut Omang
2019-05-14  6:39                                                         ` Brendan Higgins
2019-05-14  6:39                                                           ` Brendan Higgins
2019-05-14  6:39                                                           ` Brendan Higgins
2019-05-14  6:39                                                           ` Brendan Higgins
2019-05-14  6:39                                                           ` brendanhiggins
2019-05-14  6:39                                                           ` Brendan Higgins
2019-05-10 21:59                                             ` Frank Rowand
2019-05-10 21:59                                               ` Frank Rowand
2019-05-10 21:59                                               ` Frank Rowand
2019-05-10 21:59                                               ` Frank Rowand
2019-05-10 21:59                                               ` frowand.list
2019-05-10 21:59                                               ` Frank Rowand
     [not found]                                               ` <8abaf5f2-dd33-98d0-7b34-b57de7fe7c8b-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2019-05-11  6:43                                                 ` Knut Omang
2019-05-11  6:43                                                   ` Knut Omang
2019-05-11  6:43                                                   ` Knut Omang
2019-05-11  6:43                                                   ` Knut Omang
2019-05-11  6:43                                                   ` knut.omang
2019-05-11  6:43                                                   ` Knut Omang
     [not found]                                                   ` <a3362d96a6d95d852753739384ded814f5269aac.camel-QHcLZuEGTsvQT0dZR+AlfA@public.gmane.org>
2019-05-14  8:00                                                     ` Brendan Higgins
2019-05-14  8:00                                                       ` Brendan Higgins
2019-05-14  8:00                                                       ` Brendan Higgins
2019-05-14  8:00                                                       ` Brendan Higgins
2019-05-14  8:00                                                       ` brendanhiggins
2019-05-14  8:00                                                       ` Brendan Higgins
     [not found]                                           ` <CAKMK7uFd1xUx8u3xWLwifVSq4OEnMO4S-m0hESe68UzONXnMFg-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2019-05-10 11:36                                             ` Knut Omang
2019-05-10 11:36                                               ` Knut Omang
2019-05-10 11:36                                               ` Knut Omang
2019-05-10 11:36                                               ` Knut Omang
2019-05-10 11:36                                               ` knut.omang
2019-05-10 11:36                                               ` Knut Omang
2019-05-10 16:17                                     ` Logan Gunthorpe
2019-05-10 16:17                                       ` Logan Gunthorpe
2019-05-10 16:17                                       ` Logan Gunthorpe
2019-05-10 16:17                                       ` logang
2019-05-10 16:17                                       ` Logan Gunthorpe
2019-05-10 22:13                                       ` Frank Rowand
2019-05-10 22:13                                         ` Frank Rowand
2019-05-10 22:13                                         ` Frank Rowand
2019-05-10 22:13                                         ` frowand.list
2019-05-10 22:13                                         ` Frank Rowand
2019-05-10 22:13                                         ` Frank Rowand
2019-05-14  8:38                                         ` Brendan Higgins
2019-05-14  8:38                                           ` Brendan Higgins
2019-05-14  8:38                                           ` Brendan Higgins
2019-05-14  8:38                                           ` brendanhiggins
2019-05-14  8:38                                           ` Brendan Higgins
2019-05-15  0:14                                           ` Frank Rowand
2019-05-15  0:14                                             ` Frank Rowand
2019-05-15  0:14                                             ` Frank Rowand
2019-05-15  0:14                                             ` frowand.list
2019-05-15  0:14                                             ` Frank Rowand
2019-05-15  0:26                                             ` Logan Gunthorpe
2019-05-15  0:26                                               ` Logan Gunthorpe
2019-05-15  0:26                                               ` Logan Gunthorpe
2019-05-15  0:26                                               ` logang
2019-05-15  0:26                                               ` Logan Gunthorpe
2019-05-15  0:26                                               ` Logan Gunthorpe
2019-05-10 21:52                               ` Frank Rowand
2019-05-10 21:52                                 ` Frank Rowand
2019-05-10 21:52                                 ` Frank Rowand
2019-05-10 21:52                                 ` frowand.list
2019-05-10 21:52                                 ` Frank Rowand
2019-05-10 21:52                                 ` Frank Rowand
2019-05-14 20:54                                 ` Brendan Higgins
2019-05-14 20:54                                   ` Brendan Higgins
2019-05-14 20:54                                   ` Brendan Higgins
2019-05-14 20:54                                   ` brendanhiggins
2019-05-14 20:54                                   ` Brendan Higgins
2019-05-10 21:12                             ` Frank Rowand
2019-05-10 21:12                               ` Frank Rowand
2019-05-10 21:12                               ` Frank Rowand
2019-05-10 21:12                               ` frowand.list
2019-05-10 21:12                               ` Frank Rowand
2019-05-10 21:12                               ` Frank Rowand
2019-05-11 17:33                               ` Theodore Ts'o
2019-05-11 17:33                                 ` Theodore Ts'o
2019-05-11 17:33                                 ` Theodore Ts'o
2019-05-11 17:33                                 ` tytso
2019-05-11 17:33                                 ` Theodore Ts'o
2019-05-11 17:33                                 ` Theodore Ts'o
2019-05-13 14:44                                 ` Daniel Vetter
2019-05-13 14:44                                   ` Daniel Vetter
2019-05-13 14:44                                   ` Daniel Vetter
2019-05-13 14:44                                   ` daniel
2019-05-13 14:44                                   ` Daniel Vetter
2019-05-14  6:04                                   ` Brendan Higgins
2019-05-14  6:04                                     ` Brendan Higgins
2019-05-14  6:04                                     ` Brendan Higgins
2019-05-14  6:04                                     ` brendanhiggins
2019-05-14  6:04                                     ` Brendan Higgins
     [not found]                                     ` <20190514060433.GA181462-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
2019-05-14 12:05                                       ` Daniel Vetter
2019-05-14 12:05                                         ` Daniel Vetter
2019-05-14 12:05                                         ` Daniel Vetter
2019-05-14 12:05                                         ` Daniel Vetter
2019-05-14 12:05                                         ` daniel
2019-05-14 12:05                                         ` Daniel Vetter
2019-05-14 18:36                                         ` Brendan Higgins
2019-05-14 18:36                                           ` Brendan Higgins
2019-05-14 18:36                                           ` Brendan Higgins
2019-05-14 18:36                                           ` Brendan Higgins
2019-05-14 18:36                                           ` brendanhiggins
2019-05-14 18:36                                           ` Brendan Higgins
2019-05-15  7:41                                           ` Daniel Vetter
2019-05-15  7:41                                             ` Daniel Vetter
2019-05-15  7:41                                             ` Daniel Vetter
2019-05-15  7:41                                             ` Daniel Vetter
2019-05-15  7:41                                             ` daniel
2019-05-15  7:41                                             ` Daniel Vetter
2019-05-15  7:41                                             ` Daniel Vetter
2019-05-22 21:38                                             ` Brendan Higgins
2019-05-22 21:38                                               ` Brendan Higgins
2019-05-22 21:38                                               ` Brendan Higgins
2019-05-22 21:38                                               ` Brendan Higgins
2019-05-22 21:38                                               ` brendanhiggins
2019-05-22 21:38                                               ` Brendan Higgins
2019-05-23  8:40                                               ` Daniel Vetter
2019-05-23  8:40                                                 ` Daniel Vetter
2019-05-23  8:40                                                 ` Daniel Vetter
2019-05-23  8:40                                                 ` Daniel Vetter
2019-05-23  8:40                                                 ` daniel
2019-05-23  8:40                                                 ` Daniel Vetter
2019-05-23  8:40                                                 ` Daniel Vetter
     [not found]                                             ` <20190515074141.GY17751-dv86pmgwkMBes7Z6vYuT8azUEOm+Xw19@public.gmane.org>
2019-05-22 21:38                                               ` Brendan Higgins
2019-05-15  0:26                                 ` Frank Rowand
2019-05-15  0:26                                   ` Frank Rowand
2019-05-15  0:26                                   ` Frank Rowand
2019-05-15  0:26                                   ` frowand.list
2019-05-15  0:26                                   ` Frank Rowand
2019-05-15  4:28                                   ` Theodore Ts'o
2019-05-15  4:28                                     ` Theodore Ts'o
2019-05-15  4:28                                     ` Theodore Ts'o
2019-05-15  4:28                                     ` tytso
2019-05-15  4:28                                     ` Theodore Ts'o
2019-05-15  4:28                                     ` Theodore Ts'o
2019-05-10  5:11             ` Frank Rowand
2019-05-10  5:11               ` Frank Rowand
2019-05-10  5:11               ` Frank Rowand
2019-05-10  5:11               ` frowand.list
2019-05-10  5:11               ` Frank Rowand
2019-05-10  5:11               ` Frank Rowand
2019-05-10 10:43               ` Theodore Ts'o
2019-05-10 10:43                 ` Theodore Ts'o
2019-05-10 10:43                 ` Theodore Ts'o
2019-05-10 10:43                 ` tytso
2019-05-10 10:43                 ` Theodore Ts'o
2019-05-10 10:43                 ` Theodore Ts'o
2019-05-10 21:05                 ` Frank Rowand
2019-05-10 21:05                   ` Frank Rowand
2019-05-10 21:05                   ` Frank Rowand
2019-05-10 21:05                   ` frowand.list
2019-05-10 21:05                   ` Frank Rowand
2019-05-10 21:05                   ` Frank Rowand
2019-05-09 15:19 ` Masahiro Yamada
2019-05-09 15:19   ` Masahiro Yamada
2019-05-09 15:19   ` Masahiro Yamada
2019-05-09 15:19   ` Masahiro Yamada
2019-05-09 15:19   ` yamada.masahiro
2019-05-09 15:19   ` Masahiro Yamada
2019-05-10 10:25   ` Brendan Higgins
2019-05-10 10:25     ` Brendan Higgins
2019-05-10 10:25     ` Brendan Higgins
2019-05-10 10:25     ` Brendan Higgins
2019-05-10 10:25     ` brendanhiggins
2019-05-10 10:25     ` Brendan Higgins

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=20190501230126.229218-13-brendanhiggins@google.com \
    --to=brendanhiggins@google.com \
    --cc=Alexander.Levin@microsoft.com \
    --cc=Tim.Bird@sony.com \
    --cc=amir73il@gmail.com \
    --cc=dan.carpenter@oracle.com \
    --cc=dan.j.williams@intel.com \
    --cc=daniel@ffwll.ch \
    --cc=devicetree@vger.kernel.org \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=felixguoxiuping@gmail.com \
    --cc=frowand.list@gmail.com \
    --cc=gregkh@linuxfoundation.org \
    --cc=jdike@addtoit.com \
    --cc=joel@jms.id.au \
    --cc=julia.lawall@lip6.fr \
    --cc=keescook@google.com \
    --cc=khilman@baylibre.com \
    --cc=kieran.bingham@ideasonboard.com \
    --cc=knut.omang@oracle.com \
    --cc=kunit-dev@googlegroups.com \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kbuild@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-kselftest@vger.kernel.org \
    --cc=linux-nvdimm@lists.01.org \
    --cc=linux-um@lists.infradead.org \
    --cc=logang@deltatee.com \
    --cc=mcgrof@kernel.org \
    --cc=mpe@ellerman.id.au \
    --cc=pmladek@suse.com \
    --cc=richard@nod.at \
    --cc=rientjes@google.com \
    --cc=robh@kernel.org \
    --cc=rostedt@goodmis.org \
    --cc=sboyd@kernel.org \
    --cc=shuah@kernel.org \
    --cc=wfg@linux.intel.com \
    /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.