Skip to contentSkip to conversation

Revive ptest and add result to buildhistory

PATCH In review Robert Yang 3 participants 13 messages Aug 22, 2017 – Aug 24, 2017

lore
Share & tools
lore
#0 lore raw

v2 cover Revive ptest and add result to buildhistory

* V2:
- Fix Paul's comments
- Fix RP's comments

* V1:
- Initial version

// Robert

The following changes since commit 5c9ef0734d23909b5694ed43cdbb205c2ba9ca95:

devtool/copy_buildsystem: adds meta-skeleton layer in the eSDK installation. (2017-08-19 22:15:25 +0100)

are available in the git repository at:

git://git.openembedded.org/openembedded-core-contrib rbt/ptest
http://cgit.openembedded.org/openembedded-core-contrib/log/?h=rbt/ptest

Robert Yang (9):
runtime/cases/_ptest.py: revive it oeqa/utils/logparser.py: add skip status runtime/cases/_ptest.py: add skip status runtime/cases/_ptest.py: rename it to ptest.py utils/logparser.py: fix section check core/target/ssh.py: replace decode errors buildhistory.bbclass: print message when no commit testimage.bbclass: update comments buildhistory.bbclass: add ptest

 meta/classes/buildhistory.bbclass     |  32 +++++++++++
 meta/classes/testimage.bbclass        |   4 +-
 meta/lib/oeqa/core/target/ssh.py      |   4 +-
 meta/lib/oeqa/runtime/cases/_ptest.py | 103 ----------------------------------
 meta/lib/oeqa/runtime/cases/ptest.py  |  82 +++++++++++++++++++++++++++
 meta/lib/oeqa/utils/logparser.py      |   7 ++-
 6 files changed, 122 insertions(+), 110 deletions(-)
 delete mode 100644 meta/lib/oeqa/runtime/cases/_ptest.py
 create mode 100644 meta/lib/oeqa/runtime/cases/ptest.py
Signature · 1 line
2.10.2
#1 lore raw

PATCH v2 1/9 runtime/cases/_ptest.py: revive it

* Make it work with current oeqa
* Skip the test if ptest is not in DISTRO_FEATURES
* Skip the test if ptest-pkgs is not in IMAGE_FEATURES
* The logs are saved to:
testimage/ptest_log -> testimage/ptest_log.<datetime>
* This provides data that could be used to detect regressions in ptest results

[YOCTO #11547]

---
 meta/lib/oeqa/runtime/cases/_ptest.py | 96 +++++++++++++----------------------
 1 file changed, 35 insertions(+), 61 deletions(-)
meta/lib/oeqa/runtime/cases/_ptest.py +35 -61
index aaed9a5..6d23949 100644
@@ -1,28 +1,10 @@
-import os
-import shutil
-import subprocess
-
 from oeqa.runtime.case import OERuntimeTestCase
 from oeqa.core.decorator.depends import OETestDepends
 from oeqa.core.decorator.oeid import OETestID
-from oeqa.core.decorator.data import skipIfNotDataVar, skipIfNotFeature
-from oeqa.runtime.decorator.package import OEHasPackage
-
-from oeqa.runtime.cases.dnf import DnfTest
-from oeqa.utils.logparser import *
-from oeqa.utils.httpserver import HTTPService
-
-class PtestRunnerTest(DnfTest):
+from oeqa.core.decorator.data import skipIfNotFeature
+from oeqa.utils.logparser import Lparser, Result
 
-    @classmethod
-    def setUpClass(cls):
-        rpm_deploy = os.path.join(cls.tc.td['DEPLOY_DIR'], 'rpm')
-        cls.repo_server = HTTPService(rpm_deploy, cls.tc.target.server_ip)
-        cls.repo_server.start()
-
-    @classmethod
-    def tearDownClass(cls):
-        cls.repo_server.stop()
+class PtestRunnerTest(OERuntimeTestCase):
 
     # a ptest log parser
     def parse_ptest(self, logfile):
@@ -59,45 +41,37 @@ class PtestRunnerTest(DnfTest):
         result.sort_tests()
         return result
 
-    def _install_ptest_packages(self):
-        # Get ptest packages that can be installed in the image.
-        packages_dir = os.path.join(self.tc.td['DEPLOY_DIR'], 'rpm')
-        ptest_pkgs = [pkg[:pkg.find('-ptest')+6]
-                          for _, _, filenames in os.walk(packages_dir)
-                          for pkg in filenames
-                          if 'ptest' in pkg
-                          and pkg[:pkg.find('-ptest')] in self.tc.image_packages]
-
-        repo_url = 'http://%s:%s' % (self.target.server_ip,
-                                     self.repo_server.port)
-        dnf_options = ('--repofrompath=oe-ptest-repo,%s '
-                       '--nogpgcheck '
-                       'install -y' % repo_url)
-        self.dnf('%s %s ptest-runner' % (dnf_options, ' '.join(ptest_pkgs)))
-
-    @skipIfNotFeature('package-management',
-                      'Test requires package-management to be in DISTRO_FEATURES')
-    @skipIfNotFeature('ptest',
-                      'Test requires package-management to be in DISTRO_FEATURES')
-    @skipIfNotDataVar('IMAGE_PKGTYPE', 'rpm',
-                      'RPM is not the primary package manager')
-    @OEHasPackage(['dnf'])
+    @OETestID(1600)
+    @skipIfNotFeature('ptest', 'Test requires ptest to be in DISTRO_FEATURES')
+    @skipIfNotFeature('ptest-pkgs', 'Test requires ptest-pkgs to be in IMAGE_FEATURES')
     @OETestDepends(['ssh.SSHTest.test_ssh'])
     def test_ptestrunner(self):
-        self.ptest_log = os.path.join(self.tc.td['TEST_LOG_DIR'],
-                                      'ptest-%s.log' % self.tc.td['DATETIME'])
-        self._install_ptest_packages()
-
-        (runnerstatus, result) = self.target.run('/usr/bin/ptest-runner > /tmp/ptest.log 2>&1', 0)
-        #exit code is !=0 even if ptest-runner executes because some ptest tests fail.
-        self.assertTrue(runnerstatus != 127, msg="Cannot execute ptest-runner!")
-        self.target.copyFrom('/tmp/ptest.log', self.ptest_log)
-        shutil.copyfile(self.ptest_log, "ptest.log")
-
-        result = self.parse_ptest("ptest.log")
-        log_results_to_location = "./results"
-        if os.path.exists(log_results_to_location):
-            shutil.rmtree(log_results_to_location)
-        os.makedirs(log_results_to_location)
-
-        result.log_as_files(log_results_to_location, test_status = ['pass','fail'])
+        import datetime
+
+        test_log_dir = self.td.get('TEST_LOG_DIR', '')
+        # The TEST_LOG_DIR maybe NULL when testimage is added after
+        # testdata.json is generated.
+        if not test_log_dir:
+            test_log_dir = os.path.join(self.td.get('WORKDIR', ''), 'testimage')
+        # Don't use self.td.get('DATETIME'), it's from testdata.json, not
+        # up-to-date, and may cause "File exists" when re-reun.
+        datetime = datetime.datetime.now().strftime('%Y%m%d%H%M%S')
+        ptest_log_dir_link = os.path.join(test_log_dir, 'ptest_log')
+        ptest_log_dir = '%s.%s' % (ptest_log_dir_link, datetime)
+        ptest_runner_log = os.path.join(ptest_log_dir, 'ptest-runner.log')
+
+        status, output = self.target.run('ptest-runner', 0)
+        os.makedirs(ptest_log_dir)
+        with open(ptest_runner_log, 'w') as f:
+            f.write(output)
+
+        # status != 0 is OK since some ptest tests may fail
+        self.assertTrue(status != 127, msg="Cannot execute ptest-runner!")
+
+        # Parse and save results
+        parse_result = self.parse_ptest(ptest_runner_log)
+        parse_result.log_as_files(ptest_log_dir, test_status = ['pass','fail'])
+        if os.path.exists(ptest_log_dir_link):
+            # Remove the old link to create a new one
+            os.remove(ptest_log_dir_link)
+        os.symlink(os.path.basename(ptest_log_dir), ptest_log_dir_link)
Signature · 1 line
2.10.2
#2 lore raw

PATCH v2 2/9 oeqa/utils/logparser.py: add skip status

In reply to Robert Yang

Some test cases maybe skipped, let's parse it.

[YOCTO #11547]

---
 meta/lib/oeqa/utils/logparser.py | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)
meta/lib/oeqa/utils/logparser.py +3 -2
index b377dcd..4355ce0 100644
@@ -9,7 +9,7 @@ from . import ftools
 # A parser that can be used to identify weather a line is a test result or a section statement.
 class Lparser(object):
 
-    def __init__(self, test_0_pass_regex, test_0_fail_regex, section_0_begin_regex=None, section_0_end_regex=None, **kwargs):
+    def __init__(self, test_0_pass_regex, test_0_fail_regex, test_0_skip_regex, section_0_begin_regex=None, section_0_end_regex=None, **kwargs):
         # Initialize the arguments dictionary
         if kwargs:
             self.args = kwargs
@@ -19,12 +19,13 @@ class Lparser(object):
         # Add the default args to the dictionary
         self.args['test_0_pass_regex'] = test_0_pass_regex
         self.args['test_0_fail_regex'] = test_0_fail_regex
+        self.args['test_0_skip_regex'] = test_0_skip_regex
         if section_0_begin_regex:
             self.args['section_0_begin_regex'] = section_0_begin_regex
         if section_0_end_regex:
             self.args['section_0_end_regex'] = section_0_end_regex
 
-        self.test_possible_status = ['pass', 'fail', 'error']
+        self.test_possible_status = ['pass', 'fail', 'error', 'skip']
         self.section_possible_status = ['begin', 'end']
 
         self.initialized = False
Signature · 1 line
2.10.2
#3 lore raw

PATCH v2 3/9 runtime/cases/_ptest.py: add skip status

In reply to Robert Yang

The packages' test cases maybe skipped, check and save them.

[YOCTO #11547]

---
 meta/lib/oeqa/runtime/cases/_ptest.py | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)
meta/lib/oeqa/runtime/cases/_ptest.py +6 -1
index 6d23949..ec8c038 100644
@@ -10,6 +10,7 @@ class PtestRunnerTest(OERuntimeTestCase):
     def parse_ptest(self, logfile):
         parser = Lparser(test_0_pass_regex="^PASS:(.+)",
                          test_0_fail_regex="^FAIL:(.+)",
+                         test_0_skip_regex="^SKIP:(.+)",
                          section_0_begin_regex="^BEGIN: .*/(.+)/ptest",
                          section_0_end_regex="^END: .*/(.+)/ptest")
         parser.init()
@@ -38,6 +39,10 @@ class PtestRunnerTest(OERuntimeTestCase):
                     result.store(current_section, name, status)
                     continue
 
+                if line_type == 'test' and status == 'skip':
+                    result.store(current_section, name, status)
+                    continue
+
         result.sort_tests()
         return result
 
@@ -70,7 +75,7 @@ class PtestRunnerTest(OERuntimeTestCase):
 
         # Parse and save results
         parse_result = self.parse_ptest(ptest_runner_log)
-        parse_result.log_as_files(ptest_log_dir, test_status = ['pass','fail'])
+        parse_result.log_as_files(ptest_log_dir, test_status = ['pass','fail', 'skip'])
         if os.path.exists(ptest_log_dir_link):
             # Remove the old link to create a new one
             os.remove(ptest_log_dir_link)
Signature · 1 line
2.10.2
#4 lore raw

PATCH v2 4/9 runtime/cases/_ptest.py: rename it to ptest.py

In reply to Robert Yang

It works now.

[YOCTO #11547]

---
 meta/lib/oeqa/runtime/cases/{_ptest.py => ptest.py} | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename meta/lib/oeqa/runtime/cases/{_ptest.py => ptest.py} (100%)
meta/lib/oeqa/runtime/cases/ptest.py renamed from meta/lib/oeqa/runtime/cases/_ptest.py +0 -0
similarity index 100%
rename from meta/lib/oeqa/runtime/cases/_ptest.py
rename to meta/lib/oeqa/runtime/cases/ptest.py
Signature · 1 line
2.10.2
#5 lore raw

PATCH v2 5/9 utils/logparser.py: fix section check

In reply to Robert Yang

The section might be None or '', so use "if section" for it.

Fixed:
File "/buildarea/lyang1/poky/meta/lib/oeqa/utils/logparser.py", line 113, in log_as_files

    prefix += section

TypeError: Can't convert 'NoneType' object to str implicitly

[YOCTO #11547]

---
 meta/lib/oeqa/utils/logparser.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
meta/lib/oeqa/utils/logparser.py +1 -1
index 4355ce0..0670627 100644
@@ -109,7 +109,7 @@ class Result(object):
             prefix = ''
             for x in test_status:
                 prefix +=x+'.'
-            if (section != ''):
+            if section:
                 prefix += section
             section_file = os.path.join(target_dir, prefix)
             # purge the file contents if it exists
Signature · 1 line
2.10.2
#6 lore raw

PATCH v2 6/9 core/target/ssh.py: replace decode errors

In reply to Robert Yang

There might be wild strings when read from target (especially when reading ptest results), replace the errors to avoid breaking the test.

Fixed: (Not always happen)

$ bitbake core-image-sato -ctestimage
[snip]
    status, output = self.target.run('ptest-runner', 0)
  File "/buildarea/lyang1/poky/meta/lib/oeqa/core/target/ssh.py", line 84, in run
    status, output = self._run(sshCmd, processTimeout, True)
  File "/buildarea/lyang1/poky/meta/lib/oeqa/core/target/ssh.py", line 55, in _run
    status, output = SSHCall(command, self.logger, timeout)
  File "/buildarea/lyang1/poky/meta/lib/oeqa/core/target/ssh.py", line 258, in SSHCall
    run()
  File "/buildarea/lyang1/poky/meta/lib/oeqa/core/target/ssh.py", line 236, in run
    output = process.communicate()[0].decode("utf-8")
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc3 in position 4906: invalid continuation byte

[YOCTO #11547]

---
 meta/lib/oeqa/core/target/ssh.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
meta/lib/oeqa/core/target/ssh.py +2 -2
index b80939c..a2eafcd 100644
@@ -211,7 +211,7 @@ def SSHCall(command, logger, timeout=None, **opts):
                             process.stdout.close()
                             eof = True
                         else:
-                            data = data.decode("utf-8")
+                            data = data.decode("utf-8", errors='replace')
                             output += data
                             logger.debug('Partial data from SSH call: %s' % data)
                             endtime = time.time() + timeout
@@ -233,7 +233,7 @@ def SSHCall(command, logger, timeout=None, **opts):
                 output += lastline
 
         else:
-            output = process.communicate()[0].decode("utf-8")
+            output = process.communicate()[0].decode("utf-8", errors='replace')
             logger.debug('Data from SSH call: %s' % output.rstrip())
 
     options = {
Signature · 1 line
2.10.2
#7 lore raw

Re: [PATCH V2 6/9] core/target/ssh.py: replace decode errors

Quoted 29 lines · NZST Robert Yang

There might be wild strings when read from target (especially when reading ptest results), replace the errors to avoid breaking the test.

Fixed: (Not always happen)

$ bitbake core-image-sato -ctestimage
[snip]
    status, output = self.target.run('ptest-runner', 0)
  File "/buildarea/lyang1/poky/meta/lib/oeqa/core/target/ssh.py", line 84, in run
    status, output = self._run(sshCmd, processTimeout, True)
  File "/buildarea/lyang1/poky/meta/lib/oeqa/core/target/ssh.py", line 55, in _run
    status, output = SSHCall(command, self.logger, timeout)
  File "/buildarea/lyang1/poky/meta/lib/oeqa/core/target/ssh.py", line 258, in SSHCall
    run()
  File "/buildarea/lyang1/poky/meta/lib/oeqa/core/target/ssh.py", line 236, in run
    output = process.communicate()[0].decode("utf-8")
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc3 in position 4906: invalid continuation byte

[YOCTO #11547]

---
 meta/lib/oeqa/core/target/ssh.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
meta/lib/oeqa/core/target/ssh.py +0 -0
index b80939c..a2eafcd 100644

meta/lib/oeqa/core/target/ssh.py def SSHCall(command, logger, timeout=None, **opts): · line 216

Quoted 7 lines
meta/lib/oeqa/core/target/ssh.py +1 -1
@@ -211,7 +211,7 @@ def SSHCall(command, logger, timeout=None, **opts):
                             process.stdout.close()
                             eof = True
                         else:
-                            data = data.decode("utf-8")
+                            data = data.decode("utf-8", errors='replace')
                             output += data
                             logger.debug('Partial data from SSH call: %s' % data)

Since we're dealing with partial data here, shouldn't we be using a reader object? e.g.:

http://git.yoctoproject.org/cgit/cgit.cgi/poky/commit/?id=bfd8c35c3f917e3806c8dfe36c98c70fbccbb3c9

Cheers,
Paul

Signature · 2 lines
Paul Eggleton
Intel Open Source Technology Centre
#8 lore raw

Re: [PATCH V2 6/9] core/target/ssh.py: replace decode errors

Quoted 51 lines · Paul Eggleton
Quoted 39 lines · NZST Robert Yang

There might be wild strings when read from target (especially when reading ptest results), replace the errors to avoid breaking the test.

Fixed: (Not always happen)

$ bitbake core-image-sato -ctestimage
[snip]
    status, output = self.target.run('ptest-runner', 0)
  File "/buildarea/lyang1/poky/meta/lib/oeqa/core/target/ssh.py",
line 84, in run
    status, output = self._run(sshCmd, processTimeout, True)
  File "/buildarea/lyang1/poky/meta/lib/oeqa/core/target/ssh.py",
line 55, in _run
    status, output = SSHCall(command, self.logger, timeout)
  File "/buildarea/lyang1/poky/meta/lib/oeqa/core/target/ssh.py",
line 258, in SSHCall
    run()
  File "/buildarea/lyang1/poky/meta/lib/oeqa/core/target/ssh.py",
line 236, in run
    output = process.communicate()[0].decode("utf-8")
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc3 in
position 4906: invalid continuation byte

[YOCTO #11547]

---
 meta/lib/oeqa/core/target/ssh.py | 4 ++--

 1 file changed, 2 insertions(+), 2 deletions(-)
meta/lib/oeqa/core/target/ssh.py +0 -0
index b80939c..a2eafcd 100644
@@ -211,7 +211,7 @@ def SSHCall(command, logger, timeout=None,
 **opts):
                              process.stdout.close()
Quoted 8 lines

                             eof = True
                         else:
-                            data = data.decode("utf-8") +                            data = data.decode("utf-8", errors='replace')
                             output += data logger.debug('Partial data from SSH
call: %s' % data)

Since we're dealing with partial data here, shouldn't we be using a reader object? e.g.:

Quoted 2 lines

  http://git.yoctoproject.org/cgit/cgit.cgi/poky/commit/?id=bfd8c35c3
f917e3806c8dfe36c98c70fbccbb3c9

I've decided to merge this series as it improves the ptest support substantially however I am expecting a follow up with the tweaks Paul suggests here please.

Cheers,

Richard

#9 lore raw

Re: [PATCH V2 6/9] core/target/ssh.py: replace decode errors

Quoted 52 lines · Richard Purdie
Quoted 51 lines · Paul Eggleton
Quoted 45 lines · NZST Robert Yang

There might be wild strings when read from target (especially when reading ptest results), replace the errors to avoid breaking the test.

Fixed: (Not always happen)

$ bitbake core-image-sato -ctestimage
[snip]
    status, output = self.target.run('ptest-runner', 0)
  File "/buildarea/lyang1/poky/meta/lib/oeqa/core/target/ssh.py",
line 84, in run
    status, output = self._run(sshCmd, processTimeout, True)
  File "/buildarea/lyang1/poky/meta/lib/oeqa/core/target/ssh.py",
line 55, in _run
    status, output = SSHCall(command, self.logger, timeout)
  File "/buildarea/lyang1/poky/meta/lib/oeqa/core/target/ssh.py",
line 258, in SSHCall
    run()
  File "/buildarea/lyang1/poky/meta/lib/oeqa/core/target/ssh.py",
line 236, in run
    output = process.communicate()[0].decode("utf-8")
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc3 in
position 4906: invalid continuation byte

[YOCTO #11547]

---
 meta/lib/oeqa/core/target/ssh.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
meta/lib/oeqa/core/target/ssh.py +1 -1
index b80939c..a2eafcd 100644
@@ -211,7 +211,7 @@ def SSHCall(command, logger, timeout=None,
 **opts):
                             process.stdout.close()
                             eof = True
                         else:
-                            data = data.decode("utf-8")
+                            data = data.decode("utf-8",
 errors='replace')
                             output += data
Quoted 2 lines

logger.debug('Partial data from SSH
call: %s' % data)

Since we're dealing with partial data here, shouldn't we be using a reader object? e.g.:

Quoted 7 lines
Quoted 2 lines

http://git.yoctoproject.org/cgit/cgit.cgi/poky/commit/?id=bfd8c35c3
f917e3806c8dfe36c98c70fbccbb3c9

I've decided to merge this series as it improves the ptest support substantially however I am expecting a follow up with the tweaks Paul suggests here please.

Thanks, sent:
[OE-core] [PATCH 0/1] core/target/ssh.py: use reader to handle partial data

// Robert

Quoted 6 lines

Cheers,

Richard

#10 lore raw

PATCH v2 7/9 buildhistory.bbclass: print message when no commit

In reply to Robert Yang

This makes the user easier to know how to make commit in buildhistory.

[YOCTO #11547]

---
 meta/classes/buildhistory.bbclass | 2 ++
 1 file changed, 2 insertions(+)
meta/classes/buildhistory.bbclass +2 -0
index 81784ee..a3e4c7a 100644
@@ -824,6 +824,8 @@ python buildhistory_eventhandler() {
                 interrupted = getattr(e, '_interrupted', 0)
                 localdata.setVar('BUILDHISTORY_BUILD_INTERRUPTED', str(interrupted))
                 bb.build.exec_func("buildhistory_commit", localdata)
+            else:
+                bb.note("No commit since BUILDHISTORY_COMMIT != '1'")
 }
 
 addhandler buildhistory_eventhandler
Signature · 1 line
2.10.2
#11 lore raw

PATCH v2 8/9 testimage.bbclass: update comments

In reply to Robert Yang

It's very important to add IMAGE_CLASSES += "testimage" in local.conf firstly, otherwise the var like TEST_LOG_DIR (defined in testimage.bbclass) will not be in testdata.json.

[YOCTO #11547]

---
 meta/classes/testimage.bbclass | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
meta/classes/testimage.bbclass +2 -2
index 0c4a84e..6a43560 100644
@@ -7,8 +7,8 @@
 # Most of the tests are commands run on target image over ssh.
 # To use it add testimage to global inherit and call your target image with -c testimage
 # You can try it out like this:
-# - first build a qemu core-image-sato
-# - add IMAGE_CLASSES += "testimage" in local.conf
+# - first add IMAGE_CLASSES += "testimage" in local.conf
+# - build a qemu core-image-sato
 # - then bitbake core-image-sato -c testimage. That will run a standard suite of tests.
 
 # You can set (or append to) TEST_SUITES in local.conf to select the tests
Signature · 1 line
2.10.2
#12 lore raw

PATCH v2 9/9 buildhistory.bbclass: add ptest

In reply to Robert Yang

The ptest log will be saved to buildhistory/ptest, we can easily get the regression result between builds by:

$ git show HEAD ptest/pass.fail.skip.*

[YOCTO #11547]

---
 meta/classes/buildhistory.bbclass | 30 ++++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)
meta/classes/buildhistory.bbclass +30 -0
index a3e4c7a..dbfcc05 100644
@@ -912,3 +912,33 @@ def write_latest_srcrev(d, pkghistdir):
     else:
         if os.path.exists(srcrevfile):
             os.remove(srcrevfile)
+
+do_testimage[postfuncs] += "write_ptest_result"
+do_testimage[vardepsexclude] += "write_ptest_result"
+
+python write_ptest_result() {
+    write_latest_ptest_result(d, d.getVar('BUILDHISTORY_DIR'))
+}
+
+def write_latest_ptest_result(d, histdir):
+    import glob
+    import subprocess
+    test_log_dir = d.getVar('TEST_LOG_DIR')
+    input_ptest = os.path.join(test_log_dir, 'ptest_log')
+    output_ptest = os.path.join(histdir, 'ptest')
+    if os.path.exists(input_ptest):
+        try:
+            # Lock it avoid race issue
+            lock = bb.utils.lockfile(output_ptest + "/ptest.lock")
+            bb.utils.mkdirhier(output_ptest)
+            oe.path.copytree(input_ptest, output_ptest)
+            # Sort test result
+            for result in glob.glob('%s/pass.fail.*' % output_ptest):
+                bb.debug(1, 'Processing %s' % result)
+                cmd = ['sort', result, '-o', result]
+                bb.debug(1, 'Running %s' % cmd)
+                ret = subprocess.call(cmd)
+                if ret != 0:
+                    bb.error('Failed to run %s!' % cmd)
+        finally:
+            bb.utils.unlockfile(lock)
Signature · 1 line
2.10.2