diff --git a/tests/lib/external/snapd-testing-tools/.github/labeler.yml b/tests/lib/external/snapd-testing-tools/.github/labeler.yml
new file mode 100644
index 0000000..1f1b04b
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/.github/labeler.yml
@@ -0,0 +1,6 @@
+# Add 'needs tests' label to either any change on remote, tools or utils dir
+needs tests:
+ - remote/**/*
+ - tools/**/*
+ - utils/**/*
+
diff --git a/tests/lib/external/snapd-testing-tools/.github/workflows/labeler.yaml b/tests/lib/external/snapd-testing-tools/.github/workflows/labeler.yaml
new file mode 100644
index 0000000..bd41c8e
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/.github/workflows/labeler.yaml
@@ -0,0 +1,12 @@
+name: "Pull Request Labeler"
+on:
+ - pull_request_target
+
+jobs:
+ triage:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/labeler@v4
+ with:
+ repo-token: "${{ secrets.GITHUB_TOKEN }}"
+ sync-labels: false
diff --git a/tests/lib/external/snapd-testing-tools/.github/workflows/tests.yaml b/tests/lib/external/snapd-testing-tools/.github/workflows/tests.yaml
new file mode 100644
index 0000000..a1b8be7
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/.github/workflows/tests.yaml
@@ -0,0 +1,131 @@
+name: Jobs
+on:
+ pull_request:
+ branches:
+ - main
+ push:
+ branches:
+ - main
+
+jobs:
+ unit-tests:
+ runs-on: ubuntu-20.04
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v2
+
+ - name: Run shellCheck for tools
+ run: |
+ # Run shellcheck from the snap which most likely is newer than what the distro provides
+ sudo apt-get remove --purge shellcheck
+ sudo snap install shellcheck
+ find tools utils remote -type f -exec sh -c "head -n 1 {} | egrep -a 'bin/bash|bin/sh' >/dev/null" \; -print -exec shellcheck {} \;
+
+ # Run shellcheck from the distro
+ sudo snap remove shellcheck
+ sudo apt-get install -y shellcheck
+ find tools utils remote -type f -exec sh -c "head -n 1 {} | egrep -a 'bin/bash|bin/sh' >/dev/null" \; -print -exec shellcheck {} \;
+
+ - name: Run codespell tool
+ run: |
+ sudo apt install python3-pip
+ pip3 install codespell
+ codespell
+
+ test:
+ needs: [unit-tests]
+ runs-on: [self-hosted, spread-enabled]
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v2
+
+ - name: Get previous attempt
+ id: get-previous-attempt
+ run: |
+ echo "previous_attempt=$(( ${{ github.run_attempt }} - 1 ))" >> $GITHUB_OUTPUT
+ shell: bash
+
+ - name: Get previous cache
+ uses: actions/cache@v3
+ with:
+ path: "${{ github.workspace }}/.test-results"
+ key: "${{ github.job }}-results-${{ github.run_id }}-${{ steps.get-previous-attempt.outputs.previous_attempt }}"
+
+ - name: Prepare test results env and vars
+ id: prepare-test-results-env
+ run: |
+ # Create test results directories and save vars
+ TEST_RESULTS_DIR="${{ github.workspace }}/.test-results"
+ echo "TEST_RESULTS_DIR=$TEST_RESULTS_DIR" >> $GITHUB_ENV
+
+ # Save the var with the failed tests file
+ echo "FAILED_TESTS_FILE=$TEST_RESULTS_DIR/failed-tests" >> $GITHUB_ENV
+
+ # Make sure the test results dirs are created
+ # This step has to be after the cache is restored
+ mkdir -p "$TEST_RESULTS_DIR"
+
+ - name: Check failed tests to run
+ if: "!contains(github.event.pull_request.labels.*.name, 'Run all')"
+ run: |
+ # Save previous failed test results in FAILED_TESTS env var
+ FAILED_TESTS=""
+ if [ -f "$FAILED_TESTS_FILE" ]; then
+ echo "Failed tests file found"
+ FAILED_TESTS="$(cat $FAILED_TESTS_FILE)"
+ if [ -n "$FAILED_TESTS" ]; then
+ echo "Failed tests to run: $FAILED_TESTS"
+ echo "FAILED_TESTS=$FAILED_TESTS" >> $GITHUB_ENV
+ fi
+ fi
+
+ - name: Run test
+ run: |
+ RUN_TESTS="google:tests/ google-nested:tests/ openstack:tests/"
+ if [ -n "$FAILED_TESTS" ]; then
+ RUN_TESTS="$FAILED_TESTS"
+ fi
+ CHANGE_ID="${{ github.event.number }}"
+ if [ -z "$PR_ID" ]; then
+ CHANGE_ID="main"
+ fi
+ CHANGE_ID="${CHANGE_ID}_n${{ github.run_attempt }}"
+ # The log-filter tool is used to filter the spread logs to be stored
+ FILTER_PARAMS="-o spread_$CHANGE_ID.filtered.log -e Debug -e WARNING: -f Failed=NO_LINES -f Error=NO_LINES"
+
+ (set -o pipefail; spread $RUN_TESTS | ./utils/log-filter $FILTER_PARAMS | tee spread.log)
+
+ - name: Discard spread workers
+ if: always()
+ run: |
+ shopt -s nullglob;
+ for r in .spread-reuse.*.yaml; do
+ spread -discard -reuse-pid="$(echo "$r" | grep -o -E '[0-9]+')";
+ done
+
+ - name: analyze spread test results
+ if: always()
+ run: |
+ if [ -f spread.log ]; then
+ echo "Running spread log parser"
+ ./utils/log-parser spread.log --output spread-results.json
+
+ echo "Determining which tests were executed"
+ RUN_TESTS="google:tests/ google-nested:tests/"
+ if [ -n "$FAILED_TESTS" ]; then
+ RUN_TESTS="$FAILED_TESTS"
+ fi
+
+ echo "Running spread log analyzer"
+ ./utils/log-analyzer list-reexecute-tasks "$RUN_TESTS" spread-results.json > "$FAILED_TESTS_FILE"
+ else
+ echo "No spread log found, saving empty list of failed tests"
+ touch "$FAILED_TESTS_FILE"
+ fi
+
+ - name: save spread test results to cache
+ if: always()
+ uses: actions/cache/save@v3
+ with:
+ path: "${{ github.workspace }}/.test-results"
+ key: "${{ github.job }}-results-${{ github.run_id }}-${{ github.run_attempt }}"
diff --git a/tests/lib/external/snapd-testing-tools/CODE_OF_CONDUCT.md b/tests/lib/external/snapd-testing-tools/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000..e8d83b7
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/CODE_OF_CONDUCT.md
@@ -0,0 +1,76 @@
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+In the interest of fostering an open and welcoming environment, we as
+contributors and maintainers pledge to making participation in our project and
+our community a harassment-free experience for everyone, regardless of age, body
+size, disability, ethnicity, sex characteristics, gender identity and expression,
+level of experience, education, socioeconomic status, nationality, personal
+appearance, race, religion, or sexual identity and orientation.
+
+## Our Standards
+
+Examples of behavior that contributes to creating a positive environment
+include:
+
+* Using welcoming and inclusive language
+* Being respectful of differing viewpoints and experiences
+* Gracefully accepting constructive criticism
+* Focusing on what is best for the community
+* Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery and unwelcome sexual attention or
+ advances
+* Trolling, insulting/derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or electronic
+ address, without explicit permission
+* Other conduct which could reasonably be considered inappropriate in a
+ professional setting
+
+## Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of acceptable
+behavior and are expected to take appropriate and fair corrective action in
+response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or
+reject comments, commits, code, wiki edits, issues, and other contributions
+that are not aligned to this Code of Conduct, or to ban temporarily or
+permanently any contributor for other behaviors that they deem inappropriate,
+threatening, offensive, or harmful.
+
+## Scope
+
+This Code of Conduct applies both within project spaces and in public spaces
+when an individual is representing the project or its community. Examples of
+representing a project or community include using an official project e-mail
+address, posting via an official social media account, or acting as an appointed
+representative at an online or offline event. Representation of a project may be
+further defined and clarified by project maintainers.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported by contacting the project team at snap-advocacy@canonical.com. All
+complaints will be reviewed and investigated and will result in a response that
+is deemed necessary and appropriate to the circumstances. The project team is
+obligated to maintain confidentiality with regard to the reporter of an incident.
+Further details of specific enforcement policies may be posted separately.
+
+Project maintainers who do not follow or enforce the Code of Conduct in good
+faith may face temporary or permanent repercussions as determined by other
+members of the project's leadership.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
+available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
+
+[homepage]: https://www.contributor-covenant.org
+
+For answers to common questions about this code of conduct, see
+https://www.contributor-covenant.org/faq
diff --git a/tests/lib/external/snapd-testing-tools/COPYING b/tests/lib/external/snapd-testing-tools/COPYING
new file mode 100644
index 0000000..94a9ed0
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/COPYING
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
diff --git a/tests/lib/external/snapd-testing-tools/README.md b/tests/lib/external/snapd-testing-tools/README.md
new file mode 100644
index 0000000..9f213f0
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/README.md
@@ -0,0 +1,74 @@
+[![Snapcraft](https://avatars2.githubusercontent.com/u/19532717?s=200)](https://snapcraft.io)
+
+# Welcome to snapd-testing-tools
+
+This is the code repository for **snapd-testing-tools**, the set of tools
+used by snapd for testing porpuses.
+
+The tools in this project are designed and tested independently, making them easy
+to be imported and used by any project.
+
+## Get involved
+
+This is an [open source](COPYING) project and we warmly welcome community
+contributions, suggestions, and constructive feedback. If you're interested in
+contributing, please take a look at our [Code of Conduct](CODE_OF_CONDUCT.md)
+first.
+
+- to report an issue, please file [a bug
+ report](https://bugs.launchpad.net/snappy/+filebug) on our [Launchpad issue
+tracker](https://bugs.launchpad.net/snappy/)
+- for suggestions and constructive feedback, create a post on the [Snapcraft
+ forum](https://forum.snapcraft.io/c/snapd)
+
+## Get in touch
+
+We're friendly! We have a community forum at
+[https://forum.snapcraft.io](https://forum.snapcraft.io) where we discuss
+feature plans, development news, issues, updates and troubleshooting. You can
+chat in realtime with the snapd team and our wider community on the
+[#snappy](https://web.libera.chat?channel=#snappy) IRC channel on
+[libera chat](https://libera.chat/).
+
+For news and updates, follow us on [Twitter](https://twitter.com/snapcraftio)
+and on [Facebook](https://www.facebook.com/snapcraftio).
+
+## Adding new tools
+
+The tools included in this project are intended to be reused by other projects.
+
+Tools are supported in all the systems included in spread.yaml file.
+
+Read the following considerations before adding new tools:
+
+ - Each tool needs to be accompanied by at least 1 spread test in `tests//`
+ - At least 1 spread test needs to be included in the tests directory for each tool
+ - If the tool is a shell script, it needs to first pass a [ShellCheck](https://github.com/koalaman/shellcheck) assessment
+ - All tools need to be as generic as possible
+ - Each tool must also provide a command line interface (CLI), including _help_ output
+
+## Adding new utils
+
+The utils included in this project are intended to be reused by other projects.
+
+Utils are used as a complement for spread tests executions. Those are not intended to be used by spread tests.
+For example utils are used on github action workflows to analyze tests code and outputs.
+
+Utils are supported in ubuntu-18.04 and higher.
+
+Read the following considerations before adding new utils:
+
+ - Each util needs to be accompanied by at least 1 spread test in `tests//`
+ - At least 1 spread test needs to be included in the tests directory for each util
+ - If the util is a shell script, it needs to first pass a [ShellCheck](https://github.com/koalaman/shellcheck) assessment
+ - All utils need to be as generic as possible
+ - Each util must also provide a command line interface (CLI), including _help_ output
+
+
+## Project status
+
+| Service | Status |
+|-----|:---|
+| [Github Actions](https://github.com/actions/) | ![Build Status][actions-image] |
+
+[actions-image]: https://github.com/snapcore/snapd-testing-tools/actions
diff --git a/tests/lib/external/snapd-testing-tools/remote/remote.exec b/tests/lib/external/snapd-testing-tools/remote/remote.exec
new file mode 100755
index 0000000..b9d7fbf
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/remote/remote.exec
@@ -0,0 +1,88 @@
+#!/bin/bash -e
+
+show_help() {
+ echo "usage: remote.exec [--user ] [--pass ] "
+ echo ""
+ echo "Available options:"
+ echo " -h --help show this help message."
+ echo ""
+}
+
+_load_config() {
+ local CFG_FILE
+ CFG_FILE="$(remote.setup get-config-path)"
+ if [ ! -f "$CFG_FILE" ]; then
+ echo "remote.exec: config file \"$CFG_FILE\" not found, please run remote.setup command first"
+ return 1
+ fi
+ # shellcheck disable=SC1090
+ . "$CFG_FILE"
+}
+
+_get_pass() {
+ local SSH_PASS
+ if [ -n "$TESTS_REMOTE_PASS" ]; then
+ echo "sshpass -p $TESTS_REMOTE_PASS"
+ fi
+}
+
+_get_cert() {
+ if [ -n "$TESTS_REMOTE_PASS" ]; then
+ return
+ elif [ -n "$TESTS_REMOTE_CERT" ]; then
+ echo "-i $TESTS_REMOTE_CERT"
+ fi
+}
+
+remote_exec() {
+ local user pass
+ local timeout=10
+ while [ $# -gt 0 ]; do
+ case "$1" in
+ -h|--help)
+ show_help
+ exit
+ ;;
+ --user)
+ user="$2"
+ shift 2
+ ;;
+ --pass)
+ pass="$2"
+ shift 2
+ ;;
+ --timeout)
+ timeout="$2"
+ shift 2
+ ;;
+ -*)
+ echo "remote.exec: unknown option $1" >&2
+ exit 1
+ ;;
+ *)
+ break
+ ;;
+ esac
+ done
+
+ _load_config
+ if [ -n "$user" ]; then
+ TESTS_REMOTE_USER="$user"
+ fi
+ if [ -n "$pass" ]; then
+ TESTS_REMOTE_PASS="$pass"
+ fi
+
+ local SSH_PASS SSH_CERT
+ SSH_PASS="$(_get_pass)"
+ SSH_CERT="$(_get_cert)"
+
+ # shellcheck disable=SC2153,SC2086
+ $SSH_PASS ssh $SSH_CERT -p "$TESTS_REMOTE_PORT" -o LogLevel=ERROR -o ServerAliveInterval=10 -o ConnectTimeout="$timeout" -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "$TESTS_REMOTE_USER"@"$TESTS_REMOTE_HOST" "$@"
+}
+
+main() {
+ remote_exec "$@"
+}
+
+main "$@"
diff --git a/tests/lib/external/snapd-testing-tools/remote/remote.pull b/tests/lib/external/snapd-testing-tools/remote/remote.pull
new file mode 100755
index 0000000..544b373
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/remote/remote.pull
@@ -0,0 +1,67 @@
+#!/bin/bash -e
+
+show_help() {
+ echo "usage: remote.pull [LOCAL_PATH]"
+ echo ""
+ echo "Available options:"
+ echo " -h --help show this help message."
+ echo ""
+}
+
+_load_config() {
+ local CFG_FILE
+ CFG_FILE="$(remote.setup get-config-path)"
+ if [ ! -f "$CFG_FILE" ]; then
+ echo "remote.pull: config file \"$CFG_FILE\" not found, please run remote.setup command first"
+ return 1
+ fi
+ # shellcheck disable=SC1090
+ . "$CFG_FILE"
+}
+
+_get_pass() {
+ if [ -n "$TESTS_REMOTE_PASS" ]; then
+ echo "sshpass -p $TESTS_REMOTE_PASS"
+ fi
+}
+
+_get_cert() {
+ if [ -n "$TESTS_REMOTE_PASS" ]; then
+ return
+ elif [ -n "$TESTS_REMOTE_CERT" ]; then
+ echo "-i $TESTS_REMOTE_CERT"
+ fi
+}
+
+remote_pull() {
+ local REMOTE_PATH="$1"
+ local LOCAL_PATH="${2:-.}"
+ if [ -z "$REMOTE_PATH" ]; then
+ echo "remote.pull: remote path is required"
+ fi
+
+ local SSH_PASS SSH_CERT
+ SSH_PASS="$(_get_pass)"
+ SSH_CERT="$(_get_cert)"
+
+ LEGACY_PARAM="-O"
+ # We check if -O option is supported by scp
+ if scp -O 2>&1 | grep -q "unknown option"; then
+ LEGACY_PARAM=""
+ fi
+
+ # shellcheck disable=SC2153,SC2086
+ $SSH_PASS scp $SSH_CERT $LEGACY_PARAM -P "$TESTS_REMOTE_PORT" -o ConnectTimeout=10 -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "$TESTS_REMOTE_USER"@"$TESTS_REMOTE_HOST":"$REMOTE_PATH" "$LOCAL_PATH"
+}
+
+main() {
+ if [ $# -eq 0 ] || [ "$1" == '-h' ] || [ "$1" == '--help' ]; then
+ show_help
+ exit 0
+ fi
+
+ _load_config
+ remote_pull "$@"
+}
+
+main "$@"
diff --git a/tests/lib/external/snapd-testing-tools/remote/remote.push b/tests/lib/external/snapd-testing-tools/remote/remote.push
new file mode 100755
index 0000000..095be0c
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/remote/remote.push
@@ -0,0 +1,61 @@
+#!/bin/bash -e
+
+show_help() {
+ echo "usage: remote.push [REMOTE_PATH]"
+ echo ""
+ echo "Available options:"
+ echo " -h --help show this help message."
+ echo ""
+}
+
+_load_config() {
+ local CFG_FILE
+ CFG_FILE="$(remote.setup get-config-path)"
+ if [ ! -f "$CFG_FILE" ]; then
+ echo "remote.push: config file \"$CFG_FILE\" not found, please run remote.setup command first"
+ return 1
+ fi
+ # shellcheck disable=SC1090
+ . "$CFG_FILE"
+}
+
+_get_pass() {
+ if [ -n "$TESTS_REMOTE_PASS" ]; then
+ echo "sshpass -p $TESTS_REMOTE_PASS"
+ fi
+}
+
+_get_cert() {
+ if [ -n "$TESTS_REMOTE_PASS" ]; then
+ return
+ elif [ -n "$TESTS_REMOTE_CERT" ]; then
+ echo "-i $TESTS_REMOTE_CERT"
+ fi
+}
+
+remote_push() {
+ local LOCAL_PATH="$1"
+ local REMOTE_PATH="${2:-}"
+ if [ -z "$LOCAL_PATH" ]; then
+ echo "remote.push: local path is required"
+ fi
+
+ local SSH_PASS SSH_CERT
+ SSH_PASS="$(_get_pass)"
+ SSH_CERT="$(_get_cert)"
+
+ # shellcheck disable=SC2153,SC2086
+ $SSH_PASS scp $SSH_CERT -P "$TESTS_REMOTE_PORT" -o ConnectTimeout=10 -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "$LOCAL_PATH" "$TESTS_REMOTE_USER"@"$TESTS_REMOTE_HOST":"$REMOTE_PATH"
+}
+
+main() {
+ if [ $# -eq 0 ] || [ "$1" == '-h' ] || [ "$1" == '--help' ]; then
+ show_help
+ exit 0
+ fi
+
+ _load_config
+ remote_push "$@"
+}
+
+main "$@"
diff --git a/tests/lib/external/snapd-testing-tools/remote/remote.refresh b/tests/lib/external/snapd-testing-tools/remote/remote.refresh
new file mode 100755
index 0000000..95aa073
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/remote/remote.refresh
@@ -0,0 +1,322 @@
+#!/bin/bash -e
+
+show_help() {
+ echo "usage: remote.refresh snap [--channel CHANNEL] "
+ echo " remote.refresh full [--channel CHANNEL]"
+ echo " remote.refresh disable-refreshes"
+ echo ""
+ echo "SNAPNAME: allowed options are: kernel, core, base, gadget, snapd or the snap name"
+ echo ""
+ echo "Available options:"
+ echo " -h --help show this help message."
+ echo ""
+}
+
+check_refresh_after_reboot() {
+ local refresh_channel=$1
+ local snap=$2
+
+ echo "remote.refresh: checking snap \"$snap\" refresh after reboot"
+ remote.wait-for reboot
+ remote.wait-for snap-command
+ remote.retry -n 10 --wait 2 "snap info $snap | grep -q -E \"tracking: +(latest/${refresh_channel}|${refresh_channel})\""
+ echo "remote.refresh: snap \"$snap\" refreshed correctly"
+}
+
+check_refresh() {
+ local refresh_channel=$1
+ local snap=$2
+
+ echo "remote.refresh: checking snap \"$snap\" refresh with no reboot"
+ remote.wait-for ssh -n 60 --wait 2
+ remote.wait-for snap-command
+ remote.retry -n 10 --wait 2 "snap info $snap | grep -q -E \"tracking: +(latest/${refresh_channel}|${refresh_channel})\""
+ echo "remote.refresh: snap \"$snap\" refreshed correctly"
+}
+
+check_waiting_for_reboot() {
+ remote.exec "sudo journalctl -n 30" | grep -q "Waiting for system reboot"
+}
+
+check_ready_to_refresh() {
+ remote.wait-for ssh
+ if check_waiting_for_reboot; then
+ echo "remote.refresh: waiting for system reboot"
+ remote.wait-for reboot
+ fi
+ remote.wait-for refresh
+}
+
+do_refresh() {
+ local snap_name=$1
+ local refresh_channel=$2
+
+ echo "remote.refresh: triggering refresh for snap \"$snap_name\" on channel \"$refresh_channel\""
+ output=$(remote.exec "sudo snap refresh --channel ${refresh_channel} $snap_name 2>&1" || echo "snapd is about to reboot the system")
+ if echo "$output" | grep -E "(no updates available|cannot refresh \"$snap_name\"|is not installed)"; then
+ echo "remote.refresh: snap \"$snap_name\" has no updates available"
+ elif echo "$output" | grep -E "snapd is about to reboot the system"; then
+ remote.exec --timeout 3 "sudo reboot" || true
+ check_refresh_after_reboot "$refresh_channel" "$snap_name"
+ else
+ check_refresh "$refresh_channel" "$snap_name"
+ fi
+}
+
+process_refresh() {
+ local snap_name=$1
+ local refresh_channel=$2
+
+ if [ -z "$refresh_channel" ]; then
+ # Tracking is retrieved from snap info command because in old versions of
+ # snapd the tracking is not included in the snap list output
+ refresh_channel="$(remote.exec "snap info $snap_name" | grep -E '^tracking:' | awk '{ print $2 }')"
+ fi
+
+ echo "remote.refresh: Refreshing $snap_name snap from $refresh_channel channel"
+ do_refresh "$snap_name" "$refresh_channel"
+}
+
+refresh_fundamental() {
+ snap_name=$1
+ regex=$2
+ refresh_channel=$3
+
+ echo "remote.refresh: starting $snap_name refresh process"
+ snap_name="$(remote.exec "snap list" | grep -E "$regex" | awk '{ print $1 }')"
+ if [ -z "$snap_name" ]; then
+ echo "remote.refresh: no $snap_name snap to update"
+ return
+ fi
+
+ if [ "$(echo "$snap_name" | wc -l)" -gt 1 ]; then
+ echo "remote.refresh: there is more than 1 $snap_name snap to update, skipping"
+ return 0
+ fi
+ process_refresh "$snap_name" "$refresh_channel"
+}
+
+refresh_kernel() {
+ local refresh_channel=$1
+ local snap_name regex
+
+ snap_name='kernel'
+ regex='(kernel$|kernel,)'
+ refresh_fundamental "$snap_name" "$regex" "$refresh_channel"
+}
+
+refresh_gadget() {
+ local refresh_channel=$1
+ local snap_name regex
+
+ snap_name='gadget'
+ regex='(gadget$|gadget,)'
+ refresh_fundamental "$snap_name" "$regex" "$refresh_channel"
+}
+
+refresh_snapd() {
+ local refresh_channel=$1
+ local snap_name regex
+
+ snap_name='snapd'
+ regex='^snapd.*(snapd$|snapd,)'
+ refresh_fundamental "$snap_name" "$regex" "$refresh_channel"
+}
+
+refresh_core() {
+ local refresh_channel=$1
+ local snap_name regex
+
+ snap_name='core'
+ regex='^core.*(core$|core,)'
+ refresh_fundamental "$snap_name" "$regex" "$refresh_channel"
+}
+
+refresh_core_base() {
+ local refresh_channel=$1
+ local snap_name regex
+
+ snap_name='core base'
+ regex='^core.* (base$|base,)'
+ refresh_fundamental "$snap_name" "$regex" "$refresh_channel"
+}
+
+refresh_snap() {
+ local snapname=$1
+ local refresh_channel=$2
+
+ if [ -z "$snapname" ]; then
+ echo "remote.refresh: snap name to refresh is not specified"
+ return 1
+ fi
+
+ if ! remote.exec "snap list $snapname"; then
+ echo "remote.refresh: no $snapname snap to update"
+ return 0
+ fi
+
+ snap_line="$(remote.exec "snap list $snapname" | tail -1)"
+ process_refresh "$snap_line" "$refresh_channel"
+}
+
+refresh_all() {
+ # Run update and make "|| true" to continue when the connection is closed by remote host or not any snap to update
+ remote.exec "sudo snap refresh" || true
+ remote.wait-for ssh
+}
+
+
+get_boot_id() {
+ remote.exec "cat /proc/sys/kernel/random/boot_id"
+}
+
+prevent_autorefresh() {
+ remote.wait-for ssh
+ state_path="$(remote.exec 'find /writable -name state.json 2>/dev/null' || true)"
+ if [ -z "$state_path" ]; then
+ echo "remote.refresh: state file not found in writable"
+ state_path="$(remote.exec 'find / -name state.json 2>/dev/null' || true)"
+ fi
+ if [ -z "$state_path" ]; then
+ echo "remote.refresh: state.json file not found"
+ exit 1
+ fi
+
+ remote.exec "sudo cp $state_path /tmp/state.json"
+ remote.exec "sudo chmod 644 /tmp/state.json"
+ remote.pull /tmp/state.json state.json
+ echo "remote.refresh: state file retrieved"
+
+ jq ".data[\"last-refresh\"] = \"$(date +%Y-%m-%dT%H:%M:%S%:z)\"" state.json > state.json.new
+ echo "remote.refresh: state file just updated"
+
+ remote.push state.json.new /tmp/state.json.new
+ remote.exec "sudo cp /tmp/state.json.new $state_path"
+ remote.exec "sudo chmod 600 $state_path"
+ echo "remote.refresh: updated state file restored"
+ rm -f state.json state.json.new
+}
+
+disable_refreshes() {
+ if ! command -v jq &>/dev/null; then
+ snap install --devmode jq
+ fi
+
+ echo "remote.refresh: modifying state to make it look like the last refresh just happened"
+ remote.exec "sudo systemctl stop snapd.socket snapd.service"
+ prevent_autorefresh
+ remote.exec "sudo systemctl start snapd.socket snapd.service"
+
+ echo "remote.refresh: minimizing risk of hitting refresh schedule"
+ remote.exec "sudo snap set core refresh.schedule=00:00-23:59"
+ remote.exec "sudo snap refresh --time --abs-time" | grep -Eq "last: 2[0-9]{3}"
+
+}
+
+full_refresh() {
+ echo "remote.refresh: starting full refresh"
+
+ check_ready_to_refresh
+ disable_refreshes
+
+ check_ready_to_refresh
+ refresh_core
+
+ check_ready_to_refresh
+ refresh_core_base
+
+ check_ready_to_refresh
+ refresh_snapd
+
+ check_ready_to_refresh
+ refresh_kernel
+
+ check_ready_to_refresh
+ refresh_all
+
+ check_ready_to_refresh
+}
+
+snap_refresh() {
+ local channel snapname
+
+ while [ $# -gt 0 ]; do
+ case "$1" in
+ --channel)
+ channel=$2
+ shift 2
+ ;;
+ *)
+ snapname=$1
+ shift
+ ;;
+ esac
+ done
+
+ if [ -z "$snapname" ]; then
+ echo "remote.refresh: snap name to refresh is not specified"
+ return 1
+ fi
+
+ case "$snapname" in
+ core)
+ refresh_core "$channel"
+ ;;
+ base)
+ refresh_core_base "$channel"
+ ;;
+ snapd)
+ refresh_snapd "$channel"
+ ;;
+ kernel)
+ refresh_kernel "$channel"
+ ;;
+ gadget)
+ refresh_gadget "$channel"
+ ;;
+ *)
+ refresh_snap "$snapname" "$channel"
+ ;;
+ esac
+}
+
+main() {
+ if [ $# -eq 0 ]; then
+ show_help
+ exit
+ fi
+
+ local action
+ case "$1" in
+ -h|--help)
+ show_help
+ exit
+ ;;
+ full)
+ action=full_refresh
+ shift
+ ;;
+ snap)
+ action=snap_refresh
+ shift
+ ;;
+ disable-refreshes)
+ action=disable_refreshes
+ shift
+ ;;
+ *)
+ echo "remote.refresh: unsupported parameter $1" >&2
+ exit 1
+ ;;
+ esac
+
+ if [ -z "$(declare -f "$action")" ]; then
+ echo "remote.refresh: no such command: $action"
+ show_help
+ exit 1
+ fi
+
+ "$action" "$@"
+}
+
+main "$@"
diff --git a/tests/lib/external/snapd-testing-tools/remote/remote.retry b/tests/lib/external/snapd-testing-tools/remote/remote.retry
new file mode 100755
index 0000000..bfe72f2
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/remote/remote.retry
@@ -0,0 +1,59 @@
+#!/bin/bash -e
+
+show_help() {
+ echo "usage: remote.retry [--wait WAIT] [-n|--attempts ATTEMPTS] "
+ echo ""
+ echo "Available options:"
+ echo " -h --help show this help message."
+ echo ""
+}
+
+remote_retry(){
+ local attempts=$1
+ local wait=$2
+ local cmd=$3
+
+ while ! remote.exec "$cmd"; do
+ attempts=$(( attempts - 1 ))
+ if [ $attempts -le 0 ]; then
+ echo "remote.retry: timed out retrying command"
+ return 1
+ fi
+ sleep "$wait"
+ done
+}
+
+main() {
+ local wait attempts cmd
+ wait=1
+ attempts=30
+
+ while [ $# -gt 0 ]; do
+ case "$1" in
+ -h|--help)
+ show_help
+ exit
+ ;;
+ --wait)
+ wait=$2
+ shift 2
+ ;;
+ --attempts|-n)
+ attempts=$2
+ shift 2
+ ;;
+ *)
+ break
+ ;;
+ esac
+ done
+
+ if [ $# -eq 0 ]; then
+ echo "remote.retry: command to retry not specified"
+ return 1
+ fi
+
+ remote_retry "$attempts" "$wait" "$@"
+}
+
+main "$@"
diff --git a/tests/lib/external/snapd-testing-tools/remote/remote.setup b/tests/lib/external/snapd-testing-tools/remote/remote.setup
new file mode 100755
index 0000000..89d47d0
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/remote/remote.setup
@@ -0,0 +1,100 @@
+#!/bin/bash -e
+
+CFG_FILE="${REMOTE_CFG_FILE:-$(pwd)/remote.setup.cfg}"
+
+show_help() {
+ echo "usage: remote.setup config --host --port --user [--pass ] [--cert ]"
+ echo " remote.setup get-config-path"
+ echo ""
+ echo "Available options:"
+ echo " -h --help show this help message."
+ echo ""
+}
+
+get_config_path() {
+ echo "$CFG_FILE"
+}
+
+config() {
+ local host port user pass cert
+ while [ $# -gt 0 ]; do
+ case "$1" in
+ -h|--help)
+ show_help
+ exit
+ ;;
+ --host)
+ host="$2"
+ shift 2
+ ;;
+ --port)
+ port="$2"
+ shift 2
+ ;;
+ --user)
+ user="$2"
+ shift 2
+ ;;
+ --pass)
+ pass="$2"
+ shift 2
+ ;;
+ --cert)
+ cert="$2"
+ shift 2
+ ;;
+ *)
+ echo "tests.remote: unknown option $1" >&2
+ exit 1
+ ;;
+ esac
+ done
+
+ if [ -z "$host" ] || [ -z "$port" ] || [ -z "$user" ]; then
+ echo "remote.setup: host, port and user values are required"
+ exit 1
+ fi
+ if [ -n "$pass" ] && [ -z "$(command -v sshpass)" ]; then
+ echo "remote.setup: sshpass tool is required when password is configured"
+ fi
+ if [ -n "$cert" ] && ! [ -f "$cert" ]; then
+ echo "remote.setup: certificate is set but file does not exist"
+ exit 1
+ fi
+
+ rm -f "$CFG_FILE"
+ echo "export TESTS_REMOTE_HOST=$host" > "$CFG_FILE"
+ # shellcheck disable=SC2129
+ echo "export TESTS_REMOTE_PORT=$port" >> "$CFG_FILE"
+ echo "export TESTS_REMOTE_USER=$user" >> "$CFG_FILE"
+ echo "export TESTS_REMOTE_PASS=$pass" >> "$CFG_FILE"
+ echo "export TESTS_REMOTE_CERT=$cert" >> "$CFG_FILE"
+}
+
+main() {
+ local subcommand="$1"
+ local action=
+ while [ $# -gt 0 ]; do
+ case "$1" in
+ -h|--help)
+ show_help
+ exit 0
+ ;;
+ *)
+ action=$(echo "$subcommand" | tr '-' '_')
+ shift
+ break
+ ;;
+ esac
+ done
+
+ if [ -z "$(declare -f "$action")" ]; then
+ echo "remote.setup: no such command: $subcommand"
+ show_help
+ exit 1
+ fi
+
+ "$action" "$@"
+}
+
+main "$@"
diff --git a/tests/lib/external/snapd-testing-tools/remote/remote.wait-for b/tests/lib/external/snapd-testing-tools/remote/remote.wait-for
new file mode 100755
index 0000000..a16179d
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/remote/remote.wait-for
@@ -0,0 +1,310 @@
+#!/bin/bash -e
+
+# The default values have been selected trying to match with most of
+# the wait times in the tests and also trying to follow common sense.
+DEFAULT_WAIT_FOR_SSH_ATTEMPTS=800
+DEFAULT_WAIT_FOR_SSH_WAIT=1
+DEFAULT_WAIT_FOR_NO_SSH_ATTEMPTS=200
+DEFAULT_WAIT_FOR_NO_SSH_WAIT=1
+DEFAULT_WAIT_FOR_SNAP_COMMAND_ATTEMPTS=200
+DEFAULT_WAIT_FOR_SNAP_COMMAND_WAIT=1
+DEFAULT_WAIT_FOR_DEV_INIT_ATTEMPTS=60
+DEFAULT_WAIT_FOR_DEV_INIT_WAIT=1
+DEFAULT_WAIT_FOR_REBOOT_ATTEMPTS=120
+DEFAULT_WAIT_FOR_REBOOT_WAIT=5
+
+show_help() {
+ echo "usage: remote.wait-for ssh [--wait WAIT] [-n|--attempts ATTEMPTS]"
+ echo " remote.wait-for no-ssh [--wait WAIT] [-n|--attempts ATTEMPTS]"
+ echo " remote.wait-for snap-command [--wait WAIT] [-n|--attempts ATTEMPTS]"
+ echo " remote.wait-for reboot [--wait WAIT] [-n|--attempts ATTEMPTS]"
+ echo " remote.wait-for device-initialized [--wait WAIT] [-n|--attempts ATTEMPTS]"
+ echo " remote.wait-for refresh [--wait WAIT] [-n|--attempts ATTEMPTS]"
+ echo ""
+ echo "Available options:"
+ echo " -h --help show this help message."
+ echo ""
+}
+
+wait_for_ssh() {
+ local attempts=${1:-$DEFAULT_WAIT_FOR_SSH_ATTEMPTS}
+ local wait=${2:-$DEFAULT_WAIT_FOR_SSH_WAIT}
+ echo "remote.wait-for: waiting for ssh connection"
+
+ until remote.exec "true" &>/dev/null; do
+ echo -n '.'
+ attempts=$(( attempts - 1 ))
+ if [ $attempts -le 0 ]; then
+ echo ""
+ echo "remote.wait-for: timed out waiting for ssh connection to succeed"
+ return 1
+ fi
+ sleep "$wait"
+ done
+ echo ""
+ echo "remote.wait-for: ssh connection stablished"
+}
+
+wait_for_no_ssh() {
+ local attempts=${1:-$DEFAULT_WAIT_FOR_NO_SSH_ATTEMPTS}
+ local wait=${2:-$DEFAULT_WAIT_FOR_NO_SSH_WAIT}
+
+ echo "remote.wait-for: waiting for no ssh connection"
+
+ while remote.exec "true" &>/dev/null; do
+ echo -n '.'
+ attempts=$(( attempts - 1 ))
+ if [ $attempts -le 0 ]; then
+ echo ""
+ echo "remote.wait-for: timed out waiting for ssh connection to fail"
+ return 1
+ fi
+ sleep "$wait"
+ done
+ echo ""
+ echo "remote.wait-for: ssh connection lost"
+}
+
+
+wait_for_snap_command() {
+ local attempts=${1:-$DEFAULT_WAIT_FOR_SNAP_COMMAND_ATTEMPTS}
+ local wait=${2:-$DEFAULT_WAIT_FOR_SNAP_COMMAND_WAIT}
+
+ echo "remote.wait-for: waiting for snap command"
+
+ while ! remote.exec "command -v snap" &>/dev/null; do
+ echo -n '.'
+ attempts=$(( attempts - 1 ))
+ if [ $attempts -le 0 ]; then
+ echo ""
+ echo "remote.wait-for: timed out waiting for snap command to succeed"
+ return 1
+ fi
+ sleep "$wait"
+ done
+ echo ""
+ echo "remote.wait-for: snap command ready"
+}
+
+get_boot_id() {
+ remote.exec "cat /proc/sys/kernel/random/boot_id"
+}
+
+wait_for_reconnect_ssh() {
+ echo "remote.wait-for: waiting for ssh is recoonected"
+ wait_for_no_ssh "$DEFAULT_WAIT_FOR_NO_SSH_ATTEMPTS" "$DEFAULT_WAIT_FOR_NO_SSH_WAIT"
+ wait_for_ssh "$DEFAULT_WAIT_FOR_SSH_ATTEMPTS" "$DEFAULT_WAIT_FOR_SSH_WAIT"
+}
+
+wait_for_reboot() {
+ local attempts=${1:-$DEFAULT_WAIT_FOR_REBOOT_ATTEMPTS}
+ local wait=${2:-$DEFAULT_WAIT_FOR_REBOOT_WAIT}
+ local initial_boot_id=$3
+ local last_boot_id
+
+ echo "remote.wait-for: waiting for reboot"
+
+ if [ -z "$initial_boot_id" ]; then
+ echo "remote.wait-for: initial boot id not set"
+ wait_for_reconnect_ssh
+ return
+ fi
+
+ while [ "$attempts" -ge 0 ]; do
+ echo -n '.'
+ attempts=$(( attempts - 1 ))
+ # The get_boot_id could fail because the connection is broken due to the reboot
+ last_boot_id="$(get_boot_id)" || true
+ # The boot_id is a GUID, i.e. 450d12a1-9811-464e-8c9e-cec1c60e8684
+ if [[ "$last_boot_id" =~ .*-.*-.*-.*-.* ]] && [ "$last_boot_id" != "$initial_boot_id" ]; then
+ break
+ fi
+ sleep "$wait"
+ done
+
+ echo ""
+ if [ "$last_boot_id" != "$initial_boot_id" ]; then
+ echo "remote.wait-for: reboot completed"
+ else
+ echo "remote.wait-for: boot id did not change"
+ return 1
+ fi
+
+}
+
+wait_for_device_initialized() {
+ local attempts=${1:-$DEFAULT_WAIT_FOR_DEV_INIT_ATTEMPTS}
+ local wait=${2:-$DEFAULT_WAIT_FOR_DEV_INIT_WAIT}
+
+ echo "remote.wait-for: waiting for device initialized"
+
+ while ! remote.exec "snap changes" | grep -Eq "Done.*Initialize device"; do
+ echo -n '.'
+ attempts=$(( attempts - 1 ))
+ if [ $attempts -le 0 ]; then
+ echo ""
+ echo "remote.wait-for: timed out waiting for device to be fully initialized. Aborting!"
+ return 1
+ fi
+ sleep "$wait"
+ done
+ echo ""
+ echo "remote.wait-for: device initialized"
+}
+
+wait_for_refresh_reboot() {
+ echo "remote.wait-for: waiting for refresh reboot"
+
+ local change_id=${1:-}
+ local boot_id=${2:-}
+ if [ -z "$change_id" ] || [ -z "$boot_id" ]; then
+ echo "remote.wait-for: either change_id or boot_id not provided"
+ return 1
+ fi
+
+ wait_for_ssh "$DEFAULT_WAIT_FOR_SSH_ATTEMPTS" "$DEFAULT_WAIT_FOR_SSH_WAIT"
+ for _ in $(seq 5); do
+ # The refresh is being executed
+ # It checks a that refresh is in state either Do or Doing (and not Done)
+ if remote.exec "snap changes" | grep -Eq "$change_id.*( Do | Doing ).*(Auto-refresh|Refresh)"; then
+ # The systems is waiting for reboot
+ if remote.exec "sudo journalctl -u snapd -n 30" | grep -q "Waiting for system reboot"; then
+ echo "remote.wait-for: waiting for system reboot"
+ remote.exec "sudo reboot" || true
+ wait_for_no_ssh "$DEFAULT_WAIT_FOR_NO_SSH_ATTEMPTS" "$DEFAULT_WAIT_FOR_NO_SSH_WAIT"
+ break
+ fi
+ echo "remote.wait-for: either auto-refresh or refresh in progress"
+ fi
+
+ # when the refresh has finished and no reboot needed, the function returns
+ if remote.exec "snap changes" | grep -Eq "$change_id.*Done.*(Auto-refresh|Refresh)"; then
+ echo "remote.wait-for: refresh completed, reboot not required"
+ return
+ fi
+
+ if remote.exec "snap changes" | grep -Eq "$change_id.*Error.*(Auto-refresh|Refresh)"; then
+ echo "remote.wait-for: refresh finished with error"
+ return 1
+ fi
+
+ echo "remote.wait-for: system reboot not detected"
+ sleep 1
+ done
+ wait_for_ssh "$DEFAULT_WAIT_FOR_SSH_ATTEMPTS" "$DEFAULT_WAIT_FOR_SSH_WAIT"
+ if [ "$(remote.exec "cat /proc/sys/kernel/random/boot_id")" == "$boot_id" ]; then
+ echo "remote.wait-for: boot id did not change"
+ return 1
+ else
+ echo "remote.wait-for: boot id changed, refresh completed with reboot"
+ fi
+}
+
+wait_for_refresh(){
+ echo "remote.wait-for: waiting for either auto-refresh or refresh"
+
+ # It checks a that refresh is in state either Do or Doing (and not Done)
+ change_line="$(remote.exec 'snap changes' | grep -E '( Do | Doing ).*(Auto-refresh|Refresh)' || true)"
+ if [ -n "$change_line" ]; then
+ echo "remote.wait-for: refresh in progress"
+ change_id="$(echo "$change_line" | awk '{ print $1 }')"
+ boot_id="$(remote.exec "cat /proc/sys/kernel/random/boot_id")"
+
+ for _ in $(seq 20); do
+ if wait_for_refresh_reboot "$change_id" "$boot_id"; then
+ break
+ fi
+ done
+ echo ""
+
+ changes="$(remote.exec 'snap changes')"
+ # It checks a that refresh is in state either Do or Doing (and not Done)
+ if echo "$changes" | grep -Eq "$change_id.*( Do | Doing ).*(Auto-refresh|Refresh)"; then
+ echo "remote.wait-for: still doing refresh, exiting"
+ elif echo "$changes" | grep -Eq "$change_id.*Error.*(Auto-refresh|Refresh)"; then
+ echo "remote.wait-for: refresh failed"
+ elif echo "$changes" | grep -Eq "$change_id.*Done.*(Auto-refresh|Refresh)"; then
+ echo "remote.wait-for: refresh completed"
+ else
+ echo "remote.wait-for: refresh results unknown"
+ echo "$changes"
+ return 1
+ fi
+ else
+ echo "remote.wait-for: no refresh in progress"
+ fi
+}
+
+main() {
+ if [ $# -eq 0 ]; then
+ show_help
+ exit
+ fi
+
+ local action wait attempts others
+ case "$1" in
+ -h|--help)
+ show_help
+ exit
+ ;;
+ ssh)
+ action=wait_for_ssh
+ shift
+ ;;
+ no-ssh)
+ action=wait_for_no_ssh
+ shift
+ ;;
+ snap-command)
+ action=wait_for_snap_command
+ shift
+ ;;
+ reboot)
+ action=wait_for_reboot
+ shift
+ ;;
+ device-initialized)
+ action=wait_for_device_initialized
+ shift
+ ;;
+ refresh)
+ action=wait_for_refresh
+ shift
+ ;;
+ *)
+ echo "remote.wait-for: unsupported parameter $1" >&2
+ exit 1
+ ;;
+ esac
+
+ if [ -z "$(declare -f "$action")" ]; then
+ echo "remote.wait-for: no such command: $action"
+ show_help
+ exit 1
+ fi
+
+ while [ $# -gt 0 ]; do
+ case "$1" in
+ --wait)
+ wait=$2
+ shift 2
+ ;;
+ --attempts|-n)
+ attempts=$2
+ shift 2
+ ;;
+ *)
+ if [ -z "$others" ]; then
+ others=$1
+ else
+ others="$others $1"
+ fi
+ shift
+ ;;
+ esac
+ done
+
+ "$action" "$attempts" "$wait" "$others"
+}
+
+main "$@"
diff --git a/tests/lib/external/snapd-testing-tools/setup.sh b/tests/lib/external/snapd-testing-tools/setup.sh
new file mode 100755
index 0000000..6c25175
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/setup.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+# This script is used to setup the path and other environment variables needed
+# to run the tools when the tools aren't used as part of a spread test.
+# The steps are:
+# 1. source the file: `. /setup.sh`
+# 2. run any tool: `retry true`
+
+PROJECT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
+
+export PATH=$PATH:$PROJECT_DIR/tools:$PROJECT_DIR/utils:$PROJECT_DIR/remote
diff --git a/tests/lib/external/snapd-testing-tools/spread.yaml b/tests/lib/external/snapd-testing-tools/spread.yaml
new file mode 100644
index 0000000..c5435fc
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/spread.yaml
@@ -0,0 +1,90 @@
+project: snapd-testing-tools
+
+environment:
+ PROJECT_PATH: /root/snapd-testing-tools
+ PATH: $PATH:$PROJECT_PATH/tools:$PROJECT_PATH/utils:$PROJECT_PATH/remote
+
+backends:
+ google:
+ key: '$(HOST: echo "$SPREAD_GOOGLE_KEY")'
+ location: snapd-spread/us-east1-b
+ halt-timeout: 2h
+ systems:
+ - ubuntu-14.04-64:
+ - ubuntu-16.04-64:
+ - ubuntu-18.04-32:
+ - ubuntu-18.04-64:
+ - ubuntu-20.04-64:
+ - ubuntu-22.04-64:
+ - ubuntu-23.10-64:
+ - ubuntu-24.04-64:
+ - debian-11-64:
+ - debian-12-64:
+ - debian-sid-64:
+ - fedora-38-64:
+ - fedora-39-64:
+ - arch-linux-64:
+ - amazon-linux-2-64:
+ storage: preserve-size
+ - amazon-linux-2023-64:
+ storage: preserve-size
+ - centos-9-64:
+ storage: preserve-size
+ - opensuse-15.5-64:
+ - opensuse-tumbleweed-64:
+
+ google-nested:
+ type: google
+ key: '$(HOST: echo "$SPREAD_GOOGLE_KEY")'
+ location: snapd-spread/us-east1-b
+ plan: n2-standard-2
+ halt-timeout: 2h
+ cpu-family: "Intel Cascade Lake"
+ systems:
+ - ubuntu-20.04-64:
+ image: ubuntu-2004-64-virt-enabled
+ storage: 20G
+
+ openstack:
+ key: '$(HOST: echo "$SPREAD_OPENSTACK_ENV")'
+ plan: staging-cpu2-ram4-disk50
+ halt-timeout: 2h
+ groups: [default]
+ environment:
+ HTTP_PROXY: 'http://squid.internal:3128'
+ HTTPS_PROXY: 'http://squid.internal:3128'
+ NO_PROXY: 'canonical.com'
+ systems:
+ - ubuntu-20.04-64:
+ image: ubuntu-focal-20.04-amd64
+ - ubuntu-22.04-64:
+ image: ubuntu-jammy-22.04-amd64
+ - centos-9-64:
+ - debian-12-64:
+ - fedora-40-64:
+ - opensuse-15.5-64:
+
+path: /root/snapd-testing-tools
+
+kill-timeout: 10m
+warn-timeout: 3m
+
+prepare: |
+ echo "Prepare snapd-testing-tools project"
+
+restore: |
+ echo "Restore snapd-testing-tools project"
+ rm -rf "$PROJECT_PATH"
+
+suites:
+ tests/:
+ summary: Main test suite for snapd-testing-tools project
+ prepare: |
+ echo "Preparing snapd-testing-tools main suite"
+ # Create users for the tests
+ useradd -m tools-user-1
+ echo tools-user-1:tools-user-1 | sudo chpasswd
+ useradd -m tools-user-2
+ echo tools-user-2:tools-user-2 | sudo chpasswd
+ restore: |
+ echo "Restoring snapd-testing-tools main suite"
diff --git a/tests/lib/external/snapd-testing-tools/tests/check-test-format/task.yaml b/tests/lib/external/snapd-testing-tools/tests/check-test-format/task.yaml
new file mode 100644
index 0000000..c421e81
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/check-test-format/task.yaml
@@ -0,0 +1,46 @@
+summary: smoke test for the spread checker tool
+
+details: |
+ Verify that the check-test-format tool checks properly the desired
+ order and mandatory keys in spread tasks.
+
+backends: [google]
+
+systems: [ ubuntu-20.04-64 ]
+
+prepare: |
+ apt install -y python3-yamlordereddictloader
+
+restore: |
+ apt remove -y python3-yamlordereddictloader
+
+execute: |
+ check-test-format -h | MATCH "usage: check-test-format"
+
+ # Check failing tasks with order not desired
+ check-test-format --tests "$PWD/tasks/task1.yaml" 2>&1 | MATCH "Keys 'execute' and 'prepare' do not follow the desired order"
+
+ check-test-format --tests "$PWD/tasks/task2.yaml" 2>&1 | MATCH "Keys 'execute' and 'restore' do not follow the desired order"
+
+ check-test-format --tests "$PWD/tasks/task3.yaml" 2>&1 | MATCH "Keys 'systems' and 'backends' do not follow the desired order"
+ check-test-format --tests "$PWD/tasks/task3.yaml" 2>&1 | MATCH "Key 'execute' is mandatory"
+
+ # Check passing a file with more than 1 error
+ check-test-format --tests "$PWD/tasks/task4.yaml" 2>&1 | MATCH "Keys 'environment' and 'systems' do not follow the desired order"
+ check-test-format --tests "$PWD/tasks/task4.yaml" 2>&1 | MATCH "Key 'details' is mandatory"
+
+ # Check passing a file with a non supported section
+ check-test-format --tests "$PWD/tasks/task5.yaml" 2>&1 | MATCH "key 'unsupported' is not among the supported keys"
+
+ # Check passing more than 1 file to the tool
+ check-test-format --tests "$PWD/tasks/task4.yaml" "$PWD/tasks/task5.yaml" 2>&1 | MATCH "Key 'details' is mandatory"
+ check-test-format --tests "$PWD/tasks/task4.yaml" "$PWD/tasks/task5.yaml" 2>&1 | MATCH "key 'unsupported' is not among the supported keys"
+
+ # Check passing a file with a non supported format
+ # Check passing a dir
+ cp "$PWD/tasks/task6.yaml" "$PWD/tasks/task.yaml"
+ echo "newtext" >> "$PWD/tasks/task.yaml"
+ check-test-format --tests "$PWD/tasks/task.yaml" 2>&1 | MATCH "Invalid task format, checks failed for task"
+ check-test-format --dir "$PWD/tasks/" 2>&1 | MATCH "Invalid task format, checks failed for task"
+ check-test-format --dir "$PWD/tasks/" 2>&1 | NOMATCH "Key 'execute' is mandatory"
+ rm "$PWD/tasks/task.yaml"
diff --git a/tests/lib/external/snapd-testing-tools/tests/check-test-format/tasks/task1.yaml b/tests/lib/external/snapd-testing-tools/tests/check-test-format/tasks/task1.yaml
new file mode 100644
index 0000000..90ecfe6
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/check-test-format/tasks/task1.yaml
@@ -0,0 +1,9 @@
+summary: this is the summary
+
+details: details
+
+execute: |
+ echo "execute"
+
+prepare: |
+ echo "prepare"
diff --git a/tests/lib/external/snapd-testing-tools/tests/check-test-format/tasks/task2.yaml b/tests/lib/external/snapd-testing-tools/tests/check-test-format/tasks/task2.yaml
new file mode 100644
index 0000000..9e02ee7
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/check-test-format/tasks/task2.yaml
@@ -0,0 +1,9 @@
+summary: this is the summary
+
+details: details
+
+execute: |
+ echo "execute"
+
+restore: |
+ echo "restore"
diff --git a/tests/lib/external/snapd-testing-tools/tests/check-test-format/tasks/task3.yaml b/tests/lib/external/snapd-testing-tools/tests/check-test-format/tasks/task3.yaml
new file mode 100644
index 0000000..55989a1
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/check-test-format/tasks/task3.yaml
@@ -0,0 +1,7 @@
+summary: this is the summary
+
+details: details
+
+systems: [ ubuntu-20.04-64 ]
+
+backends: [ google ]
diff --git a/tests/lib/external/snapd-testing-tools/tests/check-test-format/tasks/task4.yaml b/tests/lib/external/snapd-testing-tools/tests/check-test-format/tasks/task4.yaml
new file mode 100644
index 0000000..c712c79
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/check-test-format/tasks/task4.yaml
@@ -0,0 +1,8 @@
+summary: this is the summary
+
+environment:
+ TEST: 1
+
+systems: [ ubuntu-20.04-64 ]
+
+
diff --git a/tests/lib/external/snapd-testing-tools/tests/check-test-format/tasks/task5.yaml b/tests/lib/external/snapd-testing-tools/tests/check-test-format/tasks/task5.yaml
new file mode 100644
index 0000000..ff45b06
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/check-test-format/tasks/task5.yaml
@@ -0,0 +1,10 @@
+summary: this is the summary
+
+details: details
+
+unsupported: not supported
+
+systems: [ ubuntu-20.04-64 ]
+
+execute: |
+ echo "execute"
diff --git a/tests/lib/external/snapd-testing-tools/tests/check-test-format/tasks/task6.yaml b/tests/lib/external/snapd-testing-tools/tests/check-test-format/tasks/task6.yaml
new file mode 100644
index 0000000..e42a409
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/check-test-format/tasks/task6.yaml
@@ -0,0 +1,6 @@
+summary: this is the summary
+
+details: details
+
+execute: |
+ echo "execute"
diff --git a/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/aborted-and-failed-execute-and-restore.json b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/aborted-and-failed-execute-and-restore.json
new file mode 100644
index 0000000..bba7f98
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/aborted-and-failed-execute-and-restore.json
@@ -0,0 +1,443 @@
+[
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:37:06",
+ "verb": "Allocating",
+ "task": null,
+ "extra": "google:ubuntu-22.04-64..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:37:06",
+ "verb": "Allocating",
+ "task": null,
+ "extra": "google:ubuntu-20.04-64..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:37:15",
+ "verb": "Waiting",
+ "task": null,
+ "extra": "for google:ubuntu-20.04-64 (may242137-942824) to boot at 34.75.199.115..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:37:20",
+ "verb": "Waiting",
+ "task": null,
+ "extra": "for google:ubuntu-22.04-64 (may242137-942720) to boot at 34.75.71.82..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:37:57",
+ "verb": "Allocated",
+ "task": null,
+ "extra": "google:ubuntu-22.04-64 (may242137-942720)."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:37:57",
+ "verb": "Connecting",
+ "task": null,
+ "extra": "to google:ubuntu-22.04-64 (may242137-942720)..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:37:58",
+ "verb": "Connected",
+ "task": null,
+ "extra": "to google:ubuntu-22.04-64 (may242137-942720) at 34.75.71.82."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:37:58",
+ "verb": "Sending",
+ "task": null,
+ "extra": "project content to google:ubuntu-22.04-64 (may242137-942720)..."
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:38:02",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64"
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:38:02",
+ "verb": "Allocated",
+ "task": null,
+ "extra": "google:ubuntu-20.04-64 (may242137-942824)."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:38:02",
+ "verb": "Connecting",
+ "task": null,
+ "extra": "to google:ubuntu-20.04-64 (may242137-942824)..."
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:38:02",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64:tests/"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:38:03",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64:tests/test-4"
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:38:04",
+ "verb": "Connected",
+ "task": null,
+ "extra": "to google:ubuntu-20.04-64 (may242137-942824) at 34.75.199.115."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:38:04",
+ "verb": "Sending",
+ "task": null,
+ "extra": "project content to google:ubuntu-20.04-64 (may242137-942824)..."
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:38:04",
+ "verb": "Executing",
+ "task": "google:ubuntu-22.04-64:tests/test-4"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:38:04",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64:tests/test-4"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:38:05",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64:tests/test-2"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:38:05",
+ "verb": "Executing",
+ "task": "google:ubuntu-22.04-64:tests/test-2"
+ },
+ {
+ "type": "info",
+ "date": "2022-05-24",
+ "time": "18:38:06",
+ "info_type": "Error",
+ "verb": "executing",
+ "task": "google:ubuntu-22.04-64:tests/test-2",
+ "extra": null,
+ "detail": {
+ "lines": [
+ "-----\n",
+ "+ echo execute\n",
+ "execute\n",
+ "+ exit 1\n",
+ "-----\n",
+ ".\n"
+ ]
+ }
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:38:06",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64:tests/test-2"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:38:06",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64"
+ },
+ {
+ "type": "info",
+ "date": "2022-05-24",
+ "time": "18:38:06",
+ "info_type": "Error",
+ "verb": "restoring",
+ "task": "google:ubuntu-22.04-64:tests/test-2",
+ "extra": null,
+ "detail": {
+ "lines": [
+ "-----\n",
+ "+ echo restore\n",
+ "restore\n",
+ "+ exit 1\n",
+ "-----\n",
+ ".\n"
+ ]
+ }
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:38:06",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64:tests/"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:38:07",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64:tests/"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:38:07",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:38:07",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64:tests/test-5"
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:38:07",
+ "verb": "Discarding",
+ "task": null,
+ "extra": "google:ubuntu-22.04-64 (may242137-942720)..."
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:38:08",
+ "verb": "Executing",
+ "task": "google:ubuntu-20.04-64:tests/test-5"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:38:08",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64:tests/test-5"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:38:09",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64:tests/test-3"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:38:09",
+ "verb": "Executing",
+ "task": "google:ubuntu-20.04-64:tests/test-3"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:38:10",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64:tests/test-3"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:38:10",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64:tests/test-1"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:38:11",
+ "verb": "Executing",
+ "task": "google:ubuntu-20.04-64:tests/test-1"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:38:11",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64:tests/test-1"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:38:12",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64:tests/test-4"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:38:12",
+ "verb": "Executing",
+ "task": "google:ubuntu-20.04-64:tests/test-4"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:38:13",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64:tests/test-4"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:38:13",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64:tests/test-2"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:38:14",
+ "verb": "Executing",
+ "task": "google:ubuntu-20.04-64:tests/test-2"
+ },
+ {
+ "type": "info",
+ "date": "2022-05-24",
+ "time": "18:38:14",
+ "info_type": "Error",
+ "verb": "executing",
+ "task": "google:ubuntu-20.04-64:tests/test-2",
+ "extra": null,
+ "detail": {
+ "lines": [
+ "-----\n",
+ "+ echo execute\n",
+ "execute\n",
+ "+ exit 1\n",
+ "-----\n",
+ ".\n"
+ ]
+ }
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:38:14",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64:tests/test-2"
+ },
+ {
+ "type": "info",
+ "date": "2022-05-24",
+ "time": "18:38:15",
+ "info_type": "Error",
+ "verb": "restoring",
+ "task": "google:ubuntu-20.04-64:tests/test-2",
+ "extra": null,
+ "detail": {
+ "lines": [
+ "-----\n",
+ "+ echo restore\n",
+ "restore\n",
+ "+ exit 1\n",
+ "-----\n",
+ ".\n"
+ ]
+ }
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:38:15",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64:tests/"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:38:15",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64"
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:38:16",
+ "verb": "Discarding",
+ "task": null,
+ "extra": "google:ubuntu-20.04-64 (may242137-942824)..."
+ },
+ {
+ "type": "result",
+ "date": "2022-05-24",
+ "time": "18:38:16",
+ "result_type": "Successful",
+ "level": "tasks",
+ "stage": null,
+ "number": "5",
+ "detail": null
+ },
+ {
+ "type": "result",
+ "date": "2022-05-24",
+ "time": "18:38:16",
+ "result_type": "Aborted",
+ "level": "tasks",
+ "stage": null,
+ "number": "3",
+ "detail": null
+ },
+ {
+ "type": "result",
+ "date": "2022-05-24",
+ "time": "18:38:16",
+ "result_type": "Failed",
+ "level": "tasks",
+ "stage": null,
+ "number": "2",
+ "detail": {
+ "lines": [
+ " - google:ubuntu-20.04-64:tests/test-2\n",
+ " - google:ubuntu-22.04-64:tests/test-2\n"
+ ]
+ }
+ },
+ {
+ "type": "result",
+ "date": "2022-05-24",
+ "time": "18:38:16",
+ "result_type": "Failed",
+ "level": "task",
+ "stage": "restore",
+ "number": "2",
+ "detail": {
+ "lines": [
+ " - google:ubuntu-20.04-64:tests/test-2\n",
+ " - google:ubuntu-22.04-64:tests/test-2\n"
+ ]
+ }
+ }
+]
\ No newline at end of file
diff --git a/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/aborted-and-failed-execute-and-restore.log b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/aborted-and-failed-execute-and-restore.log
new file mode 100644
index 0000000..93f003e
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/aborted-and-failed-execute-and-restore.log
@@ -0,0 +1,82 @@
+2022-05-24 18:37:06 Project content is packed for delivery (9.71KB).
+2022-05-24 18:37:06 Sequence of jobs produced with -seed=1653428226
+2022-05-24 18:37:06 If killed, discard servers with: spread -reuse-pid=101245 -discard
+2022-05-24 18:37:06 Allocating google:ubuntu-22.04-64...
+2022-05-24 18:37:06 Allocating google:ubuntu-20.04-64...
+2022-05-24 18:37:15 Waiting for google:ubuntu-20.04-64 (may242137-942824) to boot at 34.75.199.115...
+2022-05-24 18:37:20 Waiting for google:ubuntu-22.04-64 (may242137-942720) to boot at 34.75.71.82...
+2022-05-24 18:37:57 Allocated google:ubuntu-22.04-64 (may242137-942720).
+2022-05-24 18:37:57 Connecting to google:ubuntu-22.04-64 (may242137-942720)...
+2022-05-24 18:37:58 Connected to google:ubuntu-22.04-64 (may242137-942720) at 34.75.71.82.
+2022-05-24 18:37:58 Sending project content to google:ubuntu-22.04-64 (may242137-942720)...
+2022-05-24 18:38:02 Preparing google:ubuntu-22.04-64 (may242137-942720)...
+2022-05-24 18:38:02 Allocated google:ubuntu-20.04-64 (may242137-942824).
+2022-05-24 18:38:02 Connecting to google:ubuntu-20.04-64 (may242137-942824)...
+2022-05-24 18:38:02 Preparing google:ubuntu-22.04-64:tests/ (may242137-942720)...
+2022-05-24 18:38:03 Preparing google:ubuntu-22.04-64:tests/test-4 (may242137-942720)...
+2022-05-24 18:38:04 Connected to google:ubuntu-20.04-64 (may242137-942824) at 34.75.199.115.
+2022-05-24 18:38:04 Sending project content to google:ubuntu-20.04-64 (may242137-942824)...
+2022-05-24 18:38:04 Executing google:ubuntu-22.04-64:tests/test-4 (may242137-942720) (1/10)...
+2022-05-24 18:38:04 Restoring google:ubuntu-22.04-64:tests/test-4 (may242137-942720)...
+2022-05-24 18:38:05 Preparing google:ubuntu-22.04-64:tests/test-2 (may242137-942720)...
+2022-05-24 18:38:05 Executing google:ubuntu-22.04-64:tests/test-2 (may242137-942720) (2/10)...
+2022-05-24 18:38:06 Error executing google:ubuntu-22.04-64:tests/test-2 (may242137-942720) :
+-----
++ echo execute
+execute
++ exit 1
+-----
+.
+2022-05-24 18:38:06 Restoring google:ubuntu-22.04-64:tests/test-2 (may242137-942720)...
+2022-05-24 18:38:06 Preparing google:ubuntu-20.04-64 (may242137-942824)...
+2022-05-24 18:38:06 Error restoring google:ubuntu-22.04-64:tests/test-2 (may242137-942720) :
+-----
++ echo restore
+restore
++ exit 1
+-----
+.
+2022-05-24 18:38:06 Restoring google:ubuntu-22.04-64:tests/ (may242137-942720)...
+2022-05-24 18:38:07 Preparing google:ubuntu-20.04-64:tests/ (may242137-942824)...
+2022-05-24 18:38:07 Restoring google:ubuntu-22.04-64 (may242137-942720)...
+2022-05-24 18:38:07 Preparing google:ubuntu-20.04-64:tests/test-5 (may242137-942824)...
+2022-05-24 18:38:07 Discarding google:ubuntu-22.04-64 (may242137-942720)...
+2022-05-24 18:38:08 Executing google:ubuntu-20.04-64:tests/test-5 (may242137-942824) (3/10)...
+2022-05-24 18:38:08 Restoring google:ubuntu-20.04-64:tests/test-5 (may242137-942824)...
+2022-05-24 18:38:09 Preparing google:ubuntu-20.04-64:tests/test-3 (may242137-942824)...
+2022-05-24 18:38:09 Executing google:ubuntu-20.04-64:tests/test-3 (may242137-942824) (4/10)...
+2022-05-24 18:38:10 Restoring google:ubuntu-20.04-64:tests/test-3 (may242137-942824)...
+2022-05-24 18:38:10 Preparing google:ubuntu-20.04-64:tests/test-1 (may242137-942824)...
+2022-05-24 18:38:11 Executing google:ubuntu-20.04-64:tests/test-1 (may242137-942824) (5/10)...
+2022-05-24 18:38:11 Restoring google:ubuntu-20.04-64:tests/test-1 (may242137-942824)...
+2022-05-24 18:38:12 Preparing google:ubuntu-20.04-64:tests/test-4 (may242137-942824)...
+2022-05-24 18:38:12 Executing google:ubuntu-20.04-64:tests/test-4 (may242137-942824) (6/10)...
+2022-05-24 18:38:13 Restoring google:ubuntu-20.04-64:tests/test-4 (may242137-942824)...
+2022-05-24 18:38:13 Preparing google:ubuntu-20.04-64:tests/test-2 (may242137-942824)...
+2022-05-24 18:38:14 Executing google:ubuntu-20.04-64:tests/test-2 (may242137-942824) (7/10)...
+2022-05-24 18:38:14 Error executing google:ubuntu-20.04-64:tests/test-2 (may242137-942824) :
+-----
++ echo execute
+execute
++ exit 1
+-----
+.
+2022-05-24 18:38:14 Restoring google:ubuntu-20.04-64:tests/test-2 (may242137-942824)...
+2022-05-24 18:38:15 Error restoring google:ubuntu-20.04-64:tests/test-2 (may242137-942824) :
+-----
++ echo restore
+restore
++ exit 1
+-----
+.
+2022-05-24 18:38:15 Restoring google:ubuntu-20.04-64:tests/ (may242137-942824)...
+2022-05-24 18:38:15 Restoring google:ubuntu-20.04-64 (may242137-942824)...
+2022-05-24 18:38:16 Discarding google:ubuntu-20.04-64 (may242137-942824)...
+2022-05-24 18:38:16 Successful tasks: 5
+2022-05-24 18:38:16 Aborted tasks: 3
+2022-05-24 18:38:16 Failed tasks: 2
+ - google:ubuntu-20.04-64:tests/test-2
+ - google:ubuntu-22.04-64:tests/test-2
+2022-05-24 18:38:16 Failed task restore: 2
+ - google:ubuntu-20.04-64:tests/test-2
+ - google:ubuntu-22.04-64:tests/test-2
diff --git a/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/all-aborted.json b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/all-aborted.json
new file mode 100644
index 0000000..75d7954
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/all-aborted.json
@@ -0,0 +1,215 @@
+[
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:06:40",
+ "verb": "Allocating",
+ "task": null,
+ "extra": "google:ubuntu-22.04-64..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:06:40",
+ "verb": "Allocating",
+ "task": null,
+ "extra": "google:ubuntu-20.04-64..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:06:49",
+ "verb": "Waiting",
+ "task": null,
+ "extra": "for google:ubuntu-22.04-64 (may242106-435572) to boot at 34.148.101.55..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:06:50",
+ "verb": "Waiting",
+ "task": null,
+ "extra": "for google:ubuntu-20.04-64 (may242106-435744) to boot at 34.148.4.171..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:07:30",
+ "verb": "Allocated",
+ "task": null,
+ "extra": "google:ubuntu-22.04-64 (may242106-435572)."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:07:30",
+ "verb": "Connecting",
+ "task": null,
+ "extra": "to google:ubuntu-22.04-64 (may242106-435572)..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:07:32",
+ "verb": "Connected",
+ "task": null,
+ "extra": "to google:ubuntu-22.04-64 (may242106-435572) at 34.148.101.55."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:07:32",
+ "verb": "Sending",
+ "task": null,
+ "extra": "project content to google:ubuntu-22.04-64 (may242106-435572)..."
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:07:36",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64"
+ },
+ {
+ "type": "info",
+ "date": "2022-05-24",
+ "time": "18:07:37",
+ "info_type": "Error",
+ "verb": "preparing",
+ "task": "google:ubuntu-22.04-64",
+ "extra": null,
+ "detail": {
+ "lines": [
+ "-----\n",
+ "+ echo 'Prepare log-analyzer-reexec project'\n",
+ "Prepare log-analyzer-reexec project\n",
+ "+ exit 1\n",
+ "-----\n",
+ ".\n"
+ ]
+ }
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:07:37",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64"
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:07:37",
+ "verb": "Discarding",
+ "task": null,
+ "extra": "google:ubuntu-22.04-64 (may242106-435572)..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:07:41",
+ "verb": "Allocated",
+ "task": null,
+ "extra": "google:ubuntu-20.04-64 (may242106-435744)."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:07:41",
+ "verb": "Connecting",
+ "task": null,
+ "extra": "to google:ubuntu-20.04-64 (may242106-435744)..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:07:42",
+ "verb": "Connected",
+ "task": null,
+ "extra": "to google:ubuntu-20.04-64 (may242106-435744) at 34.148.4.171."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:07:42",
+ "verb": "Sending",
+ "task": null,
+ "extra": "project content to google:ubuntu-20.04-64 (may242106-435744)..."
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:07:46",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64"
+ },
+ {
+ "type": "info",
+ "date": "2022-05-24",
+ "time": "18:07:47",
+ "info_type": "Error",
+ "verb": "preparing",
+ "task": "google:ubuntu-20.04-64",
+ "extra": null,
+ "detail": {
+ "lines": [
+ "-----\n",
+ "+ echo 'Prepare log-analyzer-reexec project'\n",
+ "Prepare log-analyzer-reexec project\n",
+ "+ exit 1\n",
+ "-----\n",
+ ".\n"
+ ]
+ }
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:07:47",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64"
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:07:47",
+ "verb": "Discarding",
+ "task": null,
+ "extra": "google:ubuntu-20.04-64 (may242106-435744)..."
+ },
+ {
+ "type": "result",
+ "date": "2022-05-24",
+ "time": "18:07:48",
+ "result_type": "Successful",
+ "level": "tasks",
+ "stage": null,
+ "number": "0",
+ "detail": null
+ },
+ {
+ "type": "result",
+ "date": "2022-05-24",
+ "time": "18:07:48",
+ "result_type": "Aborted",
+ "level": "tasks",
+ "stage": null,
+ "number": "10",
+ "detail": null
+ },
+ {
+ "type": "result",
+ "date": "2022-05-24",
+ "time": "18:07:48",
+ "result_type": "Failed",
+ "level": "project",
+ "stage": "prepare",
+ "number": "2",
+ "detail": {
+ "lines": [
+ " - google:ubuntu-20.04-64:project\n",
+ " - google:ubuntu-22.04-64:project\n"
+ ]
+ }
+ }
+]
\ No newline at end of file
diff --git a/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/all-aborted.log b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/all-aborted.log
new file mode 100644
index 0000000..aa284e9
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/all-aborted.log
@@ -0,0 +1,40 @@
+2022-05-24 18:06:40 Project content is packed for delivery (5.27KB).
+2022-05-24 18:06:40 Sequence of jobs produced with -seed=1653426400
+2022-05-24 18:06:40 If killed, discard servers with: spread -reuse-pid=98654 -discard
+2022-05-24 18:06:40 Allocating google:ubuntu-22.04-64...
+2022-05-24 18:06:40 Allocating google:ubuntu-20.04-64...
+2022-05-24 18:06:49 Waiting for google:ubuntu-22.04-64 (may242106-435572) to boot at 34.148.101.55...
+2022-05-24 18:06:50 Waiting for google:ubuntu-20.04-64 (may242106-435744) to boot at 34.148.4.171...
+2022-05-24 18:07:30 Allocated google:ubuntu-22.04-64 (may242106-435572).
+2022-05-24 18:07:30 Connecting to google:ubuntu-22.04-64 (may242106-435572)...
+2022-05-24 18:07:32 Connected to google:ubuntu-22.04-64 (may242106-435572) at 34.148.101.55.
+2022-05-24 18:07:32 Sending project content to google:ubuntu-22.04-64 (may242106-435572)...
+2022-05-24 18:07:36 Preparing google:ubuntu-22.04-64 (may242106-435572)...
+2022-05-24 18:07:37 Error preparing google:ubuntu-22.04-64 (may242106-435572) :
+-----
++ echo 'Prepare log-analyzer-reexec project'
+Prepare log-analyzer-reexec project
++ exit 1
+-----
+.
+2022-05-24 18:07:37 Restoring google:ubuntu-22.04-64 (may242106-435572)...
+2022-05-24 18:07:37 Discarding google:ubuntu-22.04-64 (may242106-435572)...
+2022-05-24 18:07:41 Allocated google:ubuntu-20.04-64 (may242106-435744).
+2022-05-24 18:07:41 Connecting to google:ubuntu-20.04-64 (may242106-435744)...
+2022-05-24 18:07:42 Connected to google:ubuntu-20.04-64 (may242106-435744) at 34.148.4.171.
+2022-05-24 18:07:42 Sending project content to google:ubuntu-20.04-64 (may242106-435744)...
+2022-05-24 18:07:46 Preparing google:ubuntu-20.04-64 (may242106-435744)...
+2022-05-24 18:07:47 Error preparing google:ubuntu-20.04-64 (may242106-435744) :
+-----
++ echo 'Prepare log-analyzer-reexec project'
+Prepare log-analyzer-reexec project
++ exit 1
+-----
+.
+2022-05-24 18:07:47 Restoring google:ubuntu-20.04-64 (may242106-435744)...
+2022-05-24 18:07:47 Discarding google:ubuntu-20.04-64 (may242106-435744)...
+2022-05-24 18:07:48 Successful tasks: 0
+2022-05-24 18:07:48 Aborted tasks: 10
+2022-05-24 18:07:48 Failed project prepare: 2
+ - google:ubuntu-20.04-64:project
+ - google:ubuntu-22.04-64:project
diff --git a/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/all-failed.json b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/all-failed.json
new file mode 100644
index 0000000..705011a
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/all-failed.json
@@ -0,0 +1,613 @@
+[
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:52:29",
+ "verb": "Allocating",
+ "task": null,
+ "extra": "google:ubuntu-22.04-64..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:52:29",
+ "verb": "Allocating",
+ "task": null,
+ "extra": "google:ubuntu-20.04-64..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:52:39",
+ "verb": "Waiting",
+ "task": null,
+ "extra": "for google:ubuntu-20.04-64 (may242052-202754) to boot at 34.139.205.254..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:52:43",
+ "verb": "Waiting",
+ "task": null,
+ "extra": "for google:ubuntu-22.04-64 (may242052-202652) to boot at 104.196.209.131..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:53:24",
+ "verb": "Allocated",
+ "task": null,
+ "extra": "google:ubuntu-22.04-64 (may242052-202652)."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:53:24",
+ "verb": "Connecting",
+ "task": null,
+ "extra": "to google:ubuntu-22.04-64 (may242052-202652)..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:53:25",
+ "verb": "Allocated",
+ "task": null,
+ "extra": "google:ubuntu-20.04-64 (may242052-202754)."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:53:25",
+ "verb": "Connecting",
+ "task": null,
+ "extra": "to google:ubuntu-20.04-64 (may242052-202754)..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:53:26",
+ "verb": "Connected",
+ "task": null,
+ "extra": "to google:ubuntu-22.04-64 (may242052-202652) at 104.196.209.131."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:53:26",
+ "verb": "Sending",
+ "task": null,
+ "extra": "project content to google:ubuntu-22.04-64 (may242052-202652)..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:53:26",
+ "verb": "Connected",
+ "task": null,
+ "extra": "to google:ubuntu-20.04-64 (may242052-202754) at 34.139.205.254."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:53:26",
+ "verb": "Sending",
+ "task": null,
+ "extra": "project content to google:ubuntu-20.04-64 (may242052-202754)..."
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:53:28",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:53:29",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64:tests/"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:53:29",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:53:29",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64:tests/test-3"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:53:30",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64:tests/"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:53:30",
+ "verb": "Executing",
+ "task": "google:ubuntu-22.04-64:tests/test-3"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:53:30",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64:tests/test-5"
+ },
+ {
+ "type": "info",
+ "date": "2022-05-24",
+ "time": "17:53:31",
+ "info_type": "Error",
+ "verb": "executing",
+ "task": "google:ubuntu-22.04-64:tests/test-3",
+ "extra": null,
+ "detail": {
+ "lines": [
+ "-----\n",
+ "+ echo execute\n",
+ "execute\n",
+ "+ exit 1\n",
+ "-----\n",
+ ".\n"
+ ]
+ }
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:53:31",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64:tests/test-3"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:53:31",
+ "verb": "Executing",
+ "task": "google:ubuntu-20.04-64:tests/test-5"
+ },
+ {
+ "type": "info",
+ "date": "2022-05-24",
+ "time": "17:53:31",
+ "info_type": "Error",
+ "verb": "executing",
+ "task": "google:ubuntu-20.04-64:tests/test-5",
+ "extra": null,
+ "detail": {
+ "lines": [
+ "-----\n",
+ "+ echo execute\n",
+ "execute\n",
+ "+ exit 1\n",
+ "-----\n",
+ ".\n"
+ ]
+ }
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:53:31",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64:tests/test-5"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:53:31",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64:tests/test-4"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:53:32",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64:tests/test-2"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:53:32",
+ "verb": "Executing",
+ "task": "google:ubuntu-20.04-64:tests/test-2"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:53:32",
+ "verb": "Executing",
+ "task": "google:ubuntu-22.04-64:tests/test-4"
+ },
+ {
+ "type": "info",
+ "date": "2022-05-24",
+ "time": "17:53:33",
+ "info_type": "Error",
+ "verb": "executing",
+ "task": "google:ubuntu-20.04-64:tests/test-2",
+ "extra": null,
+ "detail": {
+ "lines": [
+ "-----\n",
+ "+ echo execute\n",
+ "execute\n",
+ "+ exit 1\n",
+ "-----\n",
+ ".\n"
+ ]
+ }
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:53:33",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64:tests/test-2"
+ },
+ {
+ "type": "info",
+ "date": "2022-05-24",
+ "time": "17:53:33",
+ "info_type": "Error",
+ "verb": "executing",
+ "task": "google:ubuntu-22.04-64:tests/test-4",
+ "extra": null,
+ "detail": {
+ "lines": [
+ "-----\n",
+ "+ echo execute\n",
+ "execute\n",
+ "+ exit 1\n",
+ "-----\n",
+ ".\n"
+ ]
+ }
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:53:33",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64:tests/test-4"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:53:33",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64:tests/test-3"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:53:33",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64:tests/test-2"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:53:34",
+ "verb": "Executing",
+ "task": "google:ubuntu-20.04-64:tests/test-3"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:53:34",
+ "verb": "Executing",
+ "task": "google:ubuntu-22.04-64:tests/test-2"
+ },
+ {
+ "type": "info",
+ "date": "2022-05-24",
+ "time": "17:53:34",
+ "info_type": "Error",
+ "verb": "executing",
+ "task": "google:ubuntu-20.04-64:tests/test-3",
+ "extra": null,
+ "detail": {
+ "lines": [
+ "-----\n",
+ "+ echo execute\n",
+ "execute\n",
+ "+ exit 1\n",
+ "-----\n",
+ ".\n"
+ ]
+ }
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:53:34",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64:tests/test-3"
+ },
+ {
+ "type": "info",
+ "date": "2022-05-24",
+ "time": "17:53:35",
+ "info_type": "Error",
+ "verb": "executing",
+ "task": "google:ubuntu-22.04-64:tests/test-2",
+ "extra": null,
+ "detail": {
+ "lines": [
+ "-----\n",
+ "+ echo execute\n",
+ "execute\n",
+ "+ exit 1\n",
+ "-----\n",
+ ".\n"
+ ]
+ }
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:53:35",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64:tests/test-2"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:53:35",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64:tests/test-4"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:53:35",
+ "verb": "Executing",
+ "task": "google:ubuntu-20.04-64:tests/test-4"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:53:35",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64:tests/test-1"
+ },
+ {
+ "type": "info",
+ "date": "2022-05-24",
+ "time": "17:53:36",
+ "info_type": "Error",
+ "verb": "executing",
+ "task": "google:ubuntu-20.04-64:tests/test-4",
+ "extra": null,
+ "detail": {
+ "lines": [
+ "-----\n",
+ "+ echo execute\n",
+ "execute\n",
+ "+ exit 1\n",
+ "-----\n",
+ ".\n"
+ ]
+ }
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:53:36",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64:tests/test-4"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:53:36",
+ "verb": "Executing",
+ "task": "google:ubuntu-22.04-64:tests/test-1"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:53:36",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64:tests/test-1"
+ },
+ {
+ "type": "info",
+ "date": "2022-05-24",
+ "time": "17:53:36",
+ "info_type": "Error",
+ "verb": "executing",
+ "task": "google:ubuntu-22.04-64:tests/test-1",
+ "extra": null,
+ "detail": {
+ "lines": [
+ "-----\n",
+ "+ echo execute\n",
+ "execute\n",
+ "+ exit 1\n",
+ "-----\n",
+ ".\n"
+ ]
+ }
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:53:36",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64:tests/test-1"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:53:37",
+ "verb": "Executing",
+ "task": "google:ubuntu-20.04-64:tests/test-1"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:53:37",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64:tests/test-5"
+ },
+ {
+ "type": "info",
+ "date": "2022-05-24",
+ "time": "17:53:37",
+ "info_type": "Error",
+ "verb": "executing",
+ "task": "google:ubuntu-20.04-64:tests/test-1",
+ "extra": null,
+ "detail": {
+ "lines": [
+ "-----\n",
+ "+ echo execute\n",
+ "execute\n",
+ "+ exit 1\n",
+ "-----\n",
+ ".\n"
+ ]
+ }
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:53:37",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64:tests/test-1"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:53:38",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64:tests/"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:53:38",
+ "verb": "Executing",
+ "task": "google:ubuntu-22.04-64:tests/test-5"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:53:38",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64"
+ },
+ {
+ "type": "info",
+ "date": "2022-05-24",
+ "time": "17:53:38",
+ "info_type": "Error",
+ "verb": "executing",
+ "task": "google:ubuntu-22.04-64:tests/test-5",
+ "extra": null,
+ "detail": {
+ "lines": [
+ "-----\n",
+ "+ echo execute\n",
+ "execute\n",
+ "+ exit 1\n",
+ "-----\n",
+ ".\n"
+ ]
+ }
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:53:38",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64:tests/test-5"
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:53:39",
+ "verb": "Discarding",
+ "task": null,
+ "extra": "google:ubuntu-20.04-64 (may242052-202754)..."
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:53:39",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64:tests/"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:53:40",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64"
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:53:41",
+ "verb": "Discarding",
+ "task": null,
+ "extra": "google:ubuntu-22.04-64 (may242052-202652)..."
+ },
+ {
+ "type": "result",
+ "date": "2022-05-24",
+ "time": "17:53:42",
+ "result_type": "Successful",
+ "level": "tasks",
+ "stage": null,
+ "number": "0",
+ "detail": null
+ },
+ {
+ "type": "result",
+ "date": "2022-05-24",
+ "time": "17:53:42",
+ "result_type": "Aborted",
+ "level": "tasks",
+ "stage": null,
+ "number": "0",
+ "detail": null
+ },
+ {
+ "type": "result",
+ "date": "2022-05-24",
+ "time": "17:53:42",
+ "result_type": "Failed",
+ "level": "tasks",
+ "stage": null,
+ "number": "10",
+ "detail": {
+ "lines": [
+ " - google:ubuntu-20.04-64:tests/test-1\n",
+ " - google:ubuntu-20.04-64:tests/test-2\n",
+ " - google:ubuntu-20.04-64:tests/test-3\n",
+ " - google:ubuntu-20.04-64:tests/test-4\n",
+ " - google:ubuntu-20.04-64:tests/test-5\n",
+ " - google:ubuntu-22.04-64:tests/test-1\n",
+ " - google:ubuntu-22.04-64:tests/test-2\n",
+ " - google:ubuntu-22.04-64:tests/test-3\n",
+ " - google:ubuntu-22.04-64:tests/test-4\n",
+ " - google:ubuntu-22.04-64:tests/test-5\n"
+ ]
+ }
+ }
+]
\ No newline at end of file
diff --git a/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/all-failed.log b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/all-failed.log
new file mode 100644
index 0000000..1003f4d
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/all-failed.log
@@ -0,0 +1,138 @@
+2022-05-24 17:52:29 Project content is packed for delivery (2.87KB).
+2022-05-24 17:52:29 Sequence of jobs produced with -seed=1653425549
+2022-05-24 17:52:29 If killed, discard servers with: spread -reuse-pid=97426 -discard
+2022-05-24 17:52:29 Allocating google:ubuntu-22.04-64...
+2022-05-24 17:52:29 Allocating google:ubuntu-20.04-64...
+2022-05-24 17:52:39 Waiting for google:ubuntu-20.04-64 (may242052-202754) to boot at 34.139.205.254...
+2022-05-24 17:52:43 Waiting for google:ubuntu-22.04-64 (may242052-202652) to boot at 104.196.209.131...
+2022-05-24 17:53:24 Allocated google:ubuntu-22.04-64 (may242052-202652).
+2022-05-24 17:53:24 Connecting to google:ubuntu-22.04-64 (may242052-202652)...
+2022-05-24 17:53:25 Allocated google:ubuntu-20.04-64 (may242052-202754).
+2022-05-24 17:53:25 Connecting to google:ubuntu-20.04-64 (may242052-202754)...
+2022-05-24 17:53:26 Connected to google:ubuntu-22.04-64 (may242052-202652) at 104.196.209.131.
+2022-05-24 17:53:26 Sending project content to google:ubuntu-22.04-64 (may242052-202652)...
+2022-05-24 17:53:26 Connected to google:ubuntu-20.04-64 (may242052-202754) at 34.139.205.254.
+2022-05-24 17:53:26 Sending project content to google:ubuntu-20.04-64 (may242052-202754)...
+2022-05-24 17:53:28 Preparing google:ubuntu-22.04-64 (may242052-202652)...
+2022-05-24 17:53:29 Preparing google:ubuntu-22.04-64:tests/ (may242052-202652)...
+2022-05-24 17:53:29 Preparing google:ubuntu-20.04-64 (may242052-202754)...
+2022-05-24 17:53:29 Preparing google:ubuntu-22.04-64:tests/test-3 (may242052-202652)...
+2022-05-24 17:53:30 Preparing google:ubuntu-20.04-64:tests/ (may242052-202754)...
+2022-05-24 17:53:30 Executing google:ubuntu-22.04-64:tests/test-3 (may242052-202652) (1/10)...
+2022-05-24 17:53:30 Preparing google:ubuntu-20.04-64:tests/test-5 (may242052-202754)...
+2022-05-24 17:53:31 Error executing google:ubuntu-22.04-64:tests/test-3 (may242052-202652) :
+-----
++ echo execute
+execute
++ exit 1
+-----
+.
+2022-05-24 17:53:31 Restoring google:ubuntu-22.04-64:tests/test-3 (may242052-202652)...
+2022-05-24 17:53:31 Executing google:ubuntu-20.04-64:tests/test-5 (may242052-202754) (2/10)...
+2022-05-24 17:53:31 Error executing google:ubuntu-20.04-64:tests/test-5 (may242052-202754) :
+-----
++ echo execute
+execute
++ exit 1
+-----
+.
+2022-05-24 17:53:31 Restoring google:ubuntu-20.04-64:tests/test-5 (may242052-202754)...
+2022-05-24 17:53:31 Preparing google:ubuntu-22.04-64:tests/test-4 (may242052-202652)...
+2022-05-24 17:53:32 Preparing google:ubuntu-20.04-64:tests/test-2 (may242052-202754)...
+2022-05-24 17:53:32 Executing google:ubuntu-20.04-64:tests/test-2 (may242052-202754) (3/10)...
+2022-05-24 17:53:32 Executing google:ubuntu-22.04-64:tests/test-4 (may242052-202652) (4/10)...
+2022-05-24 17:53:33 Error executing google:ubuntu-20.04-64:tests/test-2 (may242052-202754) :
+-----
++ echo execute
+execute
++ exit 1
+-----
+.
+2022-05-24 17:53:33 Restoring google:ubuntu-20.04-64:tests/test-2 (may242052-202754)...
+2022-05-24 17:53:33 Error executing google:ubuntu-22.04-64:tests/test-4 (may242052-202652) :
+-----
++ echo execute
+execute
++ exit 1
+-----
+.
+2022-05-24 17:53:33 Restoring google:ubuntu-22.04-64:tests/test-4 (may242052-202652)...
+2022-05-24 17:53:33 Preparing google:ubuntu-20.04-64:tests/test-3 (may242052-202754)...
+2022-05-24 17:53:33 Preparing google:ubuntu-22.04-64:tests/test-2 (may242052-202652)...
+2022-05-24 17:53:34 Executing google:ubuntu-20.04-64:tests/test-3 (may242052-202754) (5/10)...
+2022-05-24 17:53:34 Executing google:ubuntu-22.04-64:tests/test-2 (may242052-202652) (6/10)...
+2022-05-24 17:53:34 Error executing google:ubuntu-20.04-64:tests/test-3 (may242052-202754) :
+-----
++ echo execute
+execute
++ exit 1
+-----
+.
+2022-05-24 17:53:34 Restoring google:ubuntu-20.04-64:tests/test-3 (may242052-202754)...
+2022-05-24 17:53:35 Error executing google:ubuntu-22.04-64:tests/test-2 (may242052-202652) :
+-----
++ echo execute
+execute
++ exit 1
+-----
+.
+2022-05-24 17:53:35 Restoring google:ubuntu-22.04-64:tests/test-2 (may242052-202652)...
+2022-05-24 17:53:35 Preparing google:ubuntu-20.04-64:tests/test-4 (may242052-202754)...
+2022-05-24 17:53:35 Executing google:ubuntu-20.04-64:tests/test-4 (may242052-202754) (7/10)...
+2022-05-24 17:53:35 Preparing google:ubuntu-22.04-64:tests/test-1 (may242052-202652)...
+2022-05-24 17:53:36 Error executing google:ubuntu-20.04-64:tests/test-4 (may242052-202754) :
+-----
++ echo execute
+execute
++ exit 1
+-----
+.
+2022-05-24 17:53:36 Restoring google:ubuntu-20.04-64:tests/test-4 (may242052-202754)...
+2022-05-24 17:53:36 Executing google:ubuntu-22.04-64:tests/test-1 (may242052-202652) (8/10)...
+2022-05-24 17:53:36 Preparing google:ubuntu-20.04-64:tests/test-1 (may242052-202754)...
+2022-05-24 17:53:36 Error executing google:ubuntu-22.04-64:tests/test-1 (may242052-202652) :
+-----
++ echo execute
+execute
++ exit 1
+-----
+.
+2022-05-24 17:53:36 Restoring google:ubuntu-22.04-64:tests/test-1 (may242052-202652)...
+2022-05-24 17:53:37 Executing google:ubuntu-20.04-64:tests/test-1 (may242052-202754) (9/10)...
+2022-05-24 17:53:37 Preparing google:ubuntu-22.04-64:tests/test-5 (may242052-202652)...
+2022-05-24 17:53:37 Error executing google:ubuntu-20.04-64:tests/test-1 (may242052-202754) :
+-----
++ echo execute
+execute
++ exit 1
+-----
+.
+2022-05-24 17:53:37 Restoring google:ubuntu-20.04-64:tests/test-1 (may242052-202754)...
+2022-05-24 17:53:38 Restoring google:ubuntu-20.04-64:tests/ (may242052-202754)...
+2022-05-24 17:53:38 Executing google:ubuntu-22.04-64:tests/test-5 (may242052-202652) (10/10)...
+2022-05-24 17:53:38 Restoring google:ubuntu-20.04-64 (may242052-202754)...
+2022-05-24 17:53:38 Error executing google:ubuntu-22.04-64:tests/test-5 (may242052-202652) :
+-----
++ echo execute
+execute
++ exit 1
+-----
+.
+2022-05-24 17:53:38 Restoring google:ubuntu-22.04-64:tests/test-5 (may242052-202652)...
+2022-05-24 17:53:39 Discarding google:ubuntu-20.04-64 (may242052-202754)...
+2022-05-24 17:53:39 Restoring google:ubuntu-22.04-64:tests/ (may242052-202652)...
+2022-05-24 17:53:40 Restoring google:ubuntu-22.04-64 (may242052-202652)...
+2022-05-24 17:53:41 Discarding google:ubuntu-22.04-64 (may242052-202652)...
+2022-05-24 17:53:42 Successful tasks: 0
+2022-05-24 17:53:42 Aborted tasks: 0
+2022-05-24 17:53:42 Failed tasks: 10
+ - google:ubuntu-20.04-64:tests/test-1
+ - google:ubuntu-20.04-64:tests/test-2
+ - google:ubuntu-20.04-64:tests/test-3
+ - google:ubuntu-20.04-64:tests/test-4
+ - google:ubuntu-20.04-64:tests/test-5
+ - google:ubuntu-22.04-64:tests/test-1
+ - google:ubuntu-22.04-64:tests/test-2
+ - google:ubuntu-22.04-64:tests/test-3
+ - google:ubuntu-22.04-64:tests/test-4
+ - google:ubuntu-22.04-64:tests/test-5
diff --git a/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/all-success-failed-restore.json b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/all-success-failed-restore.json
new file mode 100644
index 0000000..f3b1054
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/all-success-failed-restore.json
@@ -0,0 +1,453 @@
+[
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:42:58",
+ "verb": "Allocating",
+ "task": null,
+ "extra": "google:ubuntu-22.04-64..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:42:58",
+ "verb": "Allocating",
+ "task": null,
+ "extra": "google:ubuntu-20.04-64..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:43:07",
+ "verb": "Waiting",
+ "task": null,
+ "extra": "for google:ubuntu-22.04-64 (may242043-312411) to boot at 34.148.59.204..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:43:07",
+ "verb": "Waiting",
+ "task": null,
+ "extra": "for google:ubuntu-20.04-64 (may242043-312500) to boot at 34.138.215.109..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:43:43",
+ "verb": "Allocated",
+ "task": null,
+ "extra": "google:ubuntu-22.04-64 (may242043-312411)."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:43:43",
+ "verb": "Connecting",
+ "task": null,
+ "extra": "to google:ubuntu-22.04-64 (may242043-312411)..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:43:45",
+ "verb": "Connected",
+ "task": null,
+ "extra": "to google:ubuntu-22.04-64 (may242043-312411) at 34.148.59.204."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:43:45",
+ "verb": "Sending",
+ "task": null,
+ "extra": "project content to google:ubuntu-22.04-64 (may242043-312411)..."
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:43:50",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:43:50",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64:tests/"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:43:51",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64:tests/test-3"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:43:52",
+ "verb": "Executing",
+ "task": "google:ubuntu-22.04-64:tests/test-3"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:43:52",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64:tests/test-3"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:43:53",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64:tests/test-2"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:43:54",
+ "verb": "Executing",
+ "task": "google:ubuntu-22.04-64:tests/test-2"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:43:55",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64:tests/test-2"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:43:55",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64:tests/test-4"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:43:56",
+ "verb": "Executing",
+ "task": "google:ubuntu-22.04-64:tests/test-4"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:43:56",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64:tests/test-4"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:43:57",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64:tests/test-1"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:43:58",
+ "verb": "Executing",
+ "task": "google:ubuntu-22.04-64:tests/test-1"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:43:58",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64:tests/test-1"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:43:59",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64:tests/test-5"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:44:00",
+ "verb": "Executing",
+ "task": "google:ubuntu-22.04-64:tests/test-5"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:44:00",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64:tests/test-5"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:44:01",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64:tests/"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:44:02",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64"
+ },
+ {
+ "type": "info",
+ "date": "2022-05-24",
+ "time": "17:44:02",
+ "info_type": "Error",
+ "verb": "restoring",
+ "task": "google:ubuntu-22.04-64",
+ "extra": null,
+ "detail": {
+ "lines": [
+ "-----\n",
+ "+ echo 'Restore snapd-testing-tools project'\n",
+ "Restore snapd-testing-tools project\n",
+ "/bin/bash: line 26: PROJECT_PATH: unbound variable\n",
+ "-----\n",
+ ".\n"
+ ]
+ }
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:44:02",
+ "verb": "Discarding",
+ "task": null,
+ "extra": "google:ubuntu-22.04-64 (may242043-312411)..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:44:04",
+ "verb": "Allocated",
+ "task": null,
+ "extra": "google:ubuntu-20.04-64 (may242043-312500)."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:44:04",
+ "verb": "Connecting",
+ "task": null,
+ "extra": "to google:ubuntu-20.04-64 (may242043-312500)..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:44:05",
+ "verb": "Connected",
+ "task": null,
+ "extra": "to google:ubuntu-20.04-64 (may242043-312500) at 34.138.215.109."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:44:05",
+ "verb": "Sending",
+ "task": null,
+ "extra": "project content to google:ubuntu-20.04-64 (may242043-312500)..."
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:44:08",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:44:08",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64:tests/"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:44:09",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64:tests/test-3"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:44:09",
+ "verb": "Executing",
+ "task": "google:ubuntu-20.04-64:tests/test-3"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:44:10",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64:tests/test-3"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:44:10",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64:tests/test-1"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:44:11",
+ "verb": "Executing",
+ "task": "google:ubuntu-20.04-64:tests/test-1"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:44:11",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64:tests/test-1"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:44:12",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64:tests/test-4"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:44:13",
+ "verb": "Executing",
+ "task": "google:ubuntu-20.04-64:tests/test-4"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:44:13",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64:tests/test-4"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:44:14",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64:tests/test-2"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:44:14",
+ "verb": "Executing",
+ "task": "google:ubuntu-20.04-64:tests/test-2"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:44:15",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64:tests/test-2"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:44:15",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64:tests/test-5"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:44:16",
+ "verb": "Executing",
+ "task": "google:ubuntu-20.04-64:tests/test-5"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:44:16",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64:tests/test-5"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:44:17",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64:tests/"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:44:17",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64"
+ },
+ {
+ "type": "info",
+ "date": "2022-05-24",
+ "time": "17:44:18",
+ "info_type": "Error",
+ "verb": "restoring",
+ "task": "google:ubuntu-20.04-64",
+ "extra": null,
+ "detail": {
+ "lines": [
+ "-----\n",
+ "+ echo 'Restore snapd-testing-tools project'\n",
+ "Restore snapd-testing-tools project\n",
+ "/bin/bash: line 26: PROJECT_PATH: unbound variable\n",
+ "-----\n",
+ ".\n"
+ ]
+ }
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:44:18",
+ "verb": "Discarding",
+ "task": null,
+ "extra": "google:ubuntu-20.04-64 (may242043-312500)..."
+ },
+ {
+ "type": "result",
+ "date": "2022-05-24",
+ "time": "17:44:19",
+ "result_type": "Successful",
+ "level": "tasks",
+ "stage": null,
+ "number": "10",
+ "detail": null
+ },
+ {
+ "type": "result",
+ "date": "2022-05-24",
+ "time": "17:44:19",
+ "result_type": "Aborted",
+ "level": "tasks",
+ "stage": null,
+ "number": "0",
+ "detail": null
+ },
+ {
+ "type": "result",
+ "date": "2022-05-24",
+ "time": "17:44:19",
+ "result_type": "Failed",
+ "level": "project",
+ "stage": "restore",
+ "number": "2",
+ "detail": {
+ "lines": [
+ " - google:ubuntu-20.04-64:project\n",
+ " - google:ubuntu-22.04-64:project\n"
+ ]
+ }
+ }
+]
\ No newline at end of file
diff --git a/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/all-success-failed-restore.log b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/all-success-failed-restore.log
new file mode 100644
index 0000000..a777c9f
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/all-success-failed-restore.log
@@ -0,0 +1,74 @@
+2022-05-24 17:42:58 Project content is packed for delivery (1.30KB).
+2022-05-24 17:42:58 Sequence of jobs produced with -seed=1653424978
+2022-05-24 17:42:58 If killed, discard servers with: spread -reuse-pid=96667 -discard
+2022-05-24 17:42:58 Allocating google:ubuntu-22.04-64...
+2022-05-24 17:42:58 Allocating google:ubuntu-20.04-64...
+2022-05-24 17:43:07 Waiting for google:ubuntu-22.04-64 (may242043-312411) to boot at 34.148.59.204...
+2022-05-24 17:43:07 Waiting for google:ubuntu-20.04-64 (may242043-312500) to boot at 34.138.215.109...
+2022-05-24 17:43:43 Allocated google:ubuntu-22.04-64 (may242043-312411).
+2022-05-24 17:43:43 Connecting to google:ubuntu-22.04-64 (may242043-312411)...
+2022-05-24 17:43:45 Connected to google:ubuntu-22.04-64 (may242043-312411) at 34.148.59.204.
+2022-05-24 17:43:45 Sending project content to google:ubuntu-22.04-64 (may242043-312411)...
+2022-05-24 17:43:50 Preparing google:ubuntu-22.04-64 (may242043-312411)...
+2022-05-24 17:43:50 Preparing google:ubuntu-22.04-64:tests/ (may242043-312411)...
+2022-05-24 17:43:51 Preparing google:ubuntu-22.04-64:tests/test-3 (may242043-312411)...
+2022-05-24 17:43:52 Executing google:ubuntu-22.04-64:tests/test-3 (may242043-312411) (1/10)...
+2022-05-24 17:43:52 Restoring google:ubuntu-22.04-64:tests/test-3 (may242043-312411)...
+2022-05-24 17:43:53 Preparing google:ubuntu-22.04-64:tests/test-2 (may242043-312411)...
+2022-05-24 17:43:54 Executing google:ubuntu-22.04-64:tests/test-2 (may242043-312411) (2/10)...
+2022-05-24 17:43:55 Restoring google:ubuntu-22.04-64:tests/test-2 (may242043-312411)...
+2022-05-24 17:43:55 Preparing google:ubuntu-22.04-64:tests/test-4 (may242043-312411)...
+2022-05-24 17:43:56 Executing google:ubuntu-22.04-64:tests/test-4 (may242043-312411) (3/10)...
+2022-05-24 17:43:56 Restoring google:ubuntu-22.04-64:tests/test-4 (may242043-312411)...
+2022-05-24 17:43:57 Preparing google:ubuntu-22.04-64:tests/test-1 (may242043-312411)...
+2022-05-24 17:43:58 Executing google:ubuntu-22.04-64:tests/test-1 (may242043-312411) (4/10)...
+2022-05-24 17:43:58 Restoring google:ubuntu-22.04-64:tests/test-1 (may242043-312411)...
+2022-05-24 17:43:59 Preparing google:ubuntu-22.04-64:tests/test-5 (may242043-312411)...
+2022-05-24 17:44:00 Executing google:ubuntu-22.04-64:tests/test-5 (may242043-312411) (5/10)...
+2022-05-24 17:44:00 Restoring google:ubuntu-22.04-64:tests/test-5 (may242043-312411)...
+2022-05-24 17:44:01 Restoring google:ubuntu-22.04-64:tests/ (may242043-312411)...
+2022-05-24 17:44:02 Restoring google:ubuntu-22.04-64 (may242043-312411)...
+2022-05-24 17:44:02 Error restoring google:ubuntu-22.04-64 (may242043-312411) :
+-----
++ echo 'Restore snapd-testing-tools project'
+Restore snapd-testing-tools project
+/bin/bash: line 26: PROJECT_PATH: unbound variable
+-----
+.
+2022-05-24 17:44:02 Discarding google:ubuntu-22.04-64 (may242043-312411)...
+2022-05-24 17:44:04 Allocated google:ubuntu-20.04-64 (may242043-312500).
+2022-05-24 17:44:04 Connecting to google:ubuntu-20.04-64 (may242043-312500)...
+2022-05-24 17:44:05 Connected to google:ubuntu-20.04-64 (may242043-312500) at 34.138.215.109.
+2022-05-24 17:44:05 Sending project content to google:ubuntu-20.04-64 (may242043-312500)...
+2022-05-24 17:44:08 Preparing google:ubuntu-20.04-64 (may242043-312500)...
+2022-05-24 17:44:08 Preparing google:ubuntu-20.04-64:tests/ (may242043-312500)...
+2022-05-24 17:44:09 Preparing google:ubuntu-20.04-64:tests/test-3 (may242043-312500)...
+2022-05-24 17:44:09 Executing google:ubuntu-20.04-64:tests/test-3 (may242043-312500) (6/10)...
+2022-05-24 17:44:10 Restoring google:ubuntu-20.04-64:tests/test-3 (may242043-312500)...
+2022-05-24 17:44:10 Preparing google:ubuntu-20.04-64:tests/test-1 (may242043-312500)...
+2022-05-24 17:44:11 Executing google:ubuntu-20.04-64:tests/test-1 (may242043-312500) (7/10)...
+2022-05-24 17:44:11 Restoring google:ubuntu-20.04-64:tests/test-1 (may242043-312500)...
+2022-05-24 17:44:12 Preparing google:ubuntu-20.04-64:tests/test-4 (may242043-312500)...
+2022-05-24 17:44:13 Executing google:ubuntu-20.04-64:tests/test-4 (may242043-312500) (8/10)...
+2022-05-24 17:44:13 Restoring google:ubuntu-20.04-64:tests/test-4 (may242043-312500)...
+2022-05-24 17:44:14 Preparing google:ubuntu-20.04-64:tests/test-2 (may242043-312500)...
+2022-05-24 17:44:14 Executing google:ubuntu-20.04-64:tests/test-2 (may242043-312500) (9/10)...
+2022-05-24 17:44:15 Restoring google:ubuntu-20.04-64:tests/test-2 (may242043-312500)...
+2022-05-24 17:44:15 Preparing google:ubuntu-20.04-64:tests/test-5 (may242043-312500)...
+2022-05-24 17:44:16 Executing google:ubuntu-20.04-64:tests/test-5 (may242043-312500) (10/10)...
+2022-05-24 17:44:16 Restoring google:ubuntu-20.04-64:tests/test-5 (may242043-312500)...
+2022-05-24 17:44:17 Restoring google:ubuntu-20.04-64:tests/ (may242043-312500)...
+2022-05-24 17:44:17 Restoring google:ubuntu-20.04-64 (may242043-312500)...
+2022-05-24 17:44:18 Error restoring google:ubuntu-20.04-64 (may242043-312500) :
+-----
++ echo 'Restore snapd-testing-tools project'
+Restore snapd-testing-tools project
+/bin/bash: line 26: PROJECT_PATH: unbound variable
+-----
+.
+2022-05-24 17:44:18 Discarding google:ubuntu-20.04-64 (may242043-312500)...
+2022-05-24 17:44:19 Successful tasks: 10
+2022-05-24 17:44:19 Aborted tasks: 0
+2022-05-24 17:44:19 Failed project restore: 2
+ - google:ubuntu-20.04-64:project
+ - google:ubuntu-22.04-64:project
diff --git a/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/all-success.json b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/all-success.json
new file mode 100644
index 0000000..b3560db
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/all-success.json
@@ -0,0 +1,400 @@
+[
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:45:05",
+ "verb": "Allocating",
+ "task": null,
+ "extra": "google:ubuntu-22.04-64..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:45:05",
+ "verb": "Allocating",
+ "task": null,
+ "extra": "google:ubuntu-20.04-64..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:45:14",
+ "verb": "Waiting",
+ "task": null,
+ "extra": "for google:ubuntu-22.04-64 (may242045-878680) to boot at 35.237.221.225..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:45:15",
+ "verb": "Waiting",
+ "task": null,
+ "extra": "for google:ubuntu-20.04-64 (may242045-878764) to boot at 34.148.94.157..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:46:00",
+ "verb": "Allocated",
+ "task": null,
+ "extra": "google:ubuntu-22.04-64 (may242045-878680)."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:46:00",
+ "verb": "Connecting",
+ "task": null,
+ "extra": "to google:ubuntu-22.04-64 (may242045-878680)..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:46:02",
+ "verb": "Connected",
+ "task": null,
+ "extra": "to google:ubuntu-22.04-64 (may242045-878680) at 35.237.221.225."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:46:02",
+ "verb": "Sending",
+ "task": null,
+ "extra": "project content to google:ubuntu-22.04-64 (may242045-878680)..."
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:46:06",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:46:06",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64:tests/"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:46:07",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64:tests/test-1"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:46:08",
+ "verb": "Executing",
+ "task": "google:ubuntu-22.04-64:tests/test-1"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:46:08",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64:tests/test-1"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:46:09",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64:tests/test-4"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:46:09",
+ "verb": "Executing",
+ "task": "google:ubuntu-22.04-64:tests/test-4"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:46:10",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64:tests/test-4"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:46:10",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64:tests/test-3"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:46:11",
+ "verb": "Executing",
+ "task": "google:ubuntu-22.04-64:tests/test-3"
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:46:11",
+ "verb": "Allocated",
+ "task": null,
+ "extra": "google:ubuntu-20.04-64 (may242045-878764)."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:46:11",
+ "verb": "Connecting",
+ "task": null,
+ "extra": "to google:ubuntu-20.04-64 (may242045-878764)..."
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:46:11",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64:tests/test-3"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:46:12",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64:tests/test-2"
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:46:12",
+ "verb": "Connected",
+ "task": null,
+ "extra": "to google:ubuntu-20.04-64 (may242045-878764) at 34.148.94.157."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:46:12",
+ "verb": "Sending",
+ "task": null,
+ "extra": "project content to google:ubuntu-20.04-64 (may242045-878764)..."
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:46:12",
+ "verb": "Executing",
+ "task": "google:ubuntu-22.04-64:tests/test-2"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:46:13",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64:tests/test-2"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:46:13",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64:tests/test-5"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:46:14",
+ "verb": "Executing",
+ "task": "google:ubuntu-22.04-64:tests/test-5"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:46:14",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64:tests/test-5"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:46:15",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64:tests/"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:46:15",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:46:15",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64"
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:46:16",
+ "verb": "Discarding",
+ "task": null,
+ "extra": "google:ubuntu-22.04-64 (may242045-878680)..."
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:46:16",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64:tests/"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:46:17",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64:tests/test-2"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:46:17",
+ "verb": "Executing",
+ "task": "google:ubuntu-20.04-64:tests/test-2"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:46:18",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64:tests/test-2"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:46:19",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64:tests/test-1"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:46:19",
+ "verb": "Executing",
+ "task": "google:ubuntu-20.04-64:tests/test-1"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:46:20",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64:tests/test-1"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:46:20",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64:tests/test-5"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:46:21",
+ "verb": "Executing",
+ "task": "google:ubuntu-20.04-64:tests/test-5"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:46:22",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64:tests/test-5"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:46:22",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64:tests/test-4"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:46:23",
+ "verb": "Executing",
+ "task": "google:ubuntu-20.04-64:tests/test-4"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:46:24",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64:tests/test-4"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:46:24",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64:tests/test-3"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:46:25",
+ "verb": "Executing",
+ "task": "google:ubuntu-20.04-64:tests/test-3"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:46:25",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64:tests/test-3"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:46:26",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64:tests/"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:46:27",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64"
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:46:27",
+ "verb": "Discarding",
+ "task": null,
+ "extra": "google:ubuntu-20.04-64 (may242045-878764)..."
+ },
+ {
+ "type": "result",
+ "date": "2022-05-24",
+ "time": "17:46:28",
+ "result_type": "Successful",
+ "level": "tasks",
+ "stage": null,
+ "number": "10",
+ "detail": null
+ },
+ {
+ "type": "result",
+ "date": "2022-05-24",
+ "time": "17:46:28",
+ "result_type": "Aborted",
+ "level": "tasks",
+ "stage": null,
+ "number": "0",
+ "detail": null
+ }
+]
\ No newline at end of file
diff --git a/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/all-success.log b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/all-success.log
new file mode 100644
index 0000000..6fb7141
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/all-success.log
@@ -0,0 +1,57 @@
+2022-05-24 17:45:05 Project content is packed for delivery (2.04KB).
+2022-05-24 17:45:05 Sequence of jobs produced with -seed=1653425105
+2022-05-24 17:45:05 If killed, discard servers with: spread -reuse-pid=96844 -discard
+2022-05-24 17:45:05 Allocating google:ubuntu-22.04-64...
+2022-05-24 17:45:05 Allocating google:ubuntu-20.04-64...
+2022-05-24 17:45:14 Waiting for google:ubuntu-22.04-64 (may242045-878680) to boot at 35.237.221.225...
+2022-05-24 17:45:15 Waiting for google:ubuntu-20.04-64 (may242045-878764) to boot at 34.148.94.157...
+2022-05-24 17:46:00 Allocated google:ubuntu-22.04-64 (may242045-878680).
+2022-05-24 17:46:00 Connecting to google:ubuntu-22.04-64 (may242045-878680)...
+2022-05-24 17:46:02 Connected to google:ubuntu-22.04-64 (may242045-878680) at 35.237.221.225.
+2022-05-24 17:46:02 Sending project content to google:ubuntu-22.04-64 (may242045-878680)...
+2022-05-24 17:46:06 Preparing google:ubuntu-22.04-64 (may242045-878680)...
+2022-05-24 17:46:06 Preparing google:ubuntu-22.04-64:tests/ (may242045-878680)...
+2022-05-24 17:46:07 Preparing google:ubuntu-22.04-64:tests/test-1 (may242045-878680)...
+2022-05-24 17:46:08 Executing google:ubuntu-22.04-64:tests/test-1 (may242045-878680) (1/10)...
+2022-05-24 17:46:08 Restoring google:ubuntu-22.04-64:tests/test-1 (may242045-878680)...
+2022-05-24 17:46:09 Preparing google:ubuntu-22.04-64:tests/test-4 (may242045-878680)...
+2022-05-24 17:46:09 Executing google:ubuntu-22.04-64:tests/test-4 (may242045-878680) (2/10)...
+2022-05-24 17:46:10 Restoring google:ubuntu-22.04-64:tests/test-4 (may242045-878680)...
+2022-05-24 17:46:10 Preparing google:ubuntu-22.04-64:tests/test-3 (may242045-878680)...
+2022-05-24 17:46:11 Executing google:ubuntu-22.04-64:tests/test-3 (may242045-878680) (3/10)...
+2022-05-24 17:46:11 Allocated google:ubuntu-20.04-64 (may242045-878764).
+2022-05-24 17:46:11 Connecting to google:ubuntu-20.04-64 (may242045-878764)...
+2022-05-24 17:46:11 Restoring google:ubuntu-22.04-64:tests/test-3 (may242045-878680)...
+2022-05-24 17:46:12 Preparing google:ubuntu-22.04-64:tests/test-2 (may242045-878680)...
+2022-05-24 17:46:12 Connected to google:ubuntu-20.04-64 (may242045-878764) at 34.148.94.157.
+2022-05-24 17:46:12 Sending project content to google:ubuntu-20.04-64 (may242045-878764)...
+2022-05-24 17:46:12 Executing google:ubuntu-22.04-64:tests/test-2 (may242045-878680) (4/10)...
+2022-05-24 17:46:13 Restoring google:ubuntu-22.04-64:tests/test-2 (may242045-878680)...
+2022-05-24 17:46:13 Preparing google:ubuntu-22.04-64:tests/test-5 (may242045-878680)...
+2022-05-24 17:46:14 Executing google:ubuntu-22.04-64:tests/test-5 (may242045-878680) (5/10)...
+2022-05-24 17:46:14 Restoring google:ubuntu-22.04-64:tests/test-5 (may242045-878680)...
+2022-05-24 17:46:15 Restoring google:ubuntu-22.04-64:tests/ (may242045-878680)...
+2022-05-24 17:46:15 Preparing google:ubuntu-20.04-64 (may242045-878764)...
+2022-05-24 17:46:15 Restoring google:ubuntu-22.04-64 (may242045-878680)...
+2022-05-24 17:46:16 Discarding google:ubuntu-22.04-64 (may242045-878680)...
+2022-05-24 17:46:16 Preparing google:ubuntu-20.04-64:tests/ (may242045-878764)...
+2022-05-24 17:46:17 Preparing google:ubuntu-20.04-64:tests/test-2 (may242045-878764)...
+2022-05-24 17:46:17 Executing google:ubuntu-20.04-64:tests/test-2 (may242045-878764) (6/10)...
+2022-05-24 17:46:18 Restoring google:ubuntu-20.04-64:tests/test-2 (may242045-878764)...
+2022-05-24 17:46:19 Preparing google:ubuntu-20.04-64:tests/test-1 (may242045-878764)...
+2022-05-24 17:46:19 Executing google:ubuntu-20.04-64:tests/test-1 (may242045-878764) (7/10)...
+2022-05-24 17:46:20 Restoring google:ubuntu-20.04-64:tests/test-1 (may242045-878764)...
+2022-05-24 17:46:20 Preparing google:ubuntu-20.04-64:tests/test-5 (may242045-878764)...
+2022-05-24 17:46:21 Executing google:ubuntu-20.04-64:tests/test-5 (may242045-878764) (8/10)...
+2022-05-24 17:46:22 Restoring google:ubuntu-20.04-64:tests/test-5 (may242045-878764)...
+2022-05-24 17:46:22 Preparing google:ubuntu-20.04-64:tests/test-4 (may242045-878764)...
+2022-05-24 17:46:23 Executing google:ubuntu-20.04-64:tests/test-4 (may242045-878764) (9/10)...
+2022-05-24 17:46:24 Restoring google:ubuntu-20.04-64:tests/test-4 (may242045-878764)...
+2022-05-24 17:46:24 Preparing google:ubuntu-20.04-64:tests/test-3 (may242045-878764)...
+2022-05-24 17:46:25 Executing google:ubuntu-20.04-64:tests/test-3 (may242045-878764) (10/10)...
+2022-05-24 17:46:25 Restoring google:ubuntu-20.04-64:tests/test-3 (may242045-878764)...
+2022-05-24 17:46:26 Restoring google:ubuntu-20.04-64:tests/ (may242045-878764)...
+2022-05-24 17:46:27 Restoring google:ubuntu-20.04-64 (may242045-878764)...
+2022-05-24 17:46:27 Discarding google:ubuntu-20.04-64 (may242045-878764)...
+2022-05-24 17:46:28 Successful tasks: 10
+2022-05-24 17:46:28 Aborted tasks: 0
diff --git a/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/failed-prepare-and-restore.json b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/failed-prepare-and-restore.json
new file mode 100644
index 0000000..ff09ea5
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/failed-prepare-and-restore.json
@@ -0,0 +1,513 @@
+[
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:56:26",
+ "verb": "Allocating",
+ "task": null,
+ "extra": "google:ubuntu-22.04-64..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:56:26",
+ "verb": "Allocating",
+ "task": null,
+ "extra": "google:ubuntu-20.04-64..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:56:26",
+ "verb": "Allocating",
+ "task": null,
+ "extra": "google:ubuntu-20.04-64..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:56:36",
+ "verb": "Waiting",
+ "task": null,
+ "extra": "for google:ubuntu-20.04-64 (may242056-644336) to boot at 35.185.37.159..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:56:41",
+ "verb": "Waiting",
+ "task": null,
+ "extra": "for google:ubuntu-22.04-64 (may242056-644230) to boot at 34.75.94.174..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:56:42",
+ "verb": "Waiting",
+ "task": null,
+ "extra": "for google:ubuntu-20.04-64 (may242056-644428) to boot at 34.138.43.204..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:57:17",
+ "verb": "Allocated",
+ "task": null,
+ "extra": "google:ubuntu-22.04-64 (may242056-644230)."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:57:17",
+ "verb": "Connecting",
+ "task": null,
+ "extra": "to google:ubuntu-22.04-64 (may242056-644230)..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:57:19",
+ "verb": "Connected",
+ "task": null,
+ "extra": "to google:ubuntu-22.04-64 (may242056-644230) at 34.75.94.174."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:57:19",
+ "verb": "Sending",
+ "task": null,
+ "extra": "project content to google:ubuntu-22.04-64 (may242056-644230)..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:57:22",
+ "verb": "Allocated",
+ "task": null,
+ "extra": "google:ubuntu-20.04-64 (may242056-644336)."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:57:22",
+ "verb": "Connecting",
+ "task": null,
+ "extra": "to google:ubuntu-20.04-64 (may242056-644336)..."
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:57:23",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64"
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:57:24",
+ "verb": "Allocated",
+ "task": null,
+ "extra": "google:ubuntu-20.04-64 (may242056-644428)."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:57:24",
+ "verb": "Connecting",
+ "task": null,
+ "extra": "to google:ubuntu-20.04-64 (may242056-644428)..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:57:24",
+ "verb": "Connected",
+ "task": null,
+ "extra": "to google:ubuntu-20.04-64 (may242056-644336) at 35.185.37.159."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:57:24",
+ "verb": "Sending",
+ "task": null,
+ "extra": "project content to google:ubuntu-20.04-64 (may242056-644336)..."
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:57:24",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64:tests/"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:57:25",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64:tests/test-4"
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:57:25",
+ "verb": "Connected",
+ "task": null,
+ "extra": "to google:ubuntu-20.04-64 (may242056-644428) at 34.138.43.204."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:57:25",
+ "verb": "Sending",
+ "task": null,
+ "extra": "project content to google:ubuntu-20.04-64 (may242056-644428)..."
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:57:26",
+ "verb": "Executing",
+ "task": "google:ubuntu-22.04-64:tests/test-4"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:57:26",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64:tests/test-4"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:57:27",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64:tests/test-1"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:57:27",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:57:27",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:57:28",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64:tests/"
+ },
+ {
+ "type": "info",
+ "date": "2022-05-24",
+ "time": "17:57:28",
+ "info_type": "Error",
+ "verb": "preparing",
+ "task": "google:ubuntu-22.04-64:tests/test-1",
+ "extra": null,
+ "detail": {
+ "lines": [
+ "-----\n",
+ "+ echo prepare\n",
+ "prepare\n",
+ "+ exit 1\n",
+ "-----\n",
+ ".\n"
+ ]
+ }
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:57:28",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64:tests/test-1"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:57:28",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64:tests/"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:57:28",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64:tests/test-2"
+ },
+ {
+ "type": "info",
+ "date": "2022-05-24",
+ "time": "17:57:28",
+ "info_type": "Error",
+ "verb": "restoring",
+ "task": "google:ubuntu-22.04-64:tests/test-1",
+ "extra": null,
+ "detail": {
+ "lines": [
+ "-----\n",
+ "+ echo restore\n",
+ "restore\n",
+ "+ exit 1\n",
+ "-----\n",
+ ".\n"
+ ]
+ }
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:57:28",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64:tests/"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:57:29",
+ "verb": "Executing",
+ "task": "google:ubuntu-20.04-64:tests/test-2"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:57:29",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64:tests/test-4"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:57:29",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:57:29",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64:tests/test-2"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:57:29",
+ "verb": "Executing",
+ "task": "google:ubuntu-20.04-64:tests/test-4"
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:57:29",
+ "verb": "Discarding",
+ "task": null,
+ "extra": "google:ubuntu-22.04-64 (may242056-644230)..."
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:57:30",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64:tests/test-5"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:57:30",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64:tests/test-4"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:57:30",
+ "verb": "Executing",
+ "task": "google:ubuntu-20.04-64:tests/test-5"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:57:30",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64:tests/test-1"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:57:31",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64:tests/test-5"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:57:31",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64:tests/test-3"
+ },
+ {
+ "type": "info",
+ "date": "2022-05-24",
+ "time": "17:57:31",
+ "info_type": "Error",
+ "verb": "preparing",
+ "task": "google:ubuntu-20.04-64:tests/test-1",
+ "extra": null,
+ "detail": {
+ "lines": [
+ "-----\n",
+ "+ echo prepare\n",
+ "prepare\n",
+ "+ exit 1\n",
+ "-----\n",
+ ".\n"
+ ]
+ }
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:57:31",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64:tests/test-1"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:57:32",
+ "verb": "Executing",
+ "task": "google:ubuntu-20.04-64:tests/test-3"
+ },
+ {
+ "type": "info",
+ "date": "2022-05-24",
+ "time": "17:57:32",
+ "info_type": "Error",
+ "verb": "restoring",
+ "task": "google:ubuntu-20.04-64:tests/test-1",
+ "extra": null,
+ "detail": {
+ "lines": [
+ "-----\n",
+ "+ echo restore\n",
+ "restore\n",
+ "+ exit 1\n",
+ "-----\n",
+ ".\n"
+ ]
+ }
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:57:32",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64:tests/"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:57:32",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64:tests/test-3"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:57:33",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:57:33",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64:tests/"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:57:33",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64"
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:57:34",
+ "verb": "Discarding",
+ "task": null,
+ "extra": "google:ubuntu-20.04-64 (may242056-644336)..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:57:34",
+ "verb": "Discarding",
+ "task": null,
+ "extra": "google:ubuntu-20.04-64 (may242056-644428)..."
+ },
+ {
+ "type": "result",
+ "date": "2022-05-24",
+ "time": "17:57:34",
+ "result_type": "Successful",
+ "level": "tasks",
+ "stage": null,
+ "number": "5",
+ "detail": null
+ },
+ {
+ "type": "result",
+ "date": "2022-05-24",
+ "time": "17:57:34",
+ "result_type": "Aborted",
+ "level": "tasks",
+ "stage": null,
+ "number": "5",
+ "detail": null
+ },
+ {
+ "type": "result",
+ "date": "2022-05-24",
+ "time": "17:57:34",
+ "result_type": "Failed",
+ "level": "task",
+ "stage": "prepare",
+ "number": "2",
+ "detail": {
+ "lines": [
+ " - google:ubuntu-20.04-64:tests/test-1\n",
+ " - google:ubuntu-22.04-64:tests/test-1\n"
+ ]
+ }
+ },
+ {
+ "type": "result",
+ "date": "2022-05-24",
+ "time": "17:57:34",
+ "result_type": "Failed",
+ "level": "task",
+ "stage": "restore",
+ "number": "2",
+ "detail": {
+ "lines": [
+ " - google:ubuntu-20.04-64:tests/test-1\n",
+ " - google:ubuntu-22.04-64:tests/test-1\n"
+ ]
+ }
+ }
+]
\ No newline at end of file
diff --git a/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/failed-prepare-and-restore.log b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/failed-prepare-and-restore.log
new file mode 100644
index 0000000..59283cf
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/failed-prepare-and-restore.log
@@ -0,0 +1,91 @@
+2022-05-24 17:56:26 Project content is packed for delivery (3.41KB).
+2022-05-24 17:56:26 Sequence of jobs produced with -seed=1653425786
+2022-05-24 17:56:26 If killed, discard servers with: spread -reuse-pid=97780 -discard
+2022-05-24 17:56:26 Allocating google:ubuntu-22.04-64...
+2022-05-24 17:56:26 Allocating google:ubuntu-20.04-64...
+2022-05-24 17:56:26 Allocating google:ubuntu-20.04-64...
+2022-05-24 17:56:36 Waiting for google:ubuntu-20.04-64 (may242056-644336) to boot at 35.185.37.159...
+2022-05-24 17:56:41 Waiting for google:ubuntu-22.04-64 (may242056-644230) to boot at 34.75.94.174...
+2022-05-24 17:56:42 Waiting for google:ubuntu-20.04-64 (may242056-644428) to boot at 34.138.43.204...
+2022-05-24 17:57:17 Allocated google:ubuntu-22.04-64 (may242056-644230).
+2022-05-24 17:57:17 Connecting to google:ubuntu-22.04-64 (may242056-644230)...
+2022-05-24 17:57:19 Connected to google:ubuntu-22.04-64 (may242056-644230) at 34.75.94.174.
+2022-05-24 17:57:19 Sending project content to google:ubuntu-22.04-64 (may242056-644230)...
+2022-05-24 17:57:22 Allocated google:ubuntu-20.04-64 (may242056-644336).
+2022-05-24 17:57:22 Connecting to google:ubuntu-20.04-64 (may242056-644336)...
+2022-05-24 17:57:23 Preparing google:ubuntu-22.04-64 (may242056-644230)...
+2022-05-24 17:57:24 Allocated google:ubuntu-20.04-64 (may242056-644428).
+2022-05-24 17:57:24 Connecting to google:ubuntu-20.04-64 (may242056-644428)...
+2022-05-24 17:57:24 Connected to google:ubuntu-20.04-64 (may242056-644336) at 35.185.37.159.
+2022-05-24 17:57:24 Sending project content to google:ubuntu-20.04-64 (may242056-644336)...
+2022-05-24 17:57:24 Preparing google:ubuntu-22.04-64:tests/ (may242056-644230)...
+2022-05-24 17:57:25 Preparing google:ubuntu-22.04-64:tests/test-4 (may242056-644230)...
+2022-05-24 17:57:25 Connected to google:ubuntu-20.04-64 (may242056-644428) at 34.138.43.204.
+2022-05-24 17:57:25 Sending project content to google:ubuntu-20.04-64 (may242056-644428)...
+2022-05-24 17:57:26 Executing google:ubuntu-22.04-64:tests/test-4 (may242056-644230) (1/10)...
+2022-05-24 17:57:26 Restoring google:ubuntu-22.04-64:tests/test-4 (may242056-644230)...
+2022-05-24 17:57:27 Preparing google:ubuntu-22.04-64:tests/test-1 (may242056-644230)...
+2022-05-24 17:57:27 Preparing google:ubuntu-20.04-64 (may242056-644428)...
+2022-05-24 17:57:27 Preparing google:ubuntu-20.04-64 (may242056-644336)...
+2022-05-24 17:57:28 Preparing google:ubuntu-20.04-64:tests/ (may242056-644428)...
+2022-05-24 17:57:28 Error preparing google:ubuntu-22.04-64:tests/test-1 (may242056-644230) :
+-----
++ echo prepare
+prepare
++ exit 1
+-----
+.
+2022-05-24 17:57:28 Restoring google:ubuntu-22.04-64:tests/test-1 (may242056-644230)...
+2022-05-24 17:57:28 Preparing google:ubuntu-20.04-64:tests/ (may242056-644336)...
+2022-05-24 17:57:28 Preparing google:ubuntu-20.04-64:tests/test-2 (may242056-644428)...
+2022-05-24 17:57:28 Error restoring google:ubuntu-22.04-64:tests/test-1 (may242056-644230) :
+-----
++ echo restore
+restore
++ exit 1
+-----
+.
+2022-05-24 17:57:28 Restoring google:ubuntu-22.04-64:tests/ (may242056-644230)...
+2022-05-24 17:57:29 Executing google:ubuntu-20.04-64:tests/test-2 (may242056-644428) (3/10)...
+2022-05-24 17:57:29 Preparing google:ubuntu-20.04-64:tests/test-4 (may242056-644336)...
+2022-05-24 17:57:29 Restoring google:ubuntu-22.04-64 (may242056-644230)...
+2022-05-24 17:57:29 Restoring google:ubuntu-20.04-64:tests/test-2 (may242056-644428)...
+2022-05-24 17:57:29 Executing google:ubuntu-20.04-64:tests/test-4 (may242056-644336) (4/10)...
+2022-05-24 17:57:29 Discarding google:ubuntu-22.04-64 (may242056-644230)...
+2022-05-24 17:57:30 Preparing google:ubuntu-20.04-64:tests/test-5 (may242056-644428)...
+2022-05-24 17:57:30 Restoring google:ubuntu-20.04-64:tests/test-4 (may242056-644336)...
+2022-05-24 17:57:30 Executing google:ubuntu-20.04-64:tests/test-5 (may242056-644428) (5/10)...
+2022-05-24 17:57:30 Preparing google:ubuntu-20.04-64:tests/test-1 (may242056-644336)...
+2022-05-24 17:57:31 Restoring google:ubuntu-20.04-64:tests/test-5 (may242056-644428)...
+2022-05-24 17:57:31 Preparing google:ubuntu-20.04-64:tests/test-3 (may242056-644428)...
+2022-05-24 17:57:31 Error preparing google:ubuntu-20.04-64:tests/test-1 (may242056-644336) :
+-----
++ echo prepare
+prepare
++ exit 1
+-----
+.
+2022-05-24 17:57:31 Restoring google:ubuntu-20.04-64:tests/test-1 (may242056-644336)...
+2022-05-24 17:57:32 Executing google:ubuntu-20.04-64:tests/test-3 (may242056-644428) (6/10)...
+2022-05-24 17:57:32 Error restoring google:ubuntu-20.04-64:tests/test-1 (may242056-644336) :
+-----
++ echo restore
+restore
++ exit 1
+-----
+.
+2022-05-24 17:57:32 Restoring google:ubuntu-20.04-64:tests/ (may242056-644336)...
+2022-05-24 17:57:32 Restoring google:ubuntu-20.04-64:tests/test-3 (may242056-644428)...
+2022-05-24 17:57:33 Restoring google:ubuntu-20.04-64 (may242056-644336)...
+2022-05-24 17:57:33 Restoring google:ubuntu-20.04-64:tests/ (may242056-644428)...
+2022-05-24 17:57:33 Restoring google:ubuntu-20.04-64 (may242056-644428)...
+2022-05-24 17:57:34 Discarding google:ubuntu-20.04-64 (may242056-644336)...
+2022-05-24 17:57:34 Discarding google:ubuntu-20.04-64 (may242056-644428)...
+2022-05-24 17:57:34 Successful tasks: 5
+2022-05-24 17:57:34 Aborted tasks: 5
+2022-05-24 17:57:34 Failed task prepare: 2
+ - google:ubuntu-20.04-64:tests/test-1
+ - google:ubuntu-22.04-64:tests/test-1
+2022-05-24 17:57:34 Failed task restore: 2
+ - google:ubuntu-20.04-64:tests/test-1
+ - google:ubuntu-22.04-64:tests/test-1
diff --git a/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/failed-prepare-project.json b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/failed-prepare-project.json
new file mode 100644
index 0000000..ed7da17
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/failed-prepare-project.json
@@ -0,0 +1,315 @@
+[
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:04:51",
+ "verb": "Allocating",
+ "task": null,
+ "extra": "google:ubuntu-22.04-64..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:04:51",
+ "verb": "Allocating",
+ "task": null,
+ "extra": "google:ubuntu-20.04-64..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:05:00",
+ "verb": "Waiting",
+ "task": null,
+ "extra": "for google:ubuntu-22.04-64 (may242104-523536) to boot at 34.148.146.189..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:05:05",
+ "verb": "Waiting",
+ "task": null,
+ "extra": "for google:ubuntu-20.04-64 (may242104-523644) to boot at 35.185.103.64..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:05:42",
+ "verb": "Allocated",
+ "task": null,
+ "extra": "google:ubuntu-22.04-64 (may242104-523536)."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:05:42",
+ "verb": "Connecting",
+ "task": null,
+ "extra": "to google:ubuntu-22.04-64 (may242104-523536)..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:05:43",
+ "verb": "Connected",
+ "task": null,
+ "extra": "to google:ubuntu-22.04-64 (may242104-523536) at 34.148.146.189."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:05:43",
+ "verb": "Sending",
+ "task": null,
+ "extra": "project content to google:ubuntu-22.04-64 (may242104-523536)..."
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:05:47",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:05:48",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64:tests/"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:05:48",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64:tests/test-2"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:05:49",
+ "verb": "Executing",
+ "task": "google:ubuntu-22.04-64:tests/test-2"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:05:49",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64:tests/test-2"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:05:50",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64:tests/test-5"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:05:50",
+ "verb": "Executing",
+ "task": "google:ubuntu-22.04-64:tests/test-5"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:05:50",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64:tests/test-5"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:05:51",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64:tests/test-1"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:05:52",
+ "verb": "Executing",
+ "task": "google:ubuntu-22.04-64:tests/test-1"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:05:52",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64:tests/test-1"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:05:53",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64:tests/test-4"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:05:53",
+ "verb": "Executing",
+ "task": "google:ubuntu-22.04-64:tests/test-4"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:05:54",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64:tests/test-4"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:05:54",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64:tests/test-3"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:05:55",
+ "verb": "Executing",
+ "task": "google:ubuntu-22.04-64:tests/test-3"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:05:55",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64:tests/test-3"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:05:56",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64:tests/"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:05:56",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64"
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:05:57",
+ "verb": "Allocated",
+ "task": null,
+ "extra": "google:ubuntu-20.04-64 (may242104-523644)."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:05:57",
+ "verb": "Connecting",
+ "task": null,
+ "extra": "to google:ubuntu-20.04-64 (may242104-523644)..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:05:57",
+ "verb": "Discarding",
+ "task": null,
+ "extra": "google:ubuntu-22.04-64 (may242104-523536)..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:05:58",
+ "verb": "Connected",
+ "task": null,
+ "extra": "to google:ubuntu-20.04-64 (may242104-523644) at 35.185.103.64."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:05:58",
+ "verb": "Sending",
+ "task": null,
+ "extra": "project content to google:ubuntu-20.04-64 (may242104-523644)..."
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:06:01",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64"
+ },
+ {
+ "type": "info",
+ "date": "2022-05-24",
+ "time": "18:06:01",
+ "info_type": "Error",
+ "verb": "preparing",
+ "task": "google:ubuntu-20.04-64",
+ "extra": null,
+ "detail": {
+ "lines": [
+ "-----\n",
+ "+ echo 'Prepare log-analyzer-reexec project'\n",
+ "Prepare log-analyzer-reexec project\n",
+ "+ '[' ubuntu-20.04-64 = ubuntu-20.04-64 ']'\n",
+ "+ exit 1\n",
+ "-----\n",
+ ".\n"
+ ]
+ }
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:06:01",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64"
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:06:02",
+ "verb": "Discarding",
+ "task": null,
+ "extra": "google:ubuntu-20.04-64 (may242104-523644)..."
+ },
+ {
+ "type": "result",
+ "date": "2022-05-24",
+ "time": "18:06:02",
+ "result_type": "Successful",
+ "level": "tasks",
+ "stage": null,
+ "number": "5",
+ "detail": null
+ },
+ {
+ "type": "result",
+ "date": "2022-05-24",
+ "time": "18:06:02",
+ "result_type": "Aborted",
+ "level": "tasks",
+ "stage": null,
+ "number": "5",
+ "detail": null
+ },
+ {
+ "type": "result",
+ "date": "2022-05-24",
+ "time": "18:06:02",
+ "result_type": "Failed",
+ "level": "project",
+ "stage": "prepare",
+ "number": "1",
+ "detail": {
+ "lines": [
+ " - google:ubuntu-20.04-64:project\n"
+ ]
+ }
+ }
+]
\ No newline at end of file
diff --git a/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/failed-prepare-project.log b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/failed-prepare-project.log
new file mode 100644
index 0000000..f17d748
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/failed-prepare-project.log
@@ -0,0 +1,50 @@
+2022-05-24 18:04:51 Project content is packed for delivery (4.90KB).
+2022-05-24 18:04:51 Sequence of jobs produced with -seed=1653426291
+2022-05-24 18:04:51 If killed, discard servers with: spread -reuse-pid=98482 -discard
+2022-05-24 18:04:51 Allocating google:ubuntu-22.04-64...
+2022-05-24 18:04:51 Allocating google:ubuntu-20.04-64...
+2022-05-24 18:05:00 Waiting for google:ubuntu-22.04-64 (may242104-523536) to boot at 34.148.146.189...
+2022-05-24 18:05:05 Waiting for google:ubuntu-20.04-64 (may242104-523644) to boot at 35.185.103.64...
+2022-05-24 18:05:42 Allocated google:ubuntu-22.04-64 (may242104-523536).
+2022-05-24 18:05:42 Connecting to google:ubuntu-22.04-64 (may242104-523536)...
+2022-05-24 18:05:43 Connected to google:ubuntu-22.04-64 (may242104-523536) at 34.148.146.189.
+2022-05-24 18:05:43 Sending project content to google:ubuntu-22.04-64 (may242104-523536)...
+2022-05-24 18:05:47 Preparing google:ubuntu-22.04-64 (may242104-523536)...
+2022-05-24 18:05:48 Preparing google:ubuntu-22.04-64:tests/ (may242104-523536)...
+2022-05-24 18:05:48 Preparing google:ubuntu-22.04-64:tests/test-2 (may242104-523536)...
+2022-05-24 18:05:49 Executing google:ubuntu-22.04-64:tests/test-2 (may242104-523536) (1/10)...
+2022-05-24 18:05:49 Restoring google:ubuntu-22.04-64:tests/test-2 (may242104-523536)...
+2022-05-24 18:05:50 Preparing google:ubuntu-22.04-64:tests/test-5 (may242104-523536)...
+2022-05-24 18:05:50 Executing google:ubuntu-22.04-64:tests/test-5 (may242104-523536) (2/10)...
+2022-05-24 18:05:50 Restoring google:ubuntu-22.04-64:tests/test-5 (may242104-523536)...
+2022-05-24 18:05:51 Preparing google:ubuntu-22.04-64:tests/test-1 (may242104-523536)...
+2022-05-24 18:05:52 Executing google:ubuntu-22.04-64:tests/test-1 (may242104-523536) (3/10)...
+2022-05-24 18:05:52 Restoring google:ubuntu-22.04-64:tests/test-1 (may242104-523536)...
+2022-05-24 18:05:53 Preparing google:ubuntu-22.04-64:tests/test-4 (may242104-523536)...
+2022-05-24 18:05:53 Executing google:ubuntu-22.04-64:tests/test-4 (may242104-523536) (4/10)...
+2022-05-24 18:05:54 Restoring google:ubuntu-22.04-64:tests/test-4 (may242104-523536)...
+2022-05-24 18:05:54 Preparing google:ubuntu-22.04-64:tests/test-3 (may242104-523536)...
+2022-05-24 18:05:55 Executing google:ubuntu-22.04-64:tests/test-3 (may242104-523536) (5/10)...
+2022-05-24 18:05:55 Restoring google:ubuntu-22.04-64:tests/test-3 (may242104-523536)...
+2022-05-24 18:05:56 Restoring google:ubuntu-22.04-64:tests/ (may242104-523536)...
+2022-05-24 18:05:56 Restoring google:ubuntu-22.04-64 (may242104-523536)...
+2022-05-24 18:05:57 Allocated google:ubuntu-20.04-64 (may242104-523644).
+2022-05-24 18:05:57 Connecting to google:ubuntu-20.04-64 (may242104-523644)...
+2022-05-24 18:05:57 Discarding google:ubuntu-22.04-64 (may242104-523536)...
+2022-05-24 18:05:58 Connected to google:ubuntu-20.04-64 (may242104-523644) at 35.185.103.64.
+2022-05-24 18:05:58 Sending project content to google:ubuntu-20.04-64 (may242104-523644)...
+2022-05-24 18:06:01 Preparing google:ubuntu-20.04-64 (may242104-523644)...
+2022-05-24 18:06:01 Error preparing google:ubuntu-20.04-64 (may242104-523644) :
+-----
++ echo 'Prepare log-analyzer-reexec project'
+Prepare log-analyzer-reexec project
++ '[' ubuntu-20.04-64 = ubuntu-20.04-64 ']'
++ exit 1
+-----
+.
+2022-05-24 18:06:01 Restoring google:ubuntu-20.04-64 (may242104-523644)...
+2022-05-24 18:06:02 Discarding google:ubuntu-20.04-64 (may242104-523644)...
+2022-05-24 18:06:02 Successful tasks: 5
+2022-05-24 18:06:02 Aborted tasks: 5
+2022-05-24 18:06:02 Failed project prepare: 1
+ - google:ubuntu-20.04-64:project
diff --git a/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/failed-prepare-suite.json b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/failed-prepare-suite.json
new file mode 100644
index 0000000..cd370c3
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/failed-prepare-suite.json
@@ -0,0 +1,327 @@
+[
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:02:09",
+ "verb": "Allocating",
+ "task": null,
+ "extra": "google:ubuntu-22.04-64..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:02:09",
+ "verb": "Allocating",
+ "task": null,
+ "extra": "google:ubuntu-20.04-64..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:02:17",
+ "verb": "Waiting",
+ "task": null,
+ "extra": "for google:ubuntu-22.04-64 (may242102-500954) to boot at 34.138.163.49..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:02:18",
+ "verb": "Waiting",
+ "task": null,
+ "extra": "for google:ubuntu-20.04-64 (may242102-501054) to boot at 35.227.118.17..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:03:04",
+ "verb": "Allocated",
+ "task": null,
+ "extra": "google:ubuntu-22.04-64 (may242102-500954)."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:03:04",
+ "verb": "Connecting",
+ "task": null,
+ "extra": "to google:ubuntu-22.04-64 (may242102-500954)..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:03:05",
+ "verb": "Connected",
+ "task": null,
+ "extra": "to google:ubuntu-22.04-64 (may242102-500954) at 34.138.163.49."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:03:05",
+ "verb": "Sending",
+ "task": null,
+ "extra": "project content to google:ubuntu-22.04-64 (may242102-500954)..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:03:09",
+ "verb": "Allocated",
+ "task": null,
+ "extra": "google:ubuntu-20.04-64 (may242102-501054)."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:03:09",
+ "verb": "Connecting",
+ "task": null,
+ "extra": "to google:ubuntu-20.04-64 (may242102-501054)..."
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:03:09",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64"
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:03:11",
+ "verb": "Connected",
+ "task": null,
+ "extra": "to google:ubuntu-20.04-64 (may242102-501054) at 35.227.118.17."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:03:11",
+ "verb": "Sending",
+ "task": null,
+ "extra": "project content to google:ubuntu-20.04-64 (may242102-501054)..."
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:03:11",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64:tests/"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:03:11",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64:tests/test-3"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:03:12",
+ "verb": "Executing",
+ "task": "google:ubuntu-22.04-64:tests/test-3"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:03:12",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64:tests/test-3"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:03:13",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64:tests/test-5"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:03:13",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:03:14",
+ "verb": "Executing",
+ "task": "google:ubuntu-22.04-64:tests/test-5"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:03:14",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64:tests/"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:03:14",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64:tests/test-5"
+ },
+ {
+ "type": "info",
+ "date": "2022-05-24",
+ "time": "18:03:14",
+ "info_type": "Error",
+ "verb": "preparing",
+ "task": "google:ubuntu-20.04-64:tests/",
+ "extra": null,
+ "detail": {
+ "lines": [
+ "-----\n",
+ "+ '[' ubuntu-20.04-64 = ubuntu-20.04-64 ']'\n",
+ "+ exit 1\n",
+ "-----\n",
+ ".\n"
+ ]
+ }
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:03:14",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64:tests/"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:03:15",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:03:15",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64:tests/test-1"
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:03:15",
+ "verb": "Discarding",
+ "task": null,
+ "extra": "google:ubuntu-20.04-64 (may242102-501054)..."
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:03:15",
+ "verb": "Executing",
+ "task": "google:ubuntu-22.04-64:tests/test-1"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:03:16",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64:tests/test-1"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:03:17",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64:tests/test-4"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:03:17",
+ "verb": "Executing",
+ "task": "google:ubuntu-22.04-64:tests/test-4"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:03:18",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64:tests/test-4"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:03:19",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64:tests/test-2"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:03:20",
+ "verb": "Executing",
+ "task": "google:ubuntu-22.04-64:tests/test-2"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:03:20",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64:tests/test-2"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:03:21",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64:tests/"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "18:03:22",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64"
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "18:03:22",
+ "verb": "Discarding",
+ "task": null,
+ "extra": "google:ubuntu-22.04-64 (may242102-500954)..."
+ },
+ {
+ "type": "result",
+ "date": "2022-05-24",
+ "time": "18:03:23",
+ "result_type": "Successful",
+ "level": "tasks",
+ "stage": null,
+ "number": "5",
+ "detail": null
+ },
+ {
+ "type": "result",
+ "date": "2022-05-24",
+ "time": "18:03:23",
+ "result_type": "Aborted",
+ "level": "tasks",
+ "stage": null,
+ "number": "5",
+ "detail": null
+ },
+ {
+ "type": "result",
+ "date": "2022-05-24",
+ "time": "18:03:23",
+ "result_type": "Failed",
+ "level": "suite",
+ "stage": "prepare",
+ "number": "1",
+ "detail": {
+ "lines": [
+ " - google:ubuntu-20.04-64:tests/\n"
+ ]
+ }
+ }
+]
\ No newline at end of file
diff --git a/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/failed-prepare-suite.log b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/failed-prepare-suite.log
new file mode 100644
index 0000000..e61c089
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/failed-prepare-suite.log
@@ -0,0 +1,50 @@
+2022-05-24 18:02:09 Project content is packed for delivery (4.54KB).
+2022-05-24 18:02:09 Sequence of jobs produced with -seed=1653426129
+2022-05-24 18:02:09 If killed, discard servers with: spread -reuse-pid=98221 -discard
+2022-05-24 18:02:09 Allocating google:ubuntu-22.04-64...
+2022-05-24 18:02:09 Allocating google:ubuntu-20.04-64...
+2022-05-24 18:02:17 Waiting for google:ubuntu-22.04-64 (may242102-500954) to boot at 34.138.163.49...
+2022-05-24 18:02:18 Waiting for google:ubuntu-20.04-64 (may242102-501054) to boot at 35.227.118.17...
+2022-05-24 18:03:04 Allocated google:ubuntu-22.04-64 (may242102-500954).
+2022-05-24 18:03:04 Connecting to google:ubuntu-22.04-64 (may242102-500954)...
+2022-05-24 18:03:05 Connected to google:ubuntu-22.04-64 (may242102-500954) at 34.138.163.49.
+2022-05-24 18:03:05 Sending project content to google:ubuntu-22.04-64 (may242102-500954)...
+2022-05-24 18:03:09 Allocated google:ubuntu-20.04-64 (may242102-501054).
+2022-05-24 18:03:09 Connecting to google:ubuntu-20.04-64 (may242102-501054)...
+2022-05-24 18:03:09 Preparing google:ubuntu-22.04-64 (may242102-500954)...
+2022-05-24 18:03:11 Connected to google:ubuntu-20.04-64 (may242102-501054) at 35.227.118.17.
+2022-05-24 18:03:11 Sending project content to google:ubuntu-20.04-64 (may242102-501054)...
+2022-05-24 18:03:11 Preparing google:ubuntu-22.04-64:tests/ (may242102-500954)...
+2022-05-24 18:03:11 Preparing google:ubuntu-22.04-64:tests/test-3 (may242102-500954)...
+2022-05-24 18:03:12 Executing google:ubuntu-22.04-64:tests/test-3 (may242102-500954) (1/10)...
+2022-05-24 18:03:12 Restoring google:ubuntu-22.04-64:tests/test-3 (may242102-500954)...
+2022-05-24 18:03:13 Preparing google:ubuntu-22.04-64:tests/test-5 (may242102-500954)...
+2022-05-24 18:03:13 Preparing google:ubuntu-20.04-64 (may242102-501054)...
+2022-05-24 18:03:14 Executing google:ubuntu-22.04-64:tests/test-5 (may242102-500954) (2/10)...
+2022-05-24 18:03:14 Preparing google:ubuntu-20.04-64:tests/ (may242102-501054)...
+2022-05-24 18:03:14 Restoring google:ubuntu-22.04-64:tests/test-5 (may242102-500954)...
+2022-05-24 18:03:14 Error preparing google:ubuntu-20.04-64:tests/ (may242102-501054) :
+-----
++ '[' ubuntu-20.04-64 = ubuntu-20.04-64 ']'
++ exit 1
+-----
+.
+2022-05-24 18:03:14 Restoring google:ubuntu-20.04-64:tests/ (may242102-501054)...
+2022-05-24 18:03:15 Restoring google:ubuntu-20.04-64 (may242102-501054)...
+2022-05-24 18:03:15 Preparing google:ubuntu-22.04-64:tests/test-1 (may242102-500954)...
+2022-05-24 18:03:15 Discarding google:ubuntu-20.04-64 (may242102-501054)...
+2022-05-24 18:03:15 Executing google:ubuntu-22.04-64:tests/test-1 (may242102-500954) (8/10)...
+2022-05-24 18:03:16 Restoring google:ubuntu-22.04-64:tests/test-1 (may242102-500954)...
+2022-05-24 18:03:17 Preparing google:ubuntu-22.04-64:tests/test-4 (may242102-500954)...
+2022-05-24 18:03:17 Executing google:ubuntu-22.04-64:tests/test-4 (may242102-500954) (9/10)...
+2022-05-24 18:03:18 Restoring google:ubuntu-22.04-64:tests/test-4 (may242102-500954)...
+2022-05-24 18:03:19 Preparing google:ubuntu-22.04-64:tests/test-2 (may242102-500954)...
+2022-05-24 18:03:20 Executing google:ubuntu-22.04-64:tests/test-2 (may242102-500954) (10/10)...
+2022-05-24 18:03:20 Restoring google:ubuntu-22.04-64:tests/test-2 (may242102-500954)...
+2022-05-24 18:03:21 Restoring google:ubuntu-22.04-64:tests/ (may242102-500954)...
+2022-05-24 18:03:22 Restoring google:ubuntu-22.04-64 (may242102-500954)...
+2022-05-24 18:03:22 Discarding google:ubuntu-22.04-64 (may242102-500954)...
+2022-05-24 18:03:23 Successful tasks: 5
+2022-05-24 18:03:23 Aborted tasks: 5
+2022-05-24 18:03:23 Failed suite prepare: 1
+ - google:ubuntu-20.04-64:tests/
diff --git a/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/failed-prepare-task.json b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/failed-prepare-task.json
new file mode 100644
index 0000000..9af0c03
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/failed-prepare-task.json
@@ -0,0 +1,607 @@
+[
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:58:47",
+ "verb": "Allocating",
+ "task": null,
+ "extra": "google:ubuntu-22.04-64..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:58:47",
+ "verb": "Allocating",
+ "task": null,
+ "extra": "google:ubuntu-20.04-64..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:58:47",
+ "verb": "Allocating",
+ "task": null,
+ "extra": "google:ubuntu-20.04-64..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:58:47",
+ "verb": "Allocating",
+ "task": null,
+ "extra": "google:ubuntu-22.04-64..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:58:56",
+ "verb": "Waiting",
+ "task": null,
+ "extra": "for google:ubuntu-22.04-64 (may242058-071834) to boot at 34.75.85.33..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:58:56",
+ "verb": "Waiting",
+ "task": null,
+ "extra": "for google:ubuntu-20.04-64 (may242058-071935) to boot at 104.196.44.113..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:58:57",
+ "verb": "Waiting",
+ "task": null,
+ "extra": "for google:ubuntu-20.04-64 (may242058-072004) to boot at 35.196.115.140..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:59:02",
+ "verb": "Waiting",
+ "task": null,
+ "extra": "for google:ubuntu-22.04-64 (may242058-072021) to boot at 34.139.4.50..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:59:32",
+ "verb": "Allocated",
+ "task": null,
+ "extra": "google:ubuntu-22.04-64 (may242058-071834)."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:59:32",
+ "verb": "Connecting",
+ "task": null,
+ "extra": "to google:ubuntu-22.04-64 (may242058-071834)..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:59:33",
+ "verb": "Connected",
+ "task": null,
+ "extra": "to google:ubuntu-22.04-64 (may242058-071834) at 34.75.85.33."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:59:33",
+ "verb": "Sending",
+ "task": null,
+ "extra": "project content to google:ubuntu-22.04-64 (may242058-071834)..."
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:59:37",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:59:38",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64:tests/"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:59:38",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64:tests/test-1"
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:59:38",
+ "verb": "Allocated",
+ "task": null,
+ "extra": "google:ubuntu-22.04-64 (may242058-072021)."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:59:38",
+ "verb": "Connecting",
+ "task": null,
+ "extra": "to google:ubuntu-22.04-64 (may242058-072021)..."
+ },
+ {
+ "type": "info",
+ "date": "2022-05-24",
+ "time": "17:59:39",
+ "info_type": "Error",
+ "verb": "preparing",
+ "task": "google:ubuntu-22.04-64:tests/test-1",
+ "extra": null,
+ "detail": {
+ "lines": [
+ "-----\n",
+ "+ echo prepare\n",
+ "prepare\n",
+ "+ exit 1\n",
+ "-----\n",
+ ".\n"
+ ]
+ }
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:59:39",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64:tests/test-1"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:59:39",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64:tests/test-5"
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:59:40",
+ "verb": "Connected",
+ "task": null,
+ "extra": "to google:ubuntu-22.04-64 (may242058-072021) at 34.139.4.50."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:59:40",
+ "verb": "Sending",
+ "task": null,
+ "extra": "project content to google:ubuntu-22.04-64 (may242058-072021)..."
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:59:40",
+ "verb": "Executing",
+ "task": "google:ubuntu-22.04-64:tests/test-5"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:59:40",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64:tests/test-5"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:59:41",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64:tests/test-3"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:59:41",
+ "verb": "Executing",
+ "task": "google:ubuntu-22.04-64:tests/test-3"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:59:42",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64:tests/test-3"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:59:42",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64:tests/test-2"
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:59:43",
+ "verb": "Allocated",
+ "task": null,
+ "extra": "google:ubuntu-20.04-64 (may242058-072004)."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:59:43",
+ "verb": "Connecting",
+ "task": null,
+ "extra": "to google:ubuntu-20.04-64 (may242058-072004)..."
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:59:43",
+ "verb": "Executing",
+ "task": "google:ubuntu-22.04-64:tests/test-2"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:59:43",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64:tests/test-2"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:59:43",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:59:44",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64:tests/"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:59:44",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64:tests/"
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:59:44",
+ "verb": "Connected",
+ "task": null,
+ "extra": "to google:ubuntu-20.04-64 (may242058-072004) at 35.196.115.140."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:59:44",
+ "verb": "Sending",
+ "task": null,
+ "extra": "project content to google:ubuntu-20.04-64 (may242058-072004)..."
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:59:44",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64:tests/test-4"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:59:44",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:59:45",
+ "verb": "Executing",
+ "task": "google:ubuntu-22.04-64:tests/test-4"
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:59:45",
+ "verb": "Discarding",
+ "task": null,
+ "extra": "google:ubuntu-22.04-64 (may242058-071834)..."
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:59:45",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64:tests/test-4"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:59:46",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64:tests/"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:59:46",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64"
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:59:47",
+ "verb": "Discarding",
+ "task": null,
+ "extra": "google:ubuntu-22.04-64 (may242058-072021)..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:59:47",
+ "verb": "Allocated",
+ "task": null,
+ "extra": "google:ubuntu-20.04-64 (may242058-071935)."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:59:47",
+ "verb": "Connecting",
+ "task": null,
+ "extra": "to google:ubuntu-20.04-64 (may242058-071935)..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:59:49",
+ "verb": "Connected",
+ "task": null,
+ "extra": "to google:ubuntu-20.04-64 (may242058-071935) at 104.196.44.113."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:59:49",
+ "verb": "Sending",
+ "task": null,
+ "extra": "project content to google:ubuntu-20.04-64 (may242058-071935)..."
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:59:50",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:59:50",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64:tests/"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:59:51",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64:tests/test-5"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:59:51",
+ "verb": "Executing",
+ "task": "google:ubuntu-20.04-64:tests/test-5"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:59:52",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64:tests/test-5"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:59:52",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:59:52",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64:tests/test-3"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:59:53",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64:tests/"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:59:53",
+ "verb": "Executing",
+ "task": "google:ubuntu-20.04-64:tests/test-3"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:59:53",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64:tests/test-4"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:59:53",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64:tests/test-3"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:59:54",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64:tests/test-2"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:59:54",
+ "verb": "Executing",
+ "task": "google:ubuntu-20.04-64:tests/test-4"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:59:55",
+ "verb": "Executing",
+ "task": "google:ubuntu-20.04-64:tests/test-2"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:59:55",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64:tests/test-4"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:59:55",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64:tests/test-2"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:59:56",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64:tests/test-1"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:59:56",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64:tests/"
+ },
+ {
+ "type": "info",
+ "date": "2022-05-24",
+ "time": "17:59:56",
+ "info_type": "Error",
+ "verb": "preparing",
+ "task": "google:ubuntu-20.04-64:tests/test-1",
+ "extra": null,
+ "detail": {
+ "lines": [
+ "-----\n",
+ "+ echo prepare\n",
+ "prepare\n",
+ "+ exit 1\n",
+ "-----\n",
+ ".\n"
+ ]
+ }
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:59:56",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64:tests/test-1"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:59:56",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64:tests/"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:59:57",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:59:57",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64"
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:59:57",
+ "verb": "Discarding",
+ "task": null,
+ "extra": "google:ubuntu-20.04-64 (may242058-072004)..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:59:58",
+ "verb": "Discarding",
+ "task": null,
+ "extra": "google:ubuntu-20.04-64 (may242058-071935)..."
+ },
+ {
+ "type": "result",
+ "date": "2022-05-24",
+ "time": "17:59:58",
+ "result_type": "Successful",
+ "level": "tasks",
+ "stage": null,
+ "number": "8",
+ "detail": null
+ },
+ {
+ "type": "result",
+ "date": "2022-05-24",
+ "time": "17:59:58",
+ "result_type": "Aborted",
+ "level": "tasks",
+ "stage": null,
+ "number": "2",
+ "detail": null
+ },
+ {
+ "type": "result",
+ "date": "2022-05-24",
+ "time": "17:59:58",
+ "result_type": "Failed",
+ "level": "task",
+ "stage": "prepare",
+ "number": "2",
+ "detail": {
+ "lines": [
+ " - google:ubuntu-20.04-64:tests/test-1\n",
+ " - google:ubuntu-22.04-64:tests/test-1\n"
+ ]
+ }
+ }
+]
\ No newline at end of file
diff --git a/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/failed-prepare-task.log b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/failed-prepare-task.log
new file mode 100644
index 0000000..70948d1
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/failed-prepare-task.log
@@ -0,0 +1,94 @@
+2022-05-24 17:58:47 Project content is packed for delivery (3.92KB).
+2022-05-24 17:58:47 Sequence of jobs produced with -seed=1653425927
+2022-05-24 17:58:47 If killed, discard servers with: spread -reuse-pid=97961 -discard
+2022-05-24 17:58:47 Allocating google:ubuntu-22.04-64...
+2022-05-24 17:58:47 Allocating google:ubuntu-20.04-64...
+2022-05-24 17:58:47 Allocating google:ubuntu-20.04-64...
+2022-05-24 17:58:47 Allocating google:ubuntu-22.04-64...
+2022-05-24 17:58:56 Waiting for google:ubuntu-22.04-64 (may242058-071834) to boot at 34.75.85.33...
+2022-05-24 17:58:56 Waiting for google:ubuntu-20.04-64 (may242058-071935) to boot at 104.196.44.113...
+2022-05-24 17:58:57 Waiting for google:ubuntu-20.04-64 (may242058-072004) to boot at 35.196.115.140...
+2022-05-24 17:59:02 Waiting for google:ubuntu-22.04-64 (may242058-072021) to boot at 34.139.4.50...
+2022-05-24 17:59:32 Allocated google:ubuntu-22.04-64 (may242058-071834).
+2022-05-24 17:59:32 Connecting to google:ubuntu-22.04-64 (may242058-071834)...
+2022-05-24 17:59:33 Connected to google:ubuntu-22.04-64 (may242058-071834) at 34.75.85.33.
+2022-05-24 17:59:33 Sending project content to google:ubuntu-22.04-64 (may242058-071834)...
+2022-05-24 17:59:37 Preparing google:ubuntu-22.04-64 (may242058-071834)...
+2022-05-24 17:59:38 Preparing google:ubuntu-22.04-64:tests/ (may242058-071834)...
+2022-05-24 17:59:38 Preparing google:ubuntu-22.04-64:tests/test-1 (may242058-071834)...
+2022-05-24 17:59:38 Allocated google:ubuntu-22.04-64 (may242058-072021).
+2022-05-24 17:59:38 Connecting to google:ubuntu-22.04-64 (may242058-072021)...
+2022-05-24 17:59:39 Error preparing google:ubuntu-22.04-64:tests/test-1 (may242058-071834) :
+-----
++ echo prepare
+prepare
++ exit 1
+-----
+.
+2022-05-24 17:59:39 Restoring google:ubuntu-22.04-64:tests/test-1 (may242058-071834)...
+2022-05-24 17:59:39 Preparing google:ubuntu-22.04-64:tests/test-5 (may242058-071834)...
+2022-05-24 17:59:40 Connected to google:ubuntu-22.04-64 (may242058-072021) at 34.139.4.50.
+2022-05-24 17:59:40 Sending project content to google:ubuntu-22.04-64 (may242058-072021)...
+2022-05-24 17:59:40 Executing google:ubuntu-22.04-64:tests/test-5 (may242058-071834) (2/10)...
+2022-05-24 17:59:40 Restoring google:ubuntu-22.04-64:tests/test-5 (may242058-071834)...
+2022-05-24 17:59:41 Preparing google:ubuntu-22.04-64:tests/test-3 (may242058-071834)...
+2022-05-24 17:59:41 Executing google:ubuntu-22.04-64:tests/test-3 (may242058-071834) (3/10)...
+2022-05-24 17:59:42 Restoring google:ubuntu-22.04-64:tests/test-3 (may242058-071834)...
+2022-05-24 17:59:42 Preparing google:ubuntu-22.04-64:tests/test-2 (may242058-071834)...
+2022-05-24 17:59:43 Allocated google:ubuntu-20.04-64 (may242058-072004).
+2022-05-24 17:59:43 Connecting to google:ubuntu-20.04-64 (may242058-072004)...
+2022-05-24 17:59:43 Executing google:ubuntu-22.04-64:tests/test-2 (may242058-071834) (4/10)...
+2022-05-24 17:59:43 Restoring google:ubuntu-22.04-64:tests/test-2 (may242058-071834)...
+2022-05-24 17:59:43 Preparing google:ubuntu-22.04-64 (may242058-072021)...
+2022-05-24 17:59:44 Preparing google:ubuntu-22.04-64:tests/ (may242058-072021)...
+2022-05-24 17:59:44 Restoring google:ubuntu-22.04-64:tests/ (may242058-071834)...
+2022-05-24 17:59:44 Connected to google:ubuntu-20.04-64 (may242058-072004) at 35.196.115.140.
+2022-05-24 17:59:44 Sending project content to google:ubuntu-20.04-64 (may242058-072004)...
+2022-05-24 17:59:44 Preparing google:ubuntu-22.04-64:tests/test-4 (may242058-072021)...
+2022-05-24 17:59:44 Restoring google:ubuntu-22.04-64 (may242058-071834)...
+2022-05-24 17:59:45 Executing google:ubuntu-22.04-64:tests/test-4 (may242058-072021) (5/10)...
+2022-05-24 17:59:45 Discarding google:ubuntu-22.04-64 (may242058-071834)...
+2022-05-24 17:59:45 Restoring google:ubuntu-22.04-64:tests/test-4 (may242058-072021)...
+2022-05-24 17:59:46 Restoring google:ubuntu-22.04-64:tests/ (may242058-072021)...
+2022-05-24 17:59:46 Restoring google:ubuntu-22.04-64 (may242058-072021)...
+2022-05-24 17:59:47 Discarding google:ubuntu-22.04-64 (may242058-072021)...
+2022-05-24 17:59:47 Allocated google:ubuntu-20.04-64 (may242058-071935).
+2022-05-24 17:59:47 Connecting to google:ubuntu-20.04-64 (may242058-071935)...
+2022-05-24 17:59:49 Connected to google:ubuntu-20.04-64 (may242058-071935) at 104.196.44.113.
+2022-05-24 17:59:49 Sending project content to google:ubuntu-20.04-64 (may242058-071935)...
+2022-05-24 17:59:50 Preparing google:ubuntu-20.04-64 (may242058-072004)...
+2022-05-24 17:59:50 Preparing google:ubuntu-20.04-64:tests/ (may242058-072004)...
+2022-05-24 17:59:51 Preparing google:ubuntu-20.04-64:tests/test-5 (may242058-072004)...
+2022-05-24 17:59:51 Executing google:ubuntu-20.04-64:tests/test-5 (may242058-072004) (6/10)...
+2022-05-24 17:59:52 Restoring google:ubuntu-20.04-64:tests/test-5 (may242058-072004)...
+2022-05-24 17:59:52 Preparing google:ubuntu-20.04-64 (may242058-071935)...
+2022-05-24 17:59:52 Preparing google:ubuntu-20.04-64:tests/test-3 (may242058-072004)...
+2022-05-24 17:59:53 Preparing google:ubuntu-20.04-64:tests/ (may242058-071935)...
+2022-05-24 17:59:53 Executing google:ubuntu-20.04-64:tests/test-3 (may242058-072004) (7/10)...
+2022-05-24 17:59:53 Preparing google:ubuntu-20.04-64:tests/test-4 (may242058-071935)...
+2022-05-24 17:59:53 Restoring google:ubuntu-20.04-64:tests/test-3 (may242058-072004)...
+2022-05-24 17:59:54 Preparing google:ubuntu-20.04-64:tests/test-2 (may242058-072004)...
+2022-05-24 17:59:54 Executing google:ubuntu-20.04-64:tests/test-4 (may242058-071935) (8/10)...
+2022-05-24 17:59:55 Executing google:ubuntu-20.04-64:tests/test-2 (may242058-072004) (9/10)...
+2022-05-24 17:59:55 Restoring google:ubuntu-20.04-64:tests/test-4 (may242058-071935)...
+2022-05-24 17:59:55 Restoring google:ubuntu-20.04-64:tests/test-2 (may242058-072004)...
+2022-05-24 17:59:56 Preparing google:ubuntu-20.04-64:tests/test-1 (may242058-072004)...
+2022-05-24 17:59:56 Restoring google:ubuntu-20.04-64:tests/ (may242058-071935)...
+2022-05-24 17:59:56 Error preparing google:ubuntu-20.04-64:tests/test-1 (may242058-072004) :
+-----
++ echo prepare
+prepare
++ exit 1
+-----
+.
+2022-05-24 17:59:56 Restoring google:ubuntu-20.04-64:tests/test-1 (may242058-072004)...
+2022-05-24 17:59:56 Restoring google:ubuntu-20.04-64:tests/ (may242058-072004)...
+2022-05-24 17:59:57 Restoring google:ubuntu-20.04-64 (may242058-071935)...
+2022-05-24 17:59:57 Restoring google:ubuntu-20.04-64 (may242058-072004)...
+2022-05-24 17:59:57 Discarding google:ubuntu-20.04-64 (may242058-072004)...
+2022-05-24 17:59:58 Discarding google:ubuntu-20.04-64 (may242058-071935)...
+2022-05-24 17:59:58 Successful tasks: 8
+2022-05-24 17:59:58 Aborted tasks: 2
+2022-05-24 17:59:58 Failed task prepare: 2
+ - google:ubuntu-20.04-64:tests/test-1
+ - google:ubuntu-22.04-64:tests/test-1
diff --git a/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/with-aborted-and-failed-restore.json b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/with-aborted-and-failed-restore.json
new file mode 100644
index 0000000..8d5dbbd
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/with-aborted-and-failed-restore.json
@@ -0,0 +1,285 @@
+[
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:47:36",
+ "verb": "Allocating",
+ "task": null,
+ "extra": "google:ubuntu-22.04-64..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:47:36",
+ "verb": "Allocating",
+ "task": null,
+ "extra": "google:ubuntu-20.04-64..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:47:46",
+ "verb": "Waiting",
+ "task": null,
+ "extra": "for google:ubuntu-20.04-64 (may242047-566476) to boot at 104.196.212.243..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:47:46",
+ "verb": "Waiting",
+ "task": null,
+ "extra": "for google:ubuntu-22.04-64 (may242047-566626) to boot at 34.75.76.3..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:48:32",
+ "verb": "Allocated",
+ "task": null,
+ "extra": "google:ubuntu-22.04-64 (may242047-566626)."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:48:32",
+ "verb": "Connecting",
+ "task": null,
+ "extra": "to google:ubuntu-22.04-64 (may242047-566626)..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:48:34",
+ "verb": "Connected",
+ "task": null,
+ "extra": "to google:ubuntu-22.04-64 (may242047-566626) at 34.75.76.3."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:48:34",
+ "verb": "Sending",
+ "task": null,
+ "extra": "project content to google:ubuntu-22.04-64 (may242047-566626)..."
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:48:39",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:48:40",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64:tests/"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:48:40",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64:tests/test-5"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:48:41",
+ "verb": "Executing",
+ "task": "google:ubuntu-22.04-64:tests/test-5"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:48:42",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64:tests/test-5"
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:48:42",
+ "verb": "Allocated",
+ "task": null,
+ "extra": "google:ubuntu-20.04-64 (may242047-566476)."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:48:42",
+ "verb": "Connecting",
+ "task": null,
+ "extra": "to google:ubuntu-20.04-64 (may242047-566476)..."
+ },
+ {
+ "type": "info",
+ "date": "2022-05-24",
+ "time": "17:48:43",
+ "info_type": "Error",
+ "verb": "restoring",
+ "task": "google:ubuntu-22.04-64:tests/test-5",
+ "extra": null,
+ "detail": {
+ "lines": [
+ "-----\n",
+ "+ echo restore\n",
+ "restore\n",
+ "+ exit 1\n",
+ "-----\n",
+ ".\n"
+ ]
+ }
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:48:43",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64:tests/"
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:48:43",
+ "verb": "Connected",
+ "task": null,
+ "extra": "to google:ubuntu-20.04-64 (may242047-566476) at 104.196.212.243."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:48:43",
+ "verb": "Sending",
+ "task": null,
+ "extra": "project content to google:ubuntu-20.04-64 (may242047-566476)..."
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:48:44",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64"
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:48:45",
+ "verb": "Discarding",
+ "task": null,
+ "extra": "google:ubuntu-22.04-64 (may242047-566626)..."
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:48:46",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:48:47",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64:tests/"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:48:47",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64:tests/test-1"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:48:48",
+ "verb": "Executing",
+ "task": "google:ubuntu-20.04-64:tests/test-1"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:48:48",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64:tests/test-1"
+ },
+ {
+ "type": "info",
+ "date": "2022-05-24",
+ "time": "17:48:49",
+ "info_type": "Error",
+ "verb": "restoring",
+ "task": "google:ubuntu-20.04-64:tests/test-1",
+ "extra": null,
+ "detail": {
+ "lines": [
+ "-----\n",
+ "+ echo restore\n",
+ "restore\n",
+ "+ exit 1\n",
+ "-----\n",
+ ".\n"
+ ]
+ }
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:48:49",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64:tests/"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:48:49",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64"
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:48:50",
+ "verb": "Discarding",
+ "task": null,
+ "extra": "google:ubuntu-20.04-64 (may242047-566476)..."
+ },
+ {
+ "type": "result",
+ "date": "2022-05-24",
+ "time": "17:48:50",
+ "result_type": "Successful",
+ "level": "tasks",
+ "stage": null,
+ "number": "2",
+ "detail": null
+ },
+ {
+ "type": "result",
+ "date": "2022-05-24",
+ "time": "17:48:50",
+ "result_type": "Aborted",
+ "level": "tasks",
+ "stage": null,
+ "number": "8",
+ "detail": null
+ },
+ {
+ "type": "result",
+ "date": "2022-05-24",
+ "time": "17:48:50",
+ "result_type": "Failed",
+ "level": "task",
+ "stage": "restore",
+ "number": "2",
+ "detail": {
+ "lines": [
+ " - google:ubuntu-20.04-64:tests/test-1\n",
+ " - google:ubuntu-22.04-64:tests/test-5\n"
+ ]
+ }
+ }
+]
\ No newline at end of file
diff --git a/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/with-aborted-and-failed-restore.log b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/with-aborted-and-failed-restore.log
new file mode 100644
index 0000000..e13fbb3
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/with-aborted-and-failed-restore.log
@@ -0,0 +1,50 @@
+2022-05-24 17:47:36 Project content is packed for delivery (2.50KB).
+2022-05-24 17:47:36 Sequence of jobs produced with -seed=1653425256
+2022-05-24 17:47:36 If killed, discard servers with: spread -reuse-pid=97052 -discard
+2022-05-24 17:47:36 Allocating google:ubuntu-22.04-64...
+2022-05-24 17:47:36 Allocating google:ubuntu-20.04-64...
+2022-05-24 17:47:46 Waiting for google:ubuntu-20.04-64 (may242047-566476) to boot at 104.196.212.243...
+2022-05-24 17:47:46 Waiting for google:ubuntu-22.04-64 (may242047-566626) to boot at 34.75.76.3...
+2022-05-24 17:48:32 Allocated google:ubuntu-22.04-64 (may242047-566626).
+2022-05-24 17:48:32 Connecting to google:ubuntu-22.04-64 (may242047-566626)...
+2022-05-24 17:48:34 Connected to google:ubuntu-22.04-64 (may242047-566626) at 34.75.76.3.
+2022-05-24 17:48:34 Sending project content to google:ubuntu-22.04-64 (may242047-566626)...
+2022-05-24 17:48:39 Preparing google:ubuntu-22.04-64 (may242047-566626)...
+2022-05-24 17:48:40 Preparing google:ubuntu-22.04-64:tests/ (may242047-566626)...
+2022-05-24 17:48:40 Preparing google:ubuntu-22.04-64:tests/test-5 (may242047-566626)...
+2022-05-24 17:48:41 Executing google:ubuntu-22.04-64:tests/test-5 (may242047-566626) (1/10)...
+2022-05-24 17:48:42 Restoring google:ubuntu-22.04-64:tests/test-5 (may242047-566626)...
+2022-05-24 17:48:42 Allocated google:ubuntu-20.04-64 (may242047-566476).
+2022-05-24 17:48:42 Connecting to google:ubuntu-20.04-64 (may242047-566476)...
+2022-05-24 17:48:43 Error restoring google:ubuntu-22.04-64:tests/test-5 (may242047-566626) :
+-----
++ echo restore
+restore
++ exit 1
+-----
+.
+2022-05-24 17:48:43 Restoring google:ubuntu-22.04-64:tests/ (may242047-566626)...
+2022-05-24 17:48:43 Connected to google:ubuntu-20.04-64 (may242047-566476) at 104.196.212.243.
+2022-05-24 17:48:43 Sending project content to google:ubuntu-20.04-64 (may242047-566476)...
+2022-05-24 17:48:44 Restoring google:ubuntu-22.04-64 (may242047-566626)...
+2022-05-24 17:48:45 Discarding google:ubuntu-22.04-64 (may242047-566626)...
+2022-05-24 17:48:46 Preparing google:ubuntu-20.04-64 (may242047-566476)...
+2022-05-24 17:48:47 Preparing google:ubuntu-20.04-64:tests/ (may242047-566476)...
+2022-05-24 17:48:47 Preparing google:ubuntu-20.04-64:tests/test-1 (may242047-566476)...
+2022-05-24 17:48:48 Executing google:ubuntu-20.04-64:tests/test-1 (may242047-566476) (2/10)...
+2022-05-24 17:48:48 Restoring google:ubuntu-20.04-64:tests/test-1 (may242047-566476)...
+2022-05-24 17:48:49 Error restoring google:ubuntu-20.04-64:tests/test-1 (may242047-566476) :
+-----
++ echo restore
+restore
++ exit 1
+-----
+.
+2022-05-24 17:48:49 Restoring google:ubuntu-20.04-64:tests/ (may242047-566476)...
+2022-05-24 17:48:49 Restoring google:ubuntu-20.04-64 (may242047-566476)...
+2022-05-24 17:48:50 Discarding google:ubuntu-20.04-64 (may242047-566476)...
+2022-05-24 17:48:50 Successful tasks: 2
+2022-05-24 17:48:50 Aborted tasks: 8
+2022-05-24 17:48:50 Failed task restore: 2
+ - google:ubuntu-20.04-64:tests/test-1
+ - google:ubuntu-22.04-64:tests/test-5
diff --git a/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/with-failed-and-failed-restore-suite.json b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/with-failed-and-failed-restore-suite.json
new file mode 100644
index 0000000..ed0930e
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/with-failed-and-failed-restore-suite.json
@@ -0,0 +1,493 @@
+[
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:42:58",
+ "verb": "Allocating",
+ "task": null,
+ "extra": "google:ubuntu-22.04-64..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:42:58",
+ "verb": "Allocating",
+ "task": null,
+ "extra": "google:ubuntu-20.04-64..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:43:07",
+ "verb": "Waiting",
+ "task": null,
+ "extra": "for google:ubuntu-22.04-64 (may242043-312411) to boot at 34.148.59.204..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:43:07",
+ "verb": "Waiting",
+ "task": null,
+ "extra": "for google:ubuntu-20.04-64 (may242043-312500) to boot at 34.138.215.109..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:43:43",
+ "verb": "Allocated",
+ "task": null,
+ "extra": "google:ubuntu-22.04-64 (may242043-312411)."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:43:43",
+ "verb": "Connecting",
+ "task": null,
+ "extra": "to google:ubuntu-22.04-64 (may242043-312411)..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:43:45",
+ "verb": "Connected",
+ "task": null,
+ "extra": "to google:ubuntu-22.04-64 (may242043-312411) at 34.148.59.204."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:43:45",
+ "verb": "Sending",
+ "task": null,
+ "extra": "project content to google:ubuntu-22.04-64 (may242043-312411)..."
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:43:50",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:43:50",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64:tests/"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:43:51",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64:tests/test-3"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:43:52",
+ "verb": "Executing",
+ "task": "google:ubuntu-22.04-64:tests/test-3"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:43:52",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64:tests/test-3"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:43:53",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64:tests/test-2"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:43:54",
+ "verb": "Executing",
+ "task": "google:ubuntu-22.04-64:tests/test-2"
+ },
+ {
+ "type": "info",
+ "date": "2022-05-24",
+ "time": "17:43:54",
+ "info_type": "Error",
+ "verb": "executing",
+ "task": "google:ubuntu-22.04-64:tests/test-2",
+ "extra": null,
+ "detail": {
+ "lines": [
+ "-----\n",
+ "+ echo execute\n",
+ "execute\n",
+ "+ exit 1\n",
+ "-----\n",
+ ".\n"
+ ]
+ }
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:43:55",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64:tests/test-2"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:43:55",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64:tests/test-4"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:43:56",
+ "verb": "Executing",
+ "task": "google:ubuntu-22.04-64:tests/test-4"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:43:56",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64:tests/test-4"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:43:57",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64:tests/test-1"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:43:58",
+ "verb": "Executing",
+ "task": "google:ubuntu-22.04-64:tests/test-1"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:43:58",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64:tests/test-1"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:43:59",
+ "verb": "Preparing",
+ "task": "google:ubuntu-22.04-64:tests/test-5"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:44:00",
+ "verb": "Executing",
+ "task": "google:ubuntu-22.04-64:tests/test-5"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:44:00",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64:tests/test-5"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:44:01",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64:tests/"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:44:02",
+ "verb": "Restoring",
+ "task": "google:ubuntu-22.04-64"
+ },
+ {
+ "type": "info",
+ "date": "2022-05-24",
+ "time": "17:44:02",
+ "info_type": "Error",
+ "verb": "restoring",
+ "task": "google:ubuntu-22.04-64",
+ "extra": null,
+ "detail": {
+ "lines": [
+ "-----\n",
+ "+ echo 'Restore snapd-testing-tools project'\n",
+ "Restore snapd-testing-tools project\n",
+ "/bin/bash: line 26: PROJECT_PATH: unbound variable\n",
+ "-----\n",
+ ".\n"
+ ]
+ }
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:44:02",
+ "verb": "Discarding",
+ "task": null,
+ "extra": "google:ubuntu-22.04-64 (may242043-312411)..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:44:04",
+ "verb": "Allocated",
+ "task": null,
+ "extra": "google:ubuntu-20.04-64 (may242043-312500)."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:44:04",
+ "verb": "Connecting",
+ "task": null,
+ "extra": "to google:ubuntu-20.04-64 (may242043-312500)..."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:44:05",
+ "verb": "Connected",
+ "task": null,
+ "extra": "to google:ubuntu-20.04-64 (may242043-312500) at 34.138.215.109."
+ },
+ {
+ "type": "action",
+ "date": "2022-05-24",
+ "time": "17:44:05",
+ "verb": "Sending",
+ "task": null,
+ "extra": "project content to google:ubuntu-20.04-64 (may242043-312500)..."
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:44:08",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:44:08",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64:tests/"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:44:09",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64:tests/test-3"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:44:09",
+ "verb": "Executing",
+ "task": "google:ubuntu-20.04-64:tests/test-3"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:44:10",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64:tests/test-3"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:44:10",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64:tests/test-1"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:44:11",
+ "verb": "Executing",
+ "task": "google:ubuntu-20.04-64:tests/test-1"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:44:11",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64:tests/test-1"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:44:12",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64:tests/test-4"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:44:13",
+ "verb": "Executing",
+ "task": "google:ubuntu-20.04-64:tests/test-4"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:44:13",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64:tests/test-4"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:44:14",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64:tests/test-2"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:44:14",
+ "verb": "Executing",
+ "task": "google:ubuntu-20.04-64:tests/test-2"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:44:15",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64:tests/test-2"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:44:15",
+ "verb": "Preparing",
+ "task": "google:ubuntu-20.04-64:tests/test-5"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:44:16",
+ "verb": "Executing",
+ "task": "google:ubuntu-20.04-64:tests/test-5"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:44:16",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64:tests/test-5"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:44:17",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64:tests/"
+ },
+ {
+ "type": "phase",
+ "date": "2022-05-24",
+ "time": "17:44:17",
+ "verb": "Restoring",
+ "task": "google:ubuntu-20.04-64"
+ },
+ {
+ "type": "info",
+ "date": "2022-05-24",
+ "time": "17:44:18",
+ "info_type": "Error",
+ "verb": "restoring",
+ "task": "google:ubuntu-20.04-64:tests/",
+ "extra": null,
+ "detail": null
+ },
+ {
+ "type": "info",
+ "date": "2022-05-24",
+ "time": "17:44:18",
+ "info_type": "Error",
+ "verb": "restoring",
+ "task": "google:ubuntu-20.04-64",
+ "extra": null,
+ "detail": null
+ },
+ {
+ "type": "result",
+ "date": "2022-05-24",
+ "time": "17:44:19",
+ "result_type": "Successful",
+ "level": "tasks",
+ "stage": null,
+ "number": "9",
+ "detail": null
+ },
+ {
+ "type": "result",
+ "date": "2022-05-24",
+ "time": "17:44:19",
+ "result_type": "Aborted",
+ "level": "tasks",
+ "stage": null,
+ "number": "0",
+ "detail": null
+ },
+ {
+ "type": "result",
+ "date": "2022-05-24",
+ "time": "17:44:19",
+ "result_type": "Failed",
+ "level": "tasks",
+ "stage": null,
+ "number": "1",
+ "detail": {
+ "lines": [
+ " - google:ubuntu-22.04-64:tests/test-2\n"
+ ]
+ }
+ },
+ {
+ "type": "result",
+ "date": "2022-05-24",
+ "time": "17:44:19",
+ "result_type": "Failed",
+ "level": "suite",
+ "stage": "restore",
+ "number": "1",
+ "detail": {
+ "lines": [
+ " - google:ubuntu-20.04-64:tests/\n"
+ ]
+ }
+ },
+ {
+ "type": "result",
+ "date": "2022-05-24",
+ "time": "17:44:19",
+ "result_type": "Failed",
+ "level": "project",
+ "stage": "restore",
+ "number": "2",
+ "detail": {
+ "lines": [
+ " - google:ubuntu-20.04-64:project\n",
+ " - google:ubuntu-22.04-64:project\n"
+ ]
+ }
+ }
+]
\ No newline at end of file
diff --git a/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/with-failed-and-failed-restore-suite.log b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/with-failed-and-failed-restore-suite.log
new file mode 100644
index 0000000..919a43b
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/data/with-failed-and-failed-restore-suite.log
@@ -0,0 +1,79 @@
+2022-05-24 17:42:58 Project content is packed for delivery (1.30KB).
+2022-05-24 17:42:58 Sequence of jobs produced with -seed=1653424978
+2022-05-24 17:42:58 If killed, discard servers with: spread -reuse-pid=96667 -discard
+2022-05-24 17:42:58 Allocating google:ubuntu-22.04-64...
+2022-05-24 17:42:58 Allocating google:ubuntu-20.04-64...
+2022-05-24 17:43:07 Waiting for google:ubuntu-22.04-64 (may242043-312411) to boot at 34.148.59.204...
+2022-05-24 17:43:07 Waiting for google:ubuntu-20.04-64 (may242043-312500) to boot at 34.138.215.109...
+2022-05-24 17:43:43 Allocated google:ubuntu-22.04-64 (may242043-312411).
+2022-05-24 17:43:43 Connecting to google:ubuntu-22.04-64 (may242043-312411)...
+2022-05-24 17:43:45 Connected to google:ubuntu-22.04-64 (may242043-312411) at 34.148.59.204.
+2022-05-24 17:43:45 Sending project content to google:ubuntu-22.04-64 (may242043-312411)...
+2022-05-24 17:43:50 Preparing google:ubuntu-22.04-64 (may242043-312411)...
+2022-05-24 17:43:50 Preparing google:ubuntu-22.04-64:tests/ (may242043-312411)...
+2022-05-24 17:43:51 Preparing google:ubuntu-22.04-64:tests/test-3 (may242043-312411)...
+2022-05-24 17:43:52 Executing google:ubuntu-22.04-64:tests/test-3 (may242043-312411) (1/10)...
+2022-05-24 17:43:52 Restoring google:ubuntu-22.04-64:tests/test-3 (may242043-312411)...
+2022-05-24 17:43:53 Preparing google:ubuntu-22.04-64:tests/test-2 (may242043-312411)...
+2022-05-24 17:43:54 Executing google:ubuntu-22.04-64:tests/test-2 (may242043-312411) (2/10)...
+2022-05-24 17:43:54 Error executing google:ubuntu-22.04-64:tests/test-2 (may242043-312411) :
+-----
++ echo execute
+execute
++ exit 1
+-----
+.
+2022-05-24 17:43:55 Restoring google:ubuntu-22.04-64:tests/test-2 (may242043-312411)...
+2022-05-24 17:43:55 Preparing google:ubuntu-22.04-64:tests/test-4 (may242043-312411)...
+2022-05-24 17:43:56 Executing google:ubuntu-22.04-64:tests/test-4 (may242043-312411) (3/10)...
+2022-05-24 17:43:56 Restoring google:ubuntu-22.04-64:tests/test-4 (may242043-312411)...
+2022-05-24 17:43:57 Preparing google:ubuntu-22.04-64:tests/test-1 (may242043-312411)...
+2022-05-24 17:43:58 Executing google:ubuntu-22.04-64:tests/test-1 (may242043-312411) (4/10)...
+2022-05-24 17:43:58 Restoring google:ubuntu-22.04-64:tests/test-1 (may242043-312411)...
+2022-05-24 17:43:59 Preparing google:ubuntu-22.04-64:tests/test-5 (may242043-312411)...
+2022-05-24 17:44:00 Executing google:ubuntu-22.04-64:tests/test-5 (may242043-312411) (5/10)...
+2022-05-24 17:44:00 Restoring google:ubuntu-22.04-64:tests/test-5 (may242043-312411)...
+2022-05-24 17:44:01 Restoring google:ubuntu-22.04-64:tests/ (may242043-312411)...
+2022-05-24 17:44:02 Restoring google:ubuntu-22.04-64 (may242043-312411)...
+2022-05-24 17:44:02 Error restoring google:ubuntu-22.04-64 (may242043-312411) :
+-----
++ echo 'Restore snapd-testing-tools project'
+Restore snapd-testing-tools project
+/bin/bash: line 26: PROJECT_PATH: unbound variable
+-----
+.
+2022-05-24 17:44:02 Discarding google:ubuntu-22.04-64 (may242043-312411)...
+2022-05-24 17:44:04 Allocated google:ubuntu-20.04-64 (may242043-312500).
+2022-05-24 17:44:04 Connecting to google:ubuntu-20.04-64 (may242043-312500)...
+2022-05-24 17:44:05 Connected to google:ubuntu-20.04-64 (may242043-312500) at 34.138.215.109.
+2022-05-24 17:44:05 Sending project content to google:ubuntu-20.04-64 (may242043-312500)...
+2022-05-24 17:44:08 Preparing google:ubuntu-20.04-64 (may242043-312500)...
+2022-05-24 17:44:08 Preparing google:ubuntu-20.04-64:tests/ (may242043-312500)...
+2022-05-24 17:44:09 Preparing google:ubuntu-20.04-64:tests/test-3 (may242043-312500)...
+2022-05-24 17:44:09 Executing google:ubuntu-20.04-64:tests/test-3 (may242043-312500) (6/10)...
+2022-05-24 17:44:10 Restoring google:ubuntu-20.04-64:tests/test-3 (may242043-312500)...
+2022-05-24 17:44:10 Preparing google:ubuntu-20.04-64:tests/test-1 (may242043-312500)...
+2022-05-24 17:44:11 Executing google:ubuntu-20.04-64:tests/test-1 (may242043-312500) (7/10)...
+2022-05-24 17:44:11 Restoring google:ubuntu-20.04-64:tests/test-1 (may242043-312500)...
+2022-05-24 17:44:12 Preparing google:ubuntu-20.04-64:tests/test-4 (may242043-312500)...
+2022-05-24 17:44:13 Executing google:ubuntu-20.04-64:tests/test-4 (may242043-312500) (8/10)...
+2022-05-24 17:44:13 Restoring google:ubuntu-20.04-64:tests/test-4 (may242043-312500)...
+2022-05-24 17:44:14 Preparing google:ubuntu-20.04-64:tests/test-2 (may242043-312500)...
+2022-05-24 17:44:14 Executing google:ubuntu-20.04-64:tests/test-2 (may242043-312500) (9/10)...
+2022-05-24 17:44:15 Restoring google:ubuntu-20.04-64:tests/test-2 (may242043-312500)...
+2022-05-24 17:44:15 Preparing google:ubuntu-20.04-64:tests/test-5 (may242043-312500)...
+2022-05-24 17:44:16 Executing google:ubuntu-20.04-64:tests/test-5 (may242043-312500) (10/10)...
+2022-05-24 17:44:16 Restoring google:ubuntu-20.04-64:tests/test-5 (may242043-312500)...
+2022-05-24 17:44:17 Restoring google:ubuntu-20.04-64:tests/ (may242043-312500)...
+2022-05-24 17:44:17 Restoring google:ubuntu-20.04-64 (may242043-312500)...
+2022-05-24 17:44:18 Error restoring google:ubuntu-20.04-64:tests/ (may242043-312500) : EOF
+2022-05-24 17:44:18 Error restoring google:ubuntu-20.04-64 (may242043-312500) : EOF
+2022-05-24 17:44:19 Successful tasks: 9
+2022-05-24 17:44:19 Aborted tasks: 0
+2022-05-24 17:44:19 Failed tasks: 1
+ - google:ubuntu-22.04-64:tests/test-2
+2022-05-24 17:44:19 Failed suite restore: 1
+ - google:ubuntu-20.04-64:tests/
+2022-05-24 17:44:19 Failed project restore: 2
+ - google:ubuntu-20.04-64:project
+ - google:ubuntu-22.04-64:project
diff --git a/tests/lib/external/snapd-testing-tools/tests/log-analyzer/spread.yaml b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/spread.yaml
new file mode 100644
index 0000000..6ada9d6
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/spread.yaml
@@ -0,0 +1,35 @@
+project: log-analyzer
+
+details: |
+ This project is used to test the log analyzer tool. The project contains a
+ set of tests which are needed by the log analyzer to list all the tests which
+ have to be consider to be able to detect the tests which have been aborted and
+ are not listed in the results section.
+
+backends:
+ google:
+ key: '$(HOST: echo "$SPREAD_GOOGLE_KEY")'
+ location: snapd-spread/us-east1-b
+ halt-timeout: 2h
+ systems:
+ - ubuntu-20.04-64:
+ workers: 1
+ - ubuntu-22.04-64:
+ workers: 1
+
+path: /root/log-analyzer-reexec
+
+prepare: |
+ echo "Prepare log-analyzer-reexec project"
+
+restore: |
+ echo "Restore snapd-testing-tools project"
+
+suites:
+ tests/:
+ summary: Main test suite for snapd-testing-tools
+ prepare: |
+ echo "Preparing log-analyzer-reexec main suite"
+
+ restore: |
+ echo "Restoring log-analyzer-reexec main suite"
diff --git a/tests/lib/external/snapd-testing-tools/tests/log-analyzer/task.yaml b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/task.yaml
new file mode 100644
index 0000000..7add041
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/task.yaml
@@ -0,0 +1,248 @@
+summary: integration tests for log-analyzer tool
+
+details: |
+ This test checks the log-analyzer properly retrieves the tests-to-reexecute
+ based on the spread log analysis. Also checks it properly retrieves the
+ tests by result status and all the executed tasks.
+
+backends: [google]
+
+# Github actions agents are just running ubuntu jammy
+systems: [ubuntu-22.04-64]
+
+prepare: |
+ wget https://storage.googleapis.com/snapd-spread-tests/spread/spread-amd64.tar.gz
+ tar -xvzf spread-amd64.tar.gz
+ mv spread /usr/bin
+
+restore: |
+ rm spread-amd64.tar.gz /usr/bin/spread
+
+execute: |
+ compare_tasks_count() {
+ tasks=$1
+ count=$2
+
+ test "$(echo "$tasks" | wc -w)" = "$count"
+ }
+
+ # RESULTS OF JSON FILES USED #
+ # all-failed.json
+ # When all the tests are failed, no successful and no aborted tests are listed
+ # ubuntu-20.04 (F:5 P:0 A:0)
+ # ubuntu-22.04 (F:5 P:0 A:0)
+ # all-aborted.json
+ # When all the tests are aborted, no successful and no failed tests are listed
+ # ubuntu-20.04 (F:0 P:0 A:5)
+ # ubuntu-22.04 (F:0 P:0 A:5)
+ # all-success.json
+ # When all the tests execute correctly, no tests are listed for re-execution
+ # ubuntu-20.04 (F:0 P:5 A:0)
+ # ubuntu-22.04 (F:0 P:5 A:0)
+ # all-success-failed-restore.json
+ # When all the tasks are executed correctly but the suite fails to restore, all of them are listed to reexec
+ # ubuntu-20.04 (F:0 P:5 A:0)
+ # ubuntu-22.04 (F:0 P:5 A:0)
+ # failed-prepare-task.json
+ # Tasks which fail to prepare are not counted as failed and are counted as aborted
+ # ubuntu-20.04 (F:1 P:4 A:1)
+ # ubuntu-22.04 (F:1 P:4 A:1)
+ # failed-prepare-and-restore.json
+ # Tasks which fail to prepare or restore and are not executed are not counted as failed
+ # ubuntu-20.04 (F:0 P:4 A:1)
+ # ubuntu-22.04 (F:0 P:1 A:4)
+ # failed-prepare-project.json
+ # When a project fails to prepare, all the tasks are aborted
+ # ubuntu-20.04 (F:0 P:0 A:5)
+ # ubuntu-22.04 (F:0 P:5 A:0)
+ # failed-prepare-suite.json
+ # When a suite fails to prepare, all the tasks are aborted
+ # ubuntu-20.04 (F:0 P:0 A:5)
+ # ubuntu-22.04 (F:0 P:5 A:0)
+ # aborted-and-failed-execute-and-restore.json
+ # Tasks which fail to execute are counted as fail, when a task fails to restore the following tasks are aborted
+ # ubuntu-20.04 (F:1 P:4 A:0)
+ # ubuntu-22.04 (F:1 P:1 A:3)
+ # with-aborted-and-failed-restore.json
+ # Tasks which execute correctly and fail to restore are not counted as failed
+ # ubuntu-20.04 (F:0 P:1 A:4)
+ # ubuntu-22.04 (F:0 P:1 A:4)
+ # with-failed-and-failed-restore-suite.json
+ # Just failed tasks are counted for re-execution
+ # ubuntu-20.04 (F:0 P:0 A:0)
+ # ubuntu-22.04 (F:1 P:1 A:0)
+
+ ### CHECK HELP ###
+ log-analyzer | MATCH "usage: log-analyzer list-failed-tasks "
+ log-analyzer -h | MATCH "usage: log-analyzer list-failed-tasks "
+ log-analyzer --help | MATCH "usage: log-analyzer list-failed-tasks "
+
+ ### CHECK RE-EXECUTION ###
+
+ # When all the tasks have to be re-executed, the execution expression is returned
+ log-analyzer list-reexecute-tasks google: data/all-aborted.json | MATCH "^google:$"
+ log-analyzer list-reexecute-tasks google:ubuntu-20.04-64: data/all-aborted.json | MATCH "^google:ubuntu-20.04-64:$"
+
+ # When all the tests pass and then fail to restore, no tests are listed for re-execution
+ log-analyzer list-reexecute-tasks google: data/all-success-failed-restore.json | NOMATCH "google:"
+ compare_tasks_count "$(log-analyzer list-reexecute-tasks google: data/all-success-failed-restore.json)" 0
+ log-analyzer list-reexecute-tasks google:ubuntu-20.04-64: data/all-success-failed-restore.json | NOMATCH "google:ubuntu-20.04-64:"
+
+ # When all the tests are successful, no tests are listed for re-execution
+ log-analyzer list-reexecute-tasks google: data/all-success.json | NOMATCH "google:"
+ compare_tasks_count "$(log-analyzer list-reexecute-tasks google: data/all-success.json)" 0
+ log-analyzer list-reexecute-tasks google:ubuntu-20.04-64: data/all-success.json | NOMATCH "google:ubuntu-20.04-64:"
+
+ # When all the tests are failed, the execution expression is returned
+ log-analyzer list-reexecute-tasks google: data/all-failed.json | MATCH "^google:$"
+ log-analyzer list-reexecute-tasks google:ubuntu-20.04-64: data/all-failed.json | MATCH "^google:ubuntu-20.04-64:$"
+
+ # When some tests failed, those tests are displayed, if those tests are all the tests for the expression, then the expression is returned
+ log-analyzer list-reexecute-tasks google: data/failed-prepare-project.json | MATCH "google:ubuntu-20.04-64:tests/test-1"
+ compare_tasks_count "$(log-analyzer list-reexecute-tasks google: data/failed-prepare-project.json)" 5
+ log-analyzer list-reexecute-tasks google:ubuntu-20.04-64: data/failed-prepare-project.json | MATCH "^google:ubuntu-20.04-64:$"
+
+ log-analyzer list-reexecute-tasks google: data/failed-prepare-suite.json | MATCH "google:ubuntu-20.04-64:tests/test-1"
+ compare_tasks_count "$(log-analyzer list-reexecute-tasks google: data/failed-prepare-suite.json)" 5
+ log-analyzer list-reexecute-tasks google:ubuntu-20.04-64: data/failed-prepare-suite.json | MATCH "^google:ubuntu-20.04-64:$"
+
+ # when some tasks are aborted, those are listed, also tasks that failed to restore are listed
+ log-analyzer list-reexecute-tasks google: data/with-aborted-and-failed-restore.json | MATCH "google:"
+ compare_tasks_count "$(log-analyzer list-reexecute-tasks google: data/with-aborted-and-failed-restore.json)" 1
+
+ # When some tasks failed to prepare, those are aborted and are listed in the result
+ log-analyzer list-reexecute-tasks google: data/failed-prepare-task.json | MATCH "google:ubuntu-22.04-64:tests/test-1"
+ compare_tasks_count "$(log-analyzer list-reexecute-tasks google: data/failed-prepare-task.json)" 2
+
+ # When some tests failed to prepare and restore, those are listed just once in the result
+ log-analyzer list-reexecute-tasks google: data/failed-prepare-and-restore.json | MATCH "google:ubuntu-20.04-64:tests/test-1"
+ log-analyzer list-reexecute-tasks google: data/failed-prepare-and-restore.json | MATCH "google:ubuntu-22.04-64:tests/test-1"
+ log-analyzer list-reexecute-tasks google: data/failed-prepare-and-restore.json | NOMATCH "google:ubuntu-20.04-64:tests/test-4"
+ log-analyzer list-reexecute-tasks google: data/failed-prepare-and-restore.json | NOMATCH "google:ubuntu-22.04-64:tests/test-4"
+ compare_tasks_count "$(log-analyzer list-reexecute-tasks google: data/failed-prepare-and-restore.json)" 5
+
+ # When some tests failed to execute and restore, those are listed just once in the result
+ log-analyzer list-reexecute-tasks google: data/aborted-and-failed-execute-and-restore.json | MATCH "google:ubuntu-20.04-64:tests/test-2"
+ log-analyzer list-reexecute-tasks google: data/aborted-and-failed-execute-and-restore.json | MATCH "google:ubuntu-22.04-64:tests/test-2"
+ log-analyzer list-reexecute-tasks google: data/aborted-and-failed-execute-and-restore.json | NOMATCH "google:ubuntu-20.04-64:tests/test-4"
+ log-analyzer list-reexecute-tasks google: data/aborted-and-failed-execute-and-restore.json | NOMATCH "google:ubuntu-22.04-64:tests/test-4"
+ compare_tasks_count "$(log-analyzer list-reexecute-tasks google: data/aborted-and-failed-execute-and-restore.json)" 5
+
+ # When some tests failed to execute and the suite and project fails to restore, just the failed test is re-executed
+ log-analyzer list-reexecute-tasks google: data/with-failed-and-failed-restore-suite.json | MATCH "google:ubuntu-22.04-64:tests/test-2"
+ compare_tasks_count "$(log-analyzer list-reexecute-tasks google: data/with-failed-and-failed-restore-suite.json)" 1
+
+ ### CHECK ABORTED ###
+
+ compare_tasks_count "$(log-analyzer list-aborted-tasks google: data/all-aborted.json)" 10
+ compare_tasks_count "$(log-analyzer list-aborted-tasks google:ubuntu-20.04-64: data/all-aborted.json)" 5
+
+ compare_tasks_count "$(log-analyzer list-aborted-tasks google: data/all-success-failed-restore.json)" 0
+
+ compare_tasks_count "$(log-analyzer list-aborted-tasks google: data/all-success.json)" 0
+ compare_tasks_count "$(log-analyzer list-aborted-tasks google:ubuntu-20.04-64: data/all-success.json)" 0
+
+ compare_tasks_count "$(log-analyzer list-aborted-tasks google: data/all-failed.json)" 0
+
+ compare_tasks_count "$(log-analyzer list-aborted-tasks google: data/failed-prepare-project.json)" 5
+
+ compare_tasks_count "$(log-analyzer list-aborted-tasks google: data/failed-prepare-suite.json)" 5
+ compare_tasks_count "$(log-analyzer list-aborted-tasks google:ubuntu-20.04-64: data/failed-prepare-suite.json)" 5
+ compare_tasks_count "$(log-analyzer list-aborted-tasks google:ubuntu-22.04-64: data/failed-prepare-suite.json)" 0
+
+ compare_tasks_count "$(log-analyzer list-aborted-tasks google: data/with-aborted-and-failed-restore.json)" 8
+
+ compare_tasks_count "$(log-analyzer list-aborted-tasks google: data/failed-prepare-task.json)" 2
+
+ compare_tasks_count "$(log-analyzer list-aborted-tasks google: data/failed-prepare-and-restore.json)" 5
+
+ compare_tasks_count "$(log-analyzer list-aborted-tasks google: data/aborted-and-failed-execute-and-restore.json)" 3
+ compare_tasks_count "$(log-analyzer list-aborted-tasks google:ubuntu-20.04-64: data/aborted-and-failed-execute-and-restore.json)" 0
+ compare_tasks_count "$(log-analyzer list-aborted-tasks google:ubuntu-22.04-64: data/aborted-and-failed-execute-and-restore.json)" 3
+
+ ### CHECK SUCCESSFUL ###
+
+ compare_tasks_count "$(log-analyzer list-successful-tasks google: data/all-aborted.json)" 0
+
+ compare_tasks_count "$(log-analyzer list-successful-tasks google: data/all-success-failed-restore.json)" 10
+
+ compare_tasks_count "$(log-analyzer list-successful-tasks google: data/all-success.json)" 10
+ compare_tasks_count "$(log-analyzer list-successful-tasks google:ubuntu-20.04-64: data/all-success.json)" 5
+
+ compare_tasks_count "$(log-analyzer list-successful-tasks google: data/all-failed.json)" 0
+
+ compare_tasks_count "$(log-analyzer list-successful-tasks google: data/failed-prepare-project.json)" 5
+
+ compare_tasks_count "$(log-analyzer list-successful-tasks google: data/failed-prepare-suite.json)" 5
+ compare_tasks_count "$(log-analyzer list-successful-tasks google:ubuntu-22.04-64: data/failed-prepare-suite.json)" 5
+ compare_tasks_count "$(log-analyzer list-successful-tasks google:ubuntu-20.04-64: data/failed-prepare-suite.json)" 0
+
+ compare_tasks_count "$(log-analyzer list-successful-tasks google: data/with-aborted-and-failed-restore.json)" 0
+
+ compare_tasks_count "$(log-analyzer list-successful-tasks google: data/failed-prepare-task.json)" 8
+ compare_tasks_count "$(log-analyzer list-successful-tasks google:ubuntu-20.04-64: data/failed-prepare-task.json)" 4
+ compare_tasks_count "$(log-analyzer list-successful-tasks google:ubuntu-22.04-64: data/failed-prepare-task.json)" 4
+
+ compare_tasks_count "$(log-analyzer list-successful-tasks google: data/failed-prepare-and-restore.json)" 5
+
+ compare_tasks_count "$(log-analyzer list-successful-tasks google: data/aborted-and-failed-execute-and-restore.json)" 5
+ compare_tasks_count "$(log-analyzer list-successful-tasks google:ubuntu-20.04-64: data/aborted-and-failed-execute-and-restore.json)" 4
+ compare_tasks_count "$(log-analyzer list-successful-tasks google:ubuntu-22.04-64: data/aborted-and-failed-execute-and-restore.json)" 1
+
+ ### CHECK FAILED ###
+
+ compare_tasks_count "$(log-analyzer list-failed-tasks google: data/all-aborted.json)" 0
+
+ compare_tasks_count "$(log-analyzer list-failed-tasks google: data/all-success-failed-restore.json)" 0
+
+ compare_tasks_count "$(log-analyzer list-failed-tasks google: data/all-success.json)" 0
+
+ compare_tasks_count "$(log-analyzer list-failed-tasks google: data/all-failed.json)" 10
+ compare_tasks_count "$(log-analyzer list-failed-tasks google:ubuntu-20.04-64: data/all-failed.json)" 5
+
+ compare_tasks_count "$(log-analyzer list-failed-tasks google: data/failed-prepare-project.json)" 0
+
+ compare_tasks_count "$(log-analyzer list-failed-tasks google: data/failed-prepare-suite.json)" 0
+
+ compare_tasks_count "$(log-analyzer list-failed-tasks google: data/with-aborted-and-failed-restore.json)" 0
+
+ compare_tasks_count "$(log-analyzer list-failed-tasks google: data/failed-prepare-task.json)" 0
+
+ compare_tasks_count "$(log-analyzer list-failed-tasks google: data/failed-prepare-and-restore.json)" 0
+
+ compare_tasks_count "$(log-analyzer list-failed-tasks google: data/aborted-and-failed-execute-and-restore.json)" 2
+ compare_tasks_count "$(log-analyzer list-failed-tasks google:ubuntu-20.04-64: data/aborted-and-failed-execute-and-restore.json)" 1
+ compare_tasks_count "$(log-analyzer list-failed-tasks google:ubuntu-22.04-64: data/aborted-and-failed-execute-and-restore.json)" 1
+
+ ### CHECK EXECUTED ###
+
+ compare_tasks_count "$(log-analyzer list-executed-tasks google: data/all-aborted.json)" 0
+
+ compare_tasks_count "$(log-analyzer list-executed-tasks google: data/all-success-failed-restore.json)" 10
+
+ compare_tasks_count "$(log-analyzer list-executed-tasks google: data/all-success.json)" 10
+
+ compare_tasks_count "$(log-analyzer list-executed-tasks google: data/all-failed.json)" 10
+ compare_tasks_count "$(log-analyzer list-executed-tasks google:ubuntu-20.04-64: data/all-failed.json)" 5
+ compare_tasks_count "$(log-analyzer list-executed-tasks google:ubuntu-22.04-64: data/all-failed.json)" 5
+
+ compare_tasks_count "$(log-analyzer list-executed-tasks google: data/failed-prepare-project.json)" 5
+
+ compare_tasks_count "$(log-analyzer list-executed-tasks google: data/failed-prepare-suite.json)" 5
+
+ compare_tasks_count "$(log-analyzer list-executed-tasks google: data/with-aborted-and-failed-restore.json)" 2
+
+ compare_tasks_count "$(log-analyzer list-executed-tasks google: data/failed-prepare-task.json)" 8
+
+ compare_tasks_count "$(log-analyzer list-executed-tasks google: data/failed-prepare-and-restore.json)" 5
+
+ compare_tasks_count "$(log-analyzer list-executed-tasks google: data/aborted-and-failed-execute-and-restore.json)" 7
+ compare_tasks_count "$(log-analyzer list-executed-tasks google:ubuntu-20.04-64: data/aborted-and-failed-execute-and-restore.json)" 5
+ compare_tasks_count "$(log-analyzer list-executed-tasks google:ubuntu-22.04-64: data/aborted-and-failed-execute-and-restore.json)" 2
+
+ ### CHECK ALL ###
+ compare_tasks_count "$(log-analyzer list-all-tasks google:)" 10
+ compare_tasks_count "$(log-analyzer list-all-tasks 'google:ubuntu-20.04-64: google:ubuntu-22.04-64:')" 10
+ compare_tasks_count "$(log-analyzer list-all-tasks 'google:ubuntu-20.04-64:,google:ubuntu-22.04-64:')" 10
+ compare_tasks_count "$(log-analyzer list-all-tasks google:ubuntu-20.04-64:)" 5
+ compare_tasks_count "$(log-analyzer list-all-tasks google:ubuntu-22.04-64:)" 5
diff --git a/tests/lib/external/snapd-testing-tools/tests/log-analyzer/tests/test-1/task.yaml b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/tests/test-1/task.yaml
new file mode 100644
index 0000000..0bf4b7f
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/tests/test-1/task.yaml
@@ -0,0 +1,12 @@
+summary: summary
+
+details: test
+
+prepare: |
+ echo "prepare"
+
+restore: |
+ echo "restore"
+
+execute: |
+ echo "execute"
diff --git a/tests/lib/external/snapd-testing-tools/tests/log-analyzer/tests/test-2/task.yaml b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/tests/test-2/task.yaml
new file mode 100644
index 0000000..0bf4b7f
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/tests/test-2/task.yaml
@@ -0,0 +1,12 @@
+summary: summary
+
+details: test
+
+prepare: |
+ echo "prepare"
+
+restore: |
+ echo "restore"
+
+execute: |
+ echo "execute"
diff --git a/tests/lib/external/snapd-testing-tools/tests/log-analyzer/tests/test-3/task.yaml b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/tests/test-3/task.yaml
new file mode 100644
index 0000000..0bf4b7f
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/tests/test-3/task.yaml
@@ -0,0 +1,12 @@
+summary: summary
+
+details: test
+
+prepare: |
+ echo "prepare"
+
+restore: |
+ echo "restore"
+
+execute: |
+ echo "execute"
diff --git a/tests/lib/external/snapd-testing-tools/tests/log-analyzer/tests/test-4/task.yaml b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/tests/test-4/task.yaml
new file mode 100644
index 0000000..0bf4b7f
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/tests/test-4/task.yaml
@@ -0,0 +1,12 @@
+summary: summary
+
+details: test
+
+prepare: |
+ echo "prepare"
+
+restore: |
+ echo "restore"
+
+execute: |
+ echo "execute"
diff --git a/tests/lib/external/snapd-testing-tools/tests/log-analyzer/tests/test-5/task.yaml b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/tests/test-5/task.yaml
new file mode 100644
index 0000000..bb02fd7
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/log-analyzer/tests/test-5/task.yaml
@@ -0,0 +1,12 @@
+summary: summary
+
+details: test
+
+prepare: |
+ echo "prepare"
+
+restore: |
+ echo "restore"
+
+execute: |
+ echo "execute"
diff --git a/tests/lib/external/snapd-testing-tools/tests/log-filter/task.yaml b/tests/lib/external/snapd-testing-tools/tests/log-filter/task.yaml
new file mode 100644
index 0000000..9d93d37
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/log-filter/task.yaml
@@ -0,0 +1,51 @@
+summary: test for the log filter tool
+
+details: |
+ This test checks the log-filter tool is able to read the spread output
+ and filter the log following a set of rules. It is also checked that the
+ output of the log-filter tool is the same than spread.
+
+backends: [google]
+
+# Github actions agents are just running ubuntu jammy
+systems: [ubuntu-22.04-64]
+
+prepare: |
+ wget https://storage.googleapis.com/snapd-spread-tests/dependencies/spread-log-filter.tar.xz
+ tar -xf spread-log-filter.tar.xz
+
+restore: |
+ rm -rf spread-log-filter.tar.xz ./*.log
+
+execute: |
+ log-filter --help | MATCH 'usage: log-filter \[-h\] \[-o PATH\]'
+ log-filter -h | MATCH 'usage: log-filter \[-h\] \[-o PATH\]'
+
+ # Run the tool for a real log
+ # The output should:
+ # 1. the output should go to test.filtered.log file
+ # 2. exclude: Preparing, Restoring, Error, Warning
+ # 3. show: debug lines with ###DEBUG
+ # 4. show: failed tests for google-nested
+ log-filter -o test.filtered.log -e Error -e Restoring -e Preparing -f "Debug=###DEBUG" -f "Failed=google-nested:" < spread-log-filter.log > output.log
+
+ # Check the stdout is the same than the stdin
+ diff -Z spread-log-filter.log output.log
+
+ # Check exclude
+ test "$(grep -c Restoring test.filtered.log)" -eq 0
+ test "$(grep -c Preparing test.filtered.log)" -eq 0
+ test "$(grep -c Error test.filtered.log)" -eq 0
+
+ # Check the non excluded
+ test "$(grep -c 'Debug output' test.filtered.log)" -eq "$(grep -c 'Debug output' spread-log-filter.log)"
+
+ # Check the filters
+ test "$(grep -c '###DEBUG' test.filtered.log)" -eq "$(grep -c '###DEBUG' spread-log-filter.log)"
+ grep -A 1 'Failed suite prepare: 4' test.filtered.log | MATCH 'Failed suite restore: 1'
+
+ # Check no repeated
+ test "$(grep -c 'Failed suite prepare: 4' test.filtered.log)" -eq 1
+ test "$(grep -c 'Executing' test.filtered.log)" -eq "$(grep -c 'Executing' spread-log-filter.log)"
+ test "$(grep -c 'Aborted tasks' test.filtered.log)" -eq 1
+
diff --git a/tests/lib/external/snapd-testing-tools/tests/log-parser/all-aborted.log.spread b/tests/lib/external/snapd-testing-tools/tests/log-parser/all-aborted.log.spread
new file mode 100644
index 0000000..bb64b51
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/log-parser/all-aborted.log.spread
@@ -0,0 +1,46 @@
+2021-01-20 12:29:36 Project content is packed for delivery (265.87KB).
+2021-01-20 12:29:36 Sequence of jobs produced with -seed=1611145776
+2021-01-20 12:29:36 If killed, discard servers with: spread -reuse-pid=30187 -discard
+2021-01-20 12:29:36 Allocating google:ubuntu-21.04-64...
+2021-01-20 12:29:37 Allocating google:ubuntu-21.04-64...
+2021-01-20 12:29:37 Allocating google:ubuntu-21.04-64...
+2021-01-20 12:29:37 Allocating google:ubuntu-21.04-64...
+2021-01-20 12:29:37 Allocating google:ubuntu-21.04-64...
+2021-01-20 12:29:37 Allocating google:ubuntu-21.04-64...
+2021-01-20 12:29:37 Allocating google:ubuntu-21.04-64...
+2021-01-20 12:29:37 Allocating google:ubuntu-21.04-64...
+2021-01-20 12:29:50 Waiting for google:ubuntu-21.04-64 (jan201229-232006) to boot at 35.196.53.112...
+2021-01-20 12:29:51 Waiting for google:ubuntu-21.04-64 (jan201229-232290) to boot at 35.237.111.213...
+2021-01-20 12:29:52 Waiting for google:ubuntu-21.04-64 (jan201229-232308) to boot at 104.196.172.222...
+2021-01-20 12:29:52 Waiting for google:ubuntu-21.04-64 (jan201229-232314) to boot at 34.75.124.6...
+2021-01-20 12:29:52 Waiting for google:ubuntu-21.04-64 (jan201229-232320) to boot at 35.185.0.186...
+2021-01-20 12:29:56 Waiting for google:ubuntu-21.04-64 (jan201229-232297) to boot at 34.74.223.99...
+2021-01-20 12:29:56 Waiting for google:ubuntu-21.04-64 (jan201229-232303) to boot at 35.231.44.102...
+2021-01-20 12:30:00 Waiting for google:ubuntu-21.04-64 (jan201229-232278) to boot at 35.185.82.132...
+2021-01-20 12:30:50 Server google:ubuntu-21.04-64 (jan201229-232006) is taking a while to boot...
+2021-01-20 12:30:51 Server google:ubuntu-21.04-64 (jan201229-232290) is taking a while to boot...
+2021-01-20 12:30:52 Server google:ubuntu-21.04-64 (jan201229-232308) is taking a while to boot...
+2021-01-20 12:30:52 Server google:ubuntu-21.04-64 (jan201229-232314) is taking a while to boot...
+2021-01-20 12:30:52 Server google:ubuntu-21.04-64 (jan201229-232320) is taking a while to boot...
+2021-01-20 12:30:56 Server google:ubuntu-21.04-64 (jan201229-232297) is taking a while to boot...
+2021-01-20 12:30:56 Server google:ubuntu-21.04-64 (jan201229-232303) is taking a while to boot...
+2021-01-20 12:31:00 Server google:ubuntu-21.04-64 (jan201229-232278) is taking a while to boot...
+2021-01-20 12:31:50 Server google:ubuntu-21.04-64 (jan201229-232006) is taking a while to boot...
+2021-01-20 12:31:51 Server google:ubuntu-21.04-64 (jan201229-232290) is taking a while to boot...
+2021-01-20 12:31:52 Server google:ubuntu-21.04-64 (jan201229-232308) is taking a while to boot...
+2021-01-20 12:31:52 Server google:ubuntu-21.04-64 (jan201229-232320) is taking a while to boot...
+2021-01-20 12:31:53 Server google:ubuntu-21.04-64 (jan201229-232314) is taking a while to boot...
+2021-01-20 12:31:56 Server google:ubuntu-21.04-64 (jan201229-232297) is taking a while to boot...
+2021-01-20 12:31:56 Server google:ubuntu-21.04-64 (jan201229-232303) is taking a while to boot...
+2021-01-20 12:32:00 Server google:ubuntu-21.04-64 (jan201229-232278) is taking a while to boot...
+2021-01-20 12:32:51 Cannot allocate google:ubuntu-21.04-64: cannot allocate new Google server google:ubuntu-21.04-64 (jan201229-232006): cannot find ready marker in console output for google:ubuntu-21.04-64 (jan201229-232006)
+2021-01-20 12:32:51 Cannot allocate google:ubuntu-21.04-64: cannot allocate new Google server google:ubuntu-21.04-64 (jan201229-232290): cannot find ready marker in console output for google:ubuntu-21.04-64 (jan201229-232290)
+2021-01-20 12:32:52 Cannot allocate google:ubuntu-21.04-64: cannot allocate new Google server google:ubuntu-21.04-64 (jan201229-232308): cannot find ready marker in console output for google:ubuntu-21.04-64 (jan201229-232308)
+2021-01-20 12:32:52 Cannot allocate google:ubuntu-21.04-64: cannot allocate new Google server google:ubuntu-21.04-64 (jan201229-232314): cannot find ready marker in console output for google:ubuntu-21.04-64 (jan201229-232314)
+2021-01-20 12:32:53 Cannot allocate google:ubuntu-21.04-64: cannot allocate new Google server google:ubuntu-21.04-64 (jan201229-232320): cannot find ready marker in console output for google:ubuntu-21.04-64 (jan201229-232320)
+2021-01-20 12:32:56 Cannot allocate google:ubuntu-21.04-64: cannot allocate new Google server google:ubuntu-21.04-64 (jan201229-232297): cannot find ready marker in console output for google:ubuntu-21.04-64 (jan201229-232297)
+2021-01-20 12:32:57 Cannot allocate google:ubuntu-21.04-64: cannot allocate new Google server google:ubuntu-21.04-64 (jan201229-232303): cannot find ready marker in console output for google:ubuntu-21.04-64 (jan201229-232303)
+error: unsuccessful run
+2021-01-20 12:33:01 Cannot allocate google:ubuntu-21.04-64: cannot allocate new Google server google:ubuntu-21.04-64 (jan201229-232278): cannot find ready marker in console output for google:ubuntu-21.04-64 (jan201229-232278)
+2021-01-20 12:33:01 Successful tasks: 0
+2021-01-20 12:33:01 Aborted tasks: 505
\ No newline at end of file
diff --git a/tests/lib/external/snapd-testing-tools/tests/log-parser/all-successful.log.spread b/tests/lib/external/snapd-testing-tools/tests/log-parser/all-successful.log.spread
new file mode 100644
index 0000000..c736ff6
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/log-parser/all-successful.log.spread
@@ -0,0 +1,112 @@
+2021-01-20 12:28:57 Project content is packed for delivery (266.41KB).
+2021-01-20 12:28:57 Sequence of jobs produced with -seed=1611145737
+2021-01-20 12:28:57 If killed, discard servers with: spread -reuse-pid=24265 -discard
+2021-01-20 12:28:57 Allocating google:centos-8-64...
+2021-01-20 12:29:10 Waiting for google:centos-8-64 (jan201228-089626) to boot at 35.229.96.196...
+2021-01-20 12:29:33 Sending project content to google:centos-8-64 (jan201228-089626)...
+2021-01-20 12:29:33 Connected to google:centos-8-64 (jan201228-089359) at 34.73.106.247.
+2021-01-20 12:29:33 Sending project content to google:centos-8-64 (jan201228-089359)...
+2021-01-20 12:29:33 Connected to google:centos-8-64 (jan201228-089636) at 35.227.44.209.
+2021-01-20 12:29:33 Sending project content to google:centos-8-64 (jan201228-089636)...
+2021-01-20 12:29:35 Preparing google:centos-8-64 (jan201228-089626)...
+2021-01-20 12:29:35 Preparing google:centos-8-64 (jan201228-089596)...
+2021-01-20 12:29:35 Preparing google:centos-8-64 (jan201228-089359)...
+2021-01-20 12:29:35 Preparing google:centos-8-64 (jan201228-089636)...
+2021-01-20 12:34:36 WARNING: jan201228-089636 (google:centos-8-64) running late. Current output:
+-----
+(... 376 lines above ...)
+pkgconfig(libseccomp)
+pkgconfig(libselinux)
+pkgconfig(libudev)
+pkgconfig(systemd)
+pkgconfig(udev)
+selinux-policy
+selinux-policy-devel
+systemd
+valgrind
+xfsprogs-devel
+-----
+.
+2021-01-20 12:42:47 Restoring google:centos-8-64:tests/completion/snippets:hosts (jan201228-089636)...
+2021-01-20 12:42:48 Preparing google:centos-8-64:tests/completion/indirect:func (jan201228-089636)...
+2021-01-20 12:42:50 Preparing google:centos-8-64:tests/regression/lp-1844496 (jan201228-089359)...
+2021-01-20 12:42:54 Executing google:centos-8-64:tests/completion/indirect:func (jan201228-089636) (2/435)...
+2021-01-20 12:42:55 Preparing google:centos-8-64:tests/main/degraded (jan201228-089626)...
+2021-01-20 12:43:01 Executing google:centos-8-64:tests/main/degraded (jan201228-089626) (3/435)...
+2021-01-20 12:43:01 Restoring google:centos-8-64:tests/main/degraded (jan201228-089626)...
+2021-01-20 12:43:02 Preparing google:centos-8-64:tests/main/base-snaps (jan201228-089626)...
+2021-01-20 12:43:03 Executing google:centos-8-64:tests/regression/lp-1844496 (jan201228-089359) (4/435)...
+2021-01-20 12:43:06 Executing google:centos-8-64:tests/main/base-snaps (jan201228-089626) (5/435)...
+2021-01-20 12:43:07 Restoring google:centos-8-64:tests/regression/lp-1844496 (jan201228-089359)...
+2021-01-20 12:43:09 Preparing google:centos-8-64:tests/regression/lp-1812973 (jan201228-089359)...
+2021-01-20 12:43:15 Executing google:centos-8-64:tests/regression/lp-1812973 (jan201228-089359) (6/435)...
+2021-01-20 12:43:16 Restoring google:centos-8-64:tests/regression/lp-1812973 (jan201228-089359)...
+2021-01-20 12:43:16 Restoring google:centos-8-64:tests/main/base-snaps (jan201228-089626)...
+2021-01-20 12:43:17 Preparing google:centos-8-64:tests/main/snap-validate-basic (jan201228-089626)...
+2021-01-20 12:43:18 Preparing google:centos-8-64:tests/regression/lp-1665004 (jan201228-089359)...
+2021-01-20 12:43:22 Executing google:centos-8-64:tests/main/snap-validate-basic (jan201228-089626) (7/435)...
+2021-01-20 12:43:22 Restoring google:centos-8-64:tests/main/snap-validate-basic (jan201228-089626)...
+2021-01-20 12:43:23 Preparing google:centos-8-64:tests/main/interfaces-broadcom-asic-control (jan201228-089596)...
+2021-01-20 12:43:23 Preparing google:centos-8-64:tests/main/media-sharing (jan201228-089626)...
+2021-01-20 12:43:24 Executing google:centos-8-64:tests/regression/lp-1665004 (jan201228-089359) (8/435)...
+2021-01-20 12:43:24 Restoring google:centos-8-64:tests/regression/lp-1665004 (jan201228-089359)...
+2021-01-20 12:43:25 Preparing google:centos-8-64:tests/regression/lp-1797556 (jan201228-089359)...
+2021-01-20 12:43:30 Executing google:centos-8-64:tests/main/interfaces-broadcom-asic-control (jan201228-089596) (9/435)...
+2021-01-20 12:43:32 Executing google:centos-8-64:tests/regression/lp-1797556 (jan201228-089359) (10/435)...
+2021-01-20 12:43:32 Restoring google:centos-8-64:tests/regression/lp-1797556 (jan201228-089359)...
+2021-01-20 12:43:32 Restoring google:centos-8-64:tests/main/interfaces-broadcom-asic-control (jan201228-089596)...
+2021-01-20 12:48:55 Restoring google:centos-8-64:tests/lib/tools/suite/mountinfo.query (jan201228-089359)...
+2021-01-20 12:48:56 Preparing google:centos-8-64:tests/lib/tools/suite/tests.pkgs (jan201228-089359)...
+2021-01-20 12:48:57 Executing google:centos-8-64:tests/lib/tools/suite/tests.pkgs (jan201228-089359) (90/435)...
+2021-01-20 12:48:58 Executing google:centos-8-64:tests/main/security-devpts (jan201228-089596) (91/435)...
+2021-01-20 12:49:00 Restoring google:centos-8-64:tests/main/security-devpts (jan201228-089596)...
+2021-01-20 12:49:01 Preparing google:centos-8-64:tests/main/interfaces-content-circular (jan201228-089596)...
+2021-01-20 12:49:06 Executing google:centos-8-64:tests/main/interfaces-content-circular (jan201228-089596) (92/435)...
+2021-01-20 12:49:11 Restoring google:centos-8-64:tests/completion/indirect:funcarg (jan201228-089636)...
+2021-01-20 12:49:13 Preparing google:centos-8-64:tests/completion/simple:hosts (jan201228-089636)...
+2021-01-20 12:49:17 Restoring google:centos-8-64:tests/main/services-snapctl (jan201228-089626)...
+2021-01-20 12:49:17 Executing google:centos-8-64:tests/completion/simple:hosts (jan201228-089636) (93/435)...
+2021-01-20 12:49:17 Preparing google:centos-8-64:tests/main/services-refresh-mode (jan201228-089626)...
+2021-01-20 12:49:22 Discarding google:centos-8-64 (jan201228-089359)...
+2021-01-20 12:49:24 Restoring google:centos-8-64:tests/completion/simple:hosts (jan201228-089636)...
+2021-01-20 12:49:24 Preparing google:centos-8-64:tests/completion/indirect:hosts_n_dirs (jan201228-089636)...
+2021-01-20 12:49:30 Executing google:centos-8-64:tests/completion/indirect:hosts_n_dirs (jan201228-089636) (94/435)...
+2021-01-20 12:49:36 Executing google:centos-8-64:tests/main/services-refresh-mode (jan201228-089626) (95/435)...
+2021-01-20 12:49:42 Restoring google:centos-8-64:tests/main/interfaces-content-circular (jan201228-089596)...
+2021-01-20 12:49:43 Preparing google:centos-8-64:tests/main/refresh:strict_remote (jan201228-089596)...
+2021-01-20 12:53:24 Preparing google:centos-8-64:tests/main/base-snaps-refresh (jan201228-089626)...
+2021-01-20 12:53:29 Executing google:centos-8-64:tests/main/base-snaps-refresh (jan201228-089626) (124/435)...
+2021-01-20 12:53:38 Restoring google:centos-8-64:tests/main/base-snaps-refresh (jan201228-089626)...
+2021-01-20 12:53:38 Restoring google:centos-8-64:tests/main/snapd-snap-removal (jan201228-089596)...
+2021-01-20 12:53:39 Preparing google:centos-8-64:tests/main/interfaces-daemon-notify (jan201228-089626)...
+2021-01-20 12:53:39 Preparing google:centos-8-64:tests/main/interfaces-content-mkdir-writable:data (jan201228-089596)...
+2021-01-20 12:53:45 Executing google:centos-8-64:tests/main/interfaces-daemon-notify (jan201228-089626) (125/435)...
+2021-01-20 12:53:46 Executing google:centos-8-64:tests/main/interfaces-content-mkdir-writable:data (jan201228-089596) (126/435)...
+2021-01-20 12:53:48 Restoring google:centos-8-64:tests/main/interfaces-content-mkdir-writable:data (jan201228-089596)...
+2021-01-20 12:53:49 Preparing google:centos-8-64:tests/main/refresh:parallel_strict_remote (jan201228-089596)...
+2021-01-20 12:53:56 Restoring google:centos-8-64:tests/completion/indirect:plain_plusdirs (jan201228-089636)...
+2021-01-20 12:53:57 Preparing google:centos-8-64:tests/completion/simple:hosts_n_dirs (jan201228-089636)...
+2021-01-20 12:53:57 Restoring google:centos-8-64:tests/main/interfaces-daemon-notify (jan201228-089626)...
+2021-01-20 12:53:58 Preparing google:centos-8-64:tests/main/core18-configure-hook (jan201228-089626)...
+2021-01-20 12:54:01 Executing google:centos-8-64:tests/main/refresh:parallel_strict_remote (jan201228-089596) (127/435)...
+2021-01-20 12:54:02 Executing google:centos-8-64:tests/completion/simple:hosts_n_dirs (jan201228-089636) (128/435)...
+2021-01-20 12:59:22 Preparing google:centos-8-64:tests/main/interfaces-fuse-support:regular (jan201228-089596)...
+2021-01-20 13:29:28 Restoring google:centos-8-64:tests/main/core18-with-hooks (jan201228-089626)...
+2021-01-20 13:29:31 Preparing google:centos-8-64:tests/main/proxy (jan201228-089626)...
+2021-01-20 13:29:32 Executing google:centos-8-64:tests/main/services-watchdog (jan201228-089596) (434/435)...
+2021-01-20 13:29:36 Executing google:centos-8-64:tests/main/proxy (jan201228-089626) (435/435)...
+2021-01-20 13:29:38 Restoring google:centos-8-64:tests/main/proxy (jan201228-089626)...
+2021-01-20 13:29:39 Restoring google:centos-8-64:tests/main/services-watchdog (jan201228-089596)...
+2021-01-20 13:29:39 Restoring google:centos-8-64:tests/main/ (jan201228-089626)...
+2021-01-20 13:29:40 Restoring google:centos-8-64:tests/main/ (jan201228-089596)...
+2021-01-20 13:29:46 Restoring google:centos-8-64 (jan201228-089626)...
+2021-01-20 13:29:47 Restoring google:centos-8-64 (jan201228-089596)...
+2021-01-20 13:29:48 Discarding google:centos-8-64 (jan201228-089626)...
+2021-01-20 13:29:49 Discarding google:centos-8-64 (jan201228-089596)...
+2021-01-20 13:30:03 Restoring google:centos-8-64:tests/main/auto-refresh-retry (jan201228-089636)...
+2021-01-20 13:30:04 Restoring google:centos-8-64:tests/main/ (jan201228-089636)...
+2021-01-20 13:30:11 Restoring google:centos-8-64 (jan201228-089636)...
+2021-01-20 13:30:12 Discarding google:centos-8-64 (jan201228-089636)...
+error: unsuccessful run
+2021-01-20 13:30:13 Successful tasks: 434
+2021-01-20 13:30:13 Aborted tasks: 0
\ No newline at end of file
diff --git a/tests/lib/external/snapd-testing-tools/tests/log-parser/task.yaml b/tests/lib/external/snapd-testing-tools/tests/log-parser/task.yaml
new file mode 100644
index 0000000..39b9b4c
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/log-parser/task.yaml
@@ -0,0 +1,120 @@
+summary: test for the log parser tool
+
+details: |
+ This test checks the log-parser tool is able to read different spread
+ logs and produce a json file with the logs info. This json file contains
+ all the information from the log but structured in json format.
+
+backends: [google]
+
+# Github actions agents are just running ubuntu jammy
+systems: [ubuntu-22.04-64]
+
+execute: |
+ log-parser --help | MATCH 'usage: log-parser \[-h\] \[-c CUT\]'
+ log-parser -h | MATCH 'usage: log-parser \[-h\] \[-c CUT\]'
+
+ # Check results when the log just contains successful tests
+ log-parser all-successful.log.spread
+ TESTS_SUCCESSFUL=$(jq -r '.[] | select( .type == "result") | select( .result_type == "Successful") | .number' spread-results.json)
+ test "$TESTS_SUCCESSFUL" = "434"
+
+ # Check results when the log contains failed tests
+ log-parser with-failed.log.spread
+ TESTS_SUCCESSFUL=$(jq -r '.[] | select( .type == "result") | select( .result_type == "Successful") | .number' spread-results.json)
+ TESTS_FAILED=$(jq -r '.[] | select( .type == "result") | select( .result_type == "Failed") | .number' spread-results.json)
+ test "$TESTS_SUCCESSFUL" = "434"
+ test "$TESTS_FAILED" = "1"
+
+ # Check info when the log contains failed tests
+ jq -r '.[] | select( .type == "info") | select( .info_type == "Error") | .verb' spread-results.json | MATCH 'executing'
+ jq -r '.[] | select( .type == "info") | select( .info_type == "Error") | .task' spread-results.json | MATCH 'google:centos-8-64:tests/lib/tools/suite/tests.pkgs'
+ jq -r '.[] | select( .type == "info") | select( .info_type == "Debug") | .verb' spread-results.json | MATCH 'null'
+ jq -r '.[] | select( .type == "info") | select( .info_type == "Debug") | .task' spread-results.json | MATCH 'google:centos-8-64:tests/lib/tools/suite/tests.pkgs'
+
+ # Check results when the log contains failed tests
+ log-parser all-aborted.log.spread
+ TESTS_SUCCESSFUL=$(jq -r '.[] | select( .type == "result") | select( .result_type == "Successful") | .number' spread-results.json)
+ TESTS_FAILED=$(jq -r '.[] | select( .type == "result") | select( .result_type == "Failed") | .number' spread-results.json)
+ TESTS_ABORTED=$(jq -r '.[] | select( .type == "result") | select( .result_type == "Aborted") | .number' spread-results.json)
+ test "$TESTS_SUCCESSFUL" = "0"
+ test -z "$TESTS_FAILED"
+ test "$TESTS_ABORTED" = "505"
+
+ # Check results when the log contains aborted, failed and successful TESTS
+ log-parser with-failed-and-aborted.log.spread
+ TESTS_SUCCESSFUL=$(jq -r '.[] | select( .type == "result") | select( .result_type == "Successful") | .number' spread-results.json)
+ TESTS_FAILED_TASKS=$(jq -r '.[] | select( .type == "result") | select( .result_type == "Failed") | select(.level == "tasks") | .number' spread-results.json)
+ TESTS_FAILED_TASK=$(jq -r '.[] | select( .type == "result") | select( .result_type == "Failed") | select(.level == "task") | .number' spread-results.json)
+ TESTS_FAILED_PROJECT=$(jq -r '.[] | select( .type == "result") | select( .result_type == "Failed") | select(.level == "project") | .number' spread-results.json)
+ TESTS_ABORTED=$(jq -r '.[] | select( .type == "result") | select( .result_type == "Aborted") | .number' spread-results.json)
+ test "$TESTS_SUCCESSFUL" = "400"
+ test "$TESTS_FAILED_TASKS" = "1"
+ test "$TESTS_FAILED_TASK" = "1"
+ test "$TESTS_FAILED_PROJECT" = "4"
+ test "$TESTS_ABORTED" = "24"
+
+ # Check results with all the failed levels
+ log-parser with-all-results.log.spread
+ TESTS_SUCCESSFUL=$(jq -r '.[] | select( .type == "result") | select( .result_type == "Successful") | .number' spread-results.json)
+ TESTS_FAILED_TASKS=$(jq -r '.[] | select( .type == "result") | select( .result_type == "Failed") | select(.level == "tasks") | .number' spread-results.json)
+ TESTS_FAILED_TASK_PREPARE=$(jq -r '.[] | select( .type == "result") | select( .result_type == "Failed") | select(.level == "task") | select(.stage == "prepare") | .number' spread-results.json)
+ TESTS_FAILED_TASK_RESTORE=$(jq -r '.[] | select( .type == "result") | select( .result_type == "Failed") | select(.level == "task") | select(.stage == "restore") | .number' spread-results.json)
+ TESTS_FAILED_SUITE_PREPARE=$(jq -r '.[] | select( .type == "result") | select( .result_type == "Failed") | select(.level == "suite") | select(.stage == "prepare") | .number' spread-results.json)
+ TESTS_FAILED_SUITE_RESTORE=$(jq -r '.[] | select( .type == "result") | select( .result_type == "Failed") | select(.level == "suite") | select(.stage == "restore") | .number' spread-results.json)
+ TESTS_FAILED_PROJECT_PREPARE=$(jq -r '.[] | select( .type == "result") | select( .result_type == "Failed") | select(.level == "project") | select(.stage == "prepare") | .number' spread-results.json)
+ TESTS_FAILED_PROJECT_RESTORE=$(jq -r '.[] | select( .type == "result") | select( .result_type == "Failed") | select(.level == "project") | select(.stage == "restore") | .number' spread-results.json)
+ TESTS_ABORTED=$(jq -r '.[] | select( .type == "result") | select( .result_type == "Aborted") | .number' spread-results.json)
+ test "$TESTS_SUCCESSFUL" = "7376"
+ test "$TESTS_FAILED_TASKS" = "2"
+ test "$TESTS_FAILED_TASK_PREPARE" = "3"
+ test "$TESTS_FAILED_TASK_RESTORE" = "2"
+ test "$TESTS_FAILED_SUITE_PREPARE" = "1"
+ test "$TESTS_FAILED_SUITE_RESTORE" = "2"
+ test "$TESTS_FAILED_PROJECT_PREPARE" = "1"
+ test "$TESTS_FAILED_PROJECT_RESTORE" = "2"
+ test "$TESTS_ABORTED" = "3061"
+
+ # Check results when a detail contains results
+ log-parser with-results-in-detail.log.spread
+ TESTS_SUCCESSFUL=$(jq -r '.[] | select( .type == "result") | select( .result_type == "Successful") | .number' spread-results.json)
+ TESTS_FAILED_TASKS=$(jq -r '.[] | select( .type == "result") | select( .result_type == "Failed") | select(.level == "tasks") | .number' spread-results.json)
+ TESTS_ABORTED=$(jq -r '.[] | select( .type == "result") | select( .result_type == "Aborted") | .number' spread-results.json)
+ test "$TESTS_SUCCESSFUL" = "7376"
+ test "$TESTS_FAILED_TASKS" = "2"
+ test "$TESTS_ABORTED" = "3061"
+ jq -r '.[] | select( .type == "result") | select( .result_type == "Failed") | select(.level == "tasks") | .detail' spread-results.json | NOMATCH "cloud-init-never-used-not-vuln"
+ jq -r '.[] | select( .type == "result") | select( .result_type == "Failed") | select(.level == "tasks") | .detail' spread-results.json | MATCH "degraded"
+
+ # Check the filter
+ test "$(log-parser with-failed-and-aborted.log.spread | grep -c "Error preparing")" -eq 5
+ test "$(log-parser with-failed-and-aborted.log.spread | grep -c "command -v restorecon")" -eq 4
+
+ # Check the filter cutting the logs
+ test "$(log-parser with-failed-and-aborted.log.spread | wc -l)" -eq 261
+ test "$(log-parser with-failed-and-aborted.log.spread -c 0 | wc -l)" -eq 77
+ test "$(log-parser with-failed-and-aborted.log.spread -c 0 | grep -c "Error preparing")" -eq 5
+ test "$(log-parser with-failed-and-aborted.log.spread -c 0 | grep -c "")" -eq 0
+ test "$(log-parser with-failed-and-aborted.log.spread -c 2 | wc -l)" -eq 91
+ test "$(log-parser with-failed-and-aborted.log.spread -c 2 | grep -c "Error preparing")" -eq 5
+ test "$(log-parser with-failed-and-aborted.log.spread -c 2 | grep -c "")" -eq 5
+ test "$(log-parser with-failed-and-aborted.log.spread -c 3 | wc -l)" -eq 98
+ test "$(log-parser with-failed-and-aborted.log.spread -c 3 | grep -c "Error preparing")" -eq 5
+ test "$(log-parser with-failed-and-aborted.log.spread -c 3 | grep -c "")" -eq 5
+
+ # Check the results can be filtered
+ log-parser with-failed-and-aborted.log.spread | grep -q "Failed tasks: 1"
+ log-parser with-failed-and-aborted.log.spread | grep -c "Failed project prepare: 4"
+ log-parser with-failed-and-aborted.log.spread | grep -c "Aborted tasks: 24"
+ log-parser all-successful.log.spread | grep -q "Successful tasks: 434"
+ log-parser all-successful.log.spread | grep -v "Aborted tasks"
+
+ # Check filtering the debug output
+ log-parser with-debug-output.log.spread -c 1 -dr "executed_tests=EXECUTED_TESTS=(.*)" -dr "apparmor_version=AppArmor parser version .*"
+ test "$(jq -r '.[0].detail.executed_tests[0]' spread-results.json | wc -w)" -eq 44
+ test "$(jq -r '.[0].detail.apparmor_version[0]' spread-results.json)" = "AppArmor parser version 2.13.3"
+
+ # Check filtering the error output
+ log-parser with-error-output.log.spread -c 1 -er "executed_tests=EXECUTED_TESTS=(.*)" -er "apparmor_version=AppArmor parser version .*"
+ test "$(jq -r '.[0].detail.executed_tests[0]' spread-results.json | wc -w)" -eq 44
+ test "$(jq -r '.[0].detail.apparmor_version[0]' spread-results.json)" = "AppArmor parser version 2.13.3"
diff --git a/tests/lib/external/snapd-testing-tools/tests/log-parser/with-all-results.log.spread b/tests/lib/external/snapd-testing-tools/tests/log-parser/with-all-results.log.spread
new file mode 100644
index 0000000..933cccb
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/log-parser/with-all-results.log.spread
@@ -0,0 +1,162 @@
+2022-05-18 17:49:24 Project content is packed for delivery (8.86MB).
+2022-05-18 17:49:24 Sequence of jobs produced with -seed=1652896164
+2022-05-18 17:49:24 If killed, discard servers with: spread -reuse-pid=9308 -discard
+2022-05-18 17:49:24 Allocating google-nested:ubuntu-16.04-64...
+2022-05-18 17:49:24 Allocating google-nested:ubuntu-16.04-64...
+2022-05-18 17:49:24 Allocating google-nested:ubuntu-16.04-64...
+2022-05-18 17:49:37 Waiting for google-nested:ubuntu-16.04-64 (may181749-432987) to boot at 104.196.14.127...
+2022-05-18 17:49:37 Waiting for google-nested:ubuntu-16.04-64 (may181749-433163) to boot at 35.237.37.145...
+2022-05-18 17:49:47 Waiting for google-nested:ubuntu-16.04-64 (may181749-433044) to boot at 34.148.64.110...
+2022-05-18 17:50:03 Allocated google-nested:ubuntu-16.04-64 (may181749-432987).
+2022-05-18 17:50:03 Connecting to google-nested:ubuntu-16.04-64 (may181749-432987)...
+2022-05-18 17:50:04 Connected to google-nested:ubuntu-16.04-64 (may181749-432987) at 104.196.14.127.
+2022-05-18 17:50:04 Sending project content to google-nested:ubuntu-16.04-64 (may181749-432987)...
+2022-05-18 17:50:06 Preparing google-nested:ubuntu-16.04-64 (may181749-432987)...
+2022-05-18 17:50:08 Allocated google-nested:ubuntu-16.04-64 (may181749-433163).
+2022-05-18 17:50:08 Connecting to google-nested:ubuntu-16.04-64 (may181749-433163)...
+2022-05-18 17:50:09 Connected to google-nested:ubuntu-16.04-64 (may181749-433163) at 35.237.37.145.
+2022-05-18 17:50:09 Sending project content to google-nested:ubuntu-16.04-64 (may181749-433163)...
+2022-05-18 17:50:11 Preparing google-nested:ubuntu-16.04-64 (may181749-433163)...
+2022-05-18 17:50:13 Allocated google-nested:ubuntu-16.04-64 (may181749-433044).
+2022-05-18 17:50:13 Connecting to google-nested:ubuntu-16.04-64 (may181749-433044)...
+2022-05-18 17:50:13 Connected to google-nested:ubuntu-16.04-64 (may181749-433044) at 34.148.64.110.
+2022-05-18 17:50:13 Sending project content to google-nested:ubuntu-16.04-64 (may181749-433044)...
+2022-05-18 17:50:15 Preparing google-nested:ubuntu-16.04-64 (may181749-433044)...
+2022-05-18 17:55:06 WARNING: may181749-432987 (google-nested:ubuntu-16.04-64) running late. Current output:
+-----
+(... 365 lines above ...)
++ restart_logind=
++ loginctl enable-linger test
++ loginctl disable-linger test
+Unpacking systemd (229-4ubuntu21.31) over (229-4ubuntu21.28) ...
+Processing triggers for dbus (1.10.6-1ubuntu3.6) ...
+Processing triggers for ureadahead (0.100.0-19.1) ...
+Processing triggers for man-db (2.7.5-1) ...
+Setting up systemd (229-4ubuntu21.31) ...
+addgroup: The group `systemd-journal' already exists as a system group. Exiting.
+[/usr/lib/tmpfiles.d/var.conf:14] Duplicate line for path "/var/log", ignoring.
+Setting up libpam-systemd:amd64 (229-4ubuntu21.31) ...
+-----
+.
+2022-05-18 18:00:06 WARNING: may181749-432987 (google-nested:ubuntu-16.04-64) running late. Current output:
+-----
+(... 1334 lines above ...)
+dpkg-gencontrol: warning: File::FcntlLock not available; using flock which is not NFS-safe
+dpkg-gencontrol: warning: File::FcntlLock not available; using flock which is not NFS-safe
+dpkg-gencontrol: warning: File::FcntlLock not available; using flock which is not NFS-safe
+dpkg-gencontrol: warning: File::FcntlLock not available; using flock which is not NFS-safe
+make[1]: Leaving directory '/home/gopath/src/github.com/snapcore/snapd'
+ dh_md5sums -O--buildsystem=golang -O--fail-missing -O--builddirectory=_build
+ dh_builddeb -u-Zxz -O--buildsystem=golang -O--fail-missing -O--builddirectory=_build
+dpkg-deb: building package 'golang-github-ubuntu-core-snappy-dev' in '../golang-github-ubuntu-core-snappy-dev_1337.2.55.5_all.deb'.
+dpkg-deb: building package 'golang-github-snapcore-snapd-dev' in '../golang-github-snapcore-snapd-dev_1337.2.55.5_all.deb'.
+dpkg-deb: building package 'snapd' in '../snapd_1337.2.55.5_amd64.deb'.
+-----
+.
+2022-05-18 18:00:11 WARNING: may181749-433163 (google-nested:ubuntu-16.04-64) running late. Current output:
+-----
+(... 1334 lines above ...)
+dpkg-gencontrol: warning: File::FcntlLock not available; using flock which is not NFS-safe
+dpkg-gencontrol: warning: File::FcntlLock not available; using flock which is not NFS-safe
+dpkg-gencontrol: warning: File::FcntlLock not available; using flock which is not NFS-safe
+dpkg-gencontrol: warning: File::FcntlLock not available; using flock which is not NFS-safe
+make[1]: Leaving directory '/home/gopath/src/github.com/snapcore/snapd'
+ dh_md5sums -O--buildsystem=golang -O--fail-missing -O--builddirectory=_build
+ dh_builddeb -u-Zxz -O--buildsystem=golang -O--fail-missing -O--builddirectory=_build
+dpkg-deb: building package 'golang-github-ubuntu-core-snappy-dev' in '../golang-github-ubuntu-core-snappy-dev_1337.2.55.5_all.deb'.
+dpkg-deb: building package 'golang-github-snapcore-snapd-dev' in '../golang-github-snapcore-snapd-dev_1337.2.55.5_all.deb'.
+dpkg-deb: building package 'snapd' in '../snapd_1337.2.55.5_amd64.deb'.
+-----
+.
+2022-05-18 18:00:16 WARNING: may181749-433044 (google-nested:ubuntu-16.04-64) running late. Output unchanged.
+2022-05-18 18:01:03 Preparing google-nested:ubuntu-16.04-64:tests/nested/core/ (may181749-432987)...
+2022-05-18 18:01:17 Preparing google-nested:ubuntu-16.04-64:tests/nested/manual/ (may181749-433163)...
+2022-05-18 18:03:19 Preparing google-nested:ubuntu-16.04-64:tests/nested/manual/cloud-init-never-used-not-vuln:firstboot (may181749-433163)...
+2022-05-18 18:05:16 WARNING: may181749-433044 (google-nested:ubuntu-16.04-64) running late. Output unchanged.
+2022-05-18 18:06:03 WARNING: may181749-432987 (google-nested:ubuntu-16.04-64:tests/nested/core/) running late. Current output:
+-----
+(... 97 lines above ...)
+Parallel unsquashfs: Using 2 processors
+11398 inodes (13347 blocks) to write
+
+[===========================================================\] 13347/13347 100%
+
+created 8997 files
+created 1461 directories
+created 2312 symlinks
+created 79 devices
+created 0 fifos
+-----
+.
+2022-05-18 18:06:52 Executing google-nested:ubuntu-16.04-64:tests/nested/manual/cloud-init-never-used-not-vuln:firstboot (may181749-433163) (1/13)...
+2022-05-18 18:06:58 Preparing google-nested:ubuntu-16.04-64:tests/nested/core/image-build (may181749-432987)...
+2022-05-18 18:07:57 Restoring google-nested:ubuntu-16.04-64:tests/nested/manual/cloud-init-never-used-not-vuln:firstboot (may181749-433163)...
+2022-05-18 18:08:00 Preparing google-nested:ubuntu-16.04-64:tests/nested/manual/cloud-init-nocloud-not-vuln:firstboot (may181749-433163)...
+2022-05-18 18:08:40 Executing google-nested:ubuntu-16.04-64:tests/nested/core/image-build (may181749-432987) (2/13)...
+2022-05-18 18:09:34 Error preparing google-nested:ubuntu-16.04-64:tests/nested/manual/cloud-init-nocloud-not-vuln:firstboot (may181749-433163) :
+-----
++ /home/gopath/src/github.com/snapcore/snapd/tests/lib/prepare-restore.sh --prepare-project-each
++ set -e
++ . /home/gopath/src/github.com/snapcore/snapd/tests/lib/quiet.sh
++ . /home/gopath/src/github.com/snapcore/snapd/tests/lib/pkgdb.sh
+++ . /home/gopath/src/github.com/snapcore/snapd/tests/lib/quiet.sh
++ . /home/gopath/src/github.com/snapcore/snapd/tests/lib/random.sh
++ . /home/gopath/src/github.com/snapcore/snapd/tests/lib/state.sh
+++ SNAPD_STATE_PATH=/var/tmp/snapd-tools/snapd-state
+++ SNAPD_STATE_FILE=/var/tmp/snapd-tools/snapd-state/snapd-state.tar
+++ RUNTIME_STATE_PATH=/var/tmp/snapd-tools/runtime-state
+++ SNAPD_ACTIVE_UNITS=/var/tmp/snapd-tools/runtime-state/snapd-active-units
+++ . /home/gopath/src/github.com/snapcore/snapd/tests/lib/systemd.sh
++ case "$1" in
++ prepare_project_each
++ dmesg -c
++ fixup_dev_random
++ mv /dev/random /dev/random.orig
++ mknod /dev/random c 1 9
++ kill_gpg_agent
++ pkill -9 -e gpg-agent
+
+May 18 18:50:05 may181749-433044 kernel: veth33879c75: renamed from physLGGOmx
+May 18 18:50:05 may181749-433044 libvirtd[21899]: Failed to open file '/sys/class/net/eth0/operstate': No such file or directory
+May 18 18:50:05 may181749-433044 libvirtd[21899]: unable to read: /sys/class/net/eth0/operstate: No such file or directory
+May 18 18:50:05 may181749-433044 kernel: device veth690dd0d2 left promiscuous mode
+May 18 18:50:05 may181749-433044 kernel: lxdbr0: port 1(veth690dd0d2) entered disabled state
+May 18 18:50:05 may181749-433044 audit[20780]: AVC apparmor="STATUS" operation="profile_remove" profile="unconfined" name="lxd-snapcraft-snapd_" pid=20780 comm="apparmor_parser"
+May 18 18:50:05 may181749-433044 kernel: audit: type=1400 audit(1652899805.951:102): apparmor="STATUS" operation="profile_remove" profile="unconfined" name="lxd-snapcraft-snapd_" pid=20780 comm="apparmor_parser"
+May 18 18:50:07 may181749-433044 ntpd[1698]: Deleting interface #8 lxdbr0, 10.253.239.1#123, interface stats: received=0, sent=0, dropped=0, active_time=851 secs
+May 18 18:50:07 may181749-433044 ntpd[1698]: Deleting interface #9 lxdbr0, fd42:9473:7f21:63ab::1#123, interface stats: received=0, sent=0, dropped=0, active_time=851 secs
+May 18 18:50:07 may181749-433044 ntpd[1698]: Deleting interface #10 lxdbr0, fe80::216:3eff:feec:f39f%5#123, interface stats: received=0, sent=0, dropped=0, active_time=851 secs
++ echo '# tasks executed on system'
+# tasks executed on system
++ echo ''
++ cat /var/tmp/snapd-tools/runtime-state/runs -
+google-nested:ubuntu-16.04-64:tests/nested/classic/hotplug google-nested:ubuntu-16.04-64:tests/nested/classic/snapshots-with-core-refresh-revert google-nested:ubuntu-16.04-64:tests/nested/manual/minimal-smoke:secboot_disabled google-nested:ubuntu-16.04-64:tests/nested/manual/minimal-smoke:secboot_enabled google-nested:ubuntu-16.04-64:tests/nested/manual/devmode-snaps-can-run-other-snaps
+-----
+.
+2022-05-18 16:45:15 Error restoring google:ubuntu-core-22-64:tests/main/ (may181431-047051) : EOF
+2022-05-18 16:45:15 Error debugging google:ubuntu-core-22-64:tests/main/ (may181431-047051) : EOF
+2022-05-18 16:45:15 Restoring google:ubuntu-core-22-64 (may181431-047051)...
+2022-05-18 16:45:15 Error restoring google:ubuntu-core-22-64 (may181431-047051) : EOF
+2022-05-18 16:45:15 Discarding google:ubuntu-core-22-64 (may181431-047051)...
+2022-05-18 16:45:15 Successful tasks: 7376
+2022-05-18 16:45:15 Aborted tasks: 3061
+2022-05-18 16:45:15 Failed tasks: 2
+ - google:ubuntu-20.04-64:tests/main/degraded
+ - google:ubuntu-core-22-64:tests/main/dbus-activation-session
+2022-05-18 16:45:15 Failed task prepare: 3
+ - google-nested:ubuntu-16.04-64:tests/nested/manual/cloud-init-never-used-not-vuln:refresh
+ - google-nested:ubuntu-16.04-64:tests/nested/manual/cloud-init-nocloud-not-vuln:firstboot
+ - google-nested:ubuntu-16.04-64:tests/nested/manual/devmode-snaps-can-run-other-snaps
+2022-05-18 16:45:15 Failed task restore: 2
+ - google:ubuntu-core-22-64:tests/main/dbus-activation-session
+ - google:ubuntu-core-22-64:tests/main/parallel-install-desktop
+2022-05-18 16:45:15 Failed suite prepare: 1
+ - google:opensuse-15.3-64:tests/completion/
+2022-05-18 16:45:15 Failed suite restore: 2
+ - google:ubuntu-core-22-64:tests/main/
+ - google:ubuntu-core-22-64:tests/main/
+2022-05-18 16:45:15 Failed project prepare: 1
+ - google:ubuntu-core-20-64:project
+2022-05-18 16:45:15 Failed project restore: 2
+ - google:ubuntu-core-22-64:project
+ - google:ubuntu-core-22-64:project
diff --git a/tests/lib/external/snapd-testing-tools/tests/log-parser/with-debug-output.log.spread b/tests/lib/external/snapd-testing-tools/tests/log-parser/with-debug-output.log.spread
new file mode 100644
index 0000000..f90830b
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/log-parser/with-debug-output.log.spread
@@ -0,0 +1,81 @@
+2024-04-25 16:56:53 Debug output for external:ubuntu-core-20-64:tests/core/snapd-maintenance-msg (external:ubuntu-core-20-64) :
+-----
++ '[' 1 '!=' 1 ']'
++ . /home/gopath/src/github.com/snapcore/snapd/tests/lib/state.sh
+++ SNAPD_STATE_PATH=/var/tmp/snapd-tools/snapd-state
+++ SNAPD_STATE_FILE=/var/tmp/snapd-tools/snapd-state/snapd-state.tar
+++ SNAPD_ACTIVE_UNITS=/var/tmp/snapd-tools/runtime-state/snapd-active-units
++ . /home/gopath/src/github.com/snapcore/snapd/tests/lib/systems.sh
++ echo '# System information'
+# System information
++ cat /etc/os-release
+NAME="Ubuntu Core"
+VERSION="20"
+ID=ubuntu-core
+PRETTY_NAME="Ubuntu Core 20"
+VERSION_ID="20"
+HOME_URL="https://snapcraft.io/"
+BUG_REPORT_URL="https://bugs.launchpad.net/snappy/"
++ echo '# Kernel information'
+# Kernel information
++ uname -a
+Linux ubuntu 5.4.0-177-generic #197-Ubuntu SMP Thu Mar 28 22:45:47 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux
++ echo '# Apparmor information'
+# Apparmor information
++ apparmor_parser --version
+AppArmor parser version 2.13.3
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=55 compat=0 ip=0x7ff1c6d2e73a code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=52 compat=0 ip=0x7ff1c6d2e6d7 code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=51 compat=0 ip=0x7ff1c6d2e707 code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=44 compat=0 ip=0x7ff1c700ea5a code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=7 compat=0 ip=0x7ff1c6d20b84 code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=13 compat=0 ip=0x7ff1c700fa4d code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=7 compat=0 ip=0x7ff1c6d20b84 code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=13 compat=0 ip=0x7ff1c700fa4d code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=13 compat=0 ip=0x7ff1c700fa4d code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=7 compat=0 ip=0x7ff1c6d20b84 code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=13 compat=0 ip=0x7ff1c700fa4d code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=7 compat=0 ip=0x7ff1c6d20b84 code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=13 compat=0 ip=0x7ff1c700fa4d code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=13 compat=0 ip=0x7ff1c700fa4d code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=7 compat=0 ip=0x7ff1c6d20b84 code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=45 compat=0 ip=0x7ff1c700e8da code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=5 compat=0 ip=0x7ff1c6d1b773 code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=1 compat=0 ip=0x7ff1c6d1c104 code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=1 compat=0 ip=0x7ff1c6d1c104 code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=1 compat=0 ip=0x7ff1c6d1c104 code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=1 compat=0 ip=0x7ff1c6d1c104 code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=1 compat=0 ip=0x7ff1c6d1c104 code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=13 compat=0 ip=0x7ff1c700fa4d code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=13 compat=0 ip=0x7ff1c700fa4d code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=1 compat=0 ip=0x7ff1c6d1c104 code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=13 compat=0 ip=0x7ff1c700fa4d code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=13 compat=0 ip=0x7ff1c700fa4d code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=13 compat=0 ip=0x7ff1c700fa4d code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=13 compat=0 ip=0x7ff1c700fa4d code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=3 compat=0 ip=0x7ff1c700e511 code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=13 compat=0 ip=0x7ff1c700fa4d code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=13 compat=0 ip=0x7ff1c700fa4d code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=13 compat=0 ip=0x7ff1c700fa4d code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=13 compat=0 ip=0x7ff1c700fa4d code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=13 compat=0 ip=0x7ff1c700fa4d code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=202 compat=0 ip=0x7ff1c700c92e code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=5 compat=0 ip=0x7ff1c6d1b773 code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=5 compat=0 ip=0x7ff1c6d1b773 code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=5 compat=0 ip=0x7ff1c6d1b773 code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=231 compat=0 ip=0x7ff1c6cf0ab6 code=0x7ffc0000
+Apr 25 16:56:53 ubuntu snapd[16017]: logger.go:93: DEBUG: pid=17276;uid=0;socket=/run/snapd.socket; GET /v2/changes?select=all 955.32µs 200
+Apr 25 16:56:53 ubuntu systemd[1]: snap.test-snapd-curl.curl-9e260fd8-e6ab-4b2a-82af-1c5db658cd84.scope: Succeeded.
+Apr 25 16:56:53 ubuntu sudo[14547]: pam_unix(sudo:session): session closed for user root
+Apr 25 16:56:53 ubuntu systemd[1]: snap.jq.jq-6dd86506-7d6f-4009-9ea0-77b6c34c607b.scope: Succeeded.
+Apr 25 16:56:53 ubuntu sudo[17324]: external : TTY=unknown ; *** ; USER=root ; COMMAND=/bin/bash -c /bin/bash -
+Apr 25 16:56:53 ubuntu sudo[17324]: pam_unix(sudo:session): session opened for user root by (uid=0)
+Apr 25 16:56:53 ubuntu systemd-journald[13548]: Runtime Journal (/run/log/journal/b40777928f53498d900b3982055b5893) is 45.7M, max 79.2M, 33.5M free.
+Apr 25 16:56:53 ubuntu snapd[16017]: logger.go:93: DEBUG: pid=17362;uid=0;socket=/run/snapd.socket; GET /v2/connections?select=all 521.714µs 200
++ echo '# tasks executed on system'
+# tasks executed on system
++ echo ''
++ cat /var/tmp/snapd-tools/runtime-state/runs -
+EXECUTED_TESTS=google:ubuntu-16.04-64:tests/main/interfaces-many-snap-provided google:ubuntu-16.04-64:tests/main/snap-seccomp google:ubuntu-16.04-64:tests/main/install-hook-misbehaving google:ubuntu-16.04-64:tests/main/security-device-cgroups-helper google:ubuntu-16.04-64:tests/main/interfaces-device-buttons google:ubuntu-16.04-64:tests/main/upgrade-from-2.15 google:ubuntu-16.04-64:tests/main/buildmode google:ubuntu-16.04-64:tests/main/snapshot-basic google:ubuntu-16.04-64:tests/main/system-usernames:_daemon_ google:ubuntu-16.04-64:tests/main/try-snap-goes-away:test_snapd_tools google:ubuntu-16.04-64:tests/main/interfaces-accounts-service google:ubuntu-16.04-64:tests/main/snap-run-userdata-current google:ubuntu-16.04-64:tests/main/snapd-update-services google:ubuntu-16.04-64:tests/main/remove-errors google:ubuntu-16.04-64:tests/main/mounts-persist-refresh-content-snap google:ubuntu-16.04-64:tests/main/parallel-install-auto-aliases google:ubuntu-16.04-64:tests/main/install-errors:withreexec google:ubuntu-16.04-64:tests/main/searching google:ubuntu-16.04-64:tests/main/interfaces-mount-control google:ubuntu-16.04-64:tests/main/services-after-before-install google:ubuntu-16.04-64:tests/main/system-usernames-illegal google:ubuntu-16.04-64:tests/main/interfaces-system-files google:ubuntu-16.04-64:tests/main/interfaces-desktop-launch google:ubuntu-16.04-64:tests/main/snap-env:regular google:ubuntu-16.04-64:tests/main/refresh-devmode:remote google:ubuntu-16.04-64:tests/main/snapd-sigterm google:ubuntu-16.04-64:tests/main/interfaces-browser-support:disallow google:ubuntu-16.04-64:tests/main/install-store:reexec1 google:ubuntu-16.04-64:tests/main/parallel-install-aliases google:ubuntu-16.04-64:tests/main/user-data-handling google:ubuntu-16.04-64:tests/main/interfaces-shutdown-introspection google:ubuntu-16.04-64:tests/main/refresh:classic_remote google:ubuntu-16.04-64:tests/main/system-users-are-created:snap_daemon google:ubuntu-16.04-64:tests/main/snap-update-ns google:ubuntu-16.04-64:tests/main/find-private google:ubuntu-16.04-64:tests/main/interfaces-adb-support google:ubuntu-16.04-64:tests/main/interfaces-system-observe google:ubuntu-16.04-64:tests/main/refresh-devmode:fake google:ubuntu-16.04-64:tests/main/interfaces-content google:ubuntu-16.04-64:tests/main/server-snap:pythonServer google:ubuntu-16.04-64:tests/main/retry-network google:ubuntu-16.04-64:tests/main/snap-run-devmode-classic:core_first google:ubuntu-16.04-64:tests/main/system-core-alias google:ubuntu-16.04-64:tests/main/snap-run-devmode-classic:snapd_first
+echo ''
+------
diff --git a/tests/lib/external/snapd-testing-tools/tests/log-parser/with-error-output.log.spread b/tests/lib/external/snapd-testing-tools/tests/log-parser/with-error-output.log.spread
new file mode 100644
index 0000000..1f7d5d3
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/log-parser/with-error-output.log.spread
@@ -0,0 +1,81 @@
+2024-04-26 16:38:41 Error executing google:ubuntu-core-20-64:tests/main/snap-quota-thread (apr261545-458974) :
+-----
++ '[' 1 '!=' 1 ']'
++ . /home/gopath/src/github.com/snapcore/snapd/tests/lib/state.sh
+++ SNAPD_STATE_PATH=/var/tmp/snapd-tools/snapd-state
+++ SNAPD_STATE_FILE=/var/tmp/snapd-tools/snapd-state/snapd-state.tar
+++ SNAPD_ACTIVE_UNITS=/var/tmp/snapd-tools/runtime-state/snapd-active-units
++ . /home/gopath/src/github.com/snapcore/snapd/tests/lib/systems.sh
++ echo '# System information'
+# System information
++ cat /etc/os-release
+NAME="Ubuntu Core"
+VERSION="20"
+ID=ubuntu-core
+PRETTY_NAME="Ubuntu Core 20"
+VERSION_ID="20"
+HOME_URL="https://snapcraft.io/"
+BUG_REPORT_URL="https://bugs.launchpad.net/snappy/"
++ echo '# Kernel information'
+# Kernel information
++ uname -a
+Linux ubuntu 5.4.0-177-generic #197-Ubuntu SMP Thu Mar 28 22:45:47 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux
++ echo '# Apparmor information'
+# Apparmor information
++ apparmor_parser --version
+AppArmor parser version 2.13.3
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=55 compat=0 ip=0x7ff1c6d2e73a code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=52 compat=0 ip=0x7ff1c6d2e6d7 code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=51 compat=0 ip=0x7ff1c6d2e707 code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=44 compat=0 ip=0x7ff1c700ea5a code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=7 compat=0 ip=0x7ff1c6d20b84 code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=13 compat=0 ip=0x7ff1c700fa4d code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=7 compat=0 ip=0x7ff1c6d20b84 code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=13 compat=0 ip=0x7ff1c700fa4d code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=13 compat=0 ip=0x7ff1c700fa4d code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=7 compat=0 ip=0x7ff1c6d20b84 code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=13 compat=0 ip=0x7ff1c700fa4d code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=7 compat=0 ip=0x7ff1c6d20b84 code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=13 compat=0 ip=0x7ff1c700fa4d code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=13 compat=0 ip=0x7ff1c700fa4d code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=7 compat=0 ip=0x7ff1c6d20b84 code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=45 compat=0 ip=0x7ff1c700e8da code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=5 compat=0 ip=0x7ff1c6d1b773 code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=1 compat=0 ip=0x7ff1c6d1c104 code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=1 compat=0 ip=0x7ff1c6d1c104 code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=1 compat=0 ip=0x7ff1c6d1c104 code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=1 compat=0 ip=0x7ff1c6d1c104 code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=1 compat=0 ip=0x7ff1c6d1c104 code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=13 compat=0 ip=0x7ff1c700fa4d code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=13 compat=0 ip=0x7ff1c700fa4d code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=1 compat=0 ip=0x7ff1c6d1c104 code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=13 compat=0 ip=0x7ff1c700fa4d code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=13 compat=0 ip=0x7ff1c700fa4d code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=13 compat=0 ip=0x7ff1c700fa4d code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=13 compat=0 ip=0x7ff1c700fa4d code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=3 compat=0 ip=0x7ff1c700e511 code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=13 compat=0 ip=0x7ff1c700fa4d code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=13 compat=0 ip=0x7ff1c700fa4d code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=13 compat=0 ip=0x7ff1c700fa4d code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=13 compat=0 ip=0x7ff1c700fa4d code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=13 compat=0 ip=0x7ff1c700fa4d code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=202 compat=0 ip=0x7ff1c700c92e code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=5 compat=0 ip=0x7ff1c6d1b773 code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=5 compat=0 ip=0x7ff1c6d1b773 code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=5 compat=0 ip=0x7ff1c6d1b773 code=0x7ffc0000
+Apr 25 16:56:53 ubuntu audit[17276]: SECCOMP auid=1001 uid=0 gid=0 ses=79 pid=17276 comm="curl" exe="/snap/test-snapd-curl/27/usr/bin/curl" sig=0 arch=c000003e syscall=231 compat=0 ip=0x7ff1c6cf0ab6 code=0x7ffc0000
+Apr 25 16:56:53 ubuntu snapd[16017]: logger.go:93: DEBUG: pid=17276;uid=0;socket=/run/snapd.socket; GET /v2/changes?select=all 955.32µs 200
+Apr 25 16:56:53 ubuntu systemd[1]: snap.test-snapd-curl.curl-9e260fd8-e6ab-4b2a-82af-1c5db658cd84.scope: Succeeded.
+Apr 25 16:56:53 ubuntu sudo[14547]: pam_unix(sudo:session): session closed for user root
+Apr 25 16:56:53 ubuntu systemd[1]: snap.jq.jq-6dd86506-7d6f-4009-9ea0-77b6c34c607b.scope: Succeeded.
+Apr 25 16:56:53 ubuntu sudo[17324]: external : TTY=unknown ; *** ; USER=root ; COMMAND=/bin/bash -c /bin/bash -
+Apr 25 16:56:53 ubuntu sudo[17324]: pam_unix(sudo:session): session opened for user root by (uid=0)
+Apr 25 16:56:53 ubuntu systemd-journald[13548]: Runtime Journal (/run/log/journal/b40777928f53498d900b3982055b5893) is 45.7M, max 79.2M, 33.5M free.
+Apr 25 16:56:53 ubuntu snapd[16017]: logger.go:93: DEBUG: pid=17362;uid=0;socket=/run/snapd.socket; GET /v2/connections?select=all 521.714µs 200
++ echo '# tasks executed on system'
+# tasks executed on system
++ echo ''
++ cat /var/tmp/snapd-tools/runtime-state/runs -
+EXECUTED_TESTS=google:ubuntu-16.04-64:tests/main/interfaces-many-snap-provided google:ubuntu-16.04-64:tests/main/snap-seccomp google:ubuntu-16.04-64:tests/main/install-hook-misbehaving google:ubuntu-16.04-64:tests/main/security-device-cgroups-helper google:ubuntu-16.04-64:tests/main/interfaces-device-buttons google:ubuntu-16.04-64:tests/main/upgrade-from-2.15 google:ubuntu-16.04-64:tests/main/buildmode google:ubuntu-16.04-64:tests/main/snapshot-basic google:ubuntu-16.04-64:tests/main/system-usernames:_daemon_ google:ubuntu-16.04-64:tests/main/try-snap-goes-away:test_snapd_tools google:ubuntu-16.04-64:tests/main/interfaces-accounts-service google:ubuntu-16.04-64:tests/main/snap-run-userdata-current google:ubuntu-16.04-64:tests/main/snapd-update-services google:ubuntu-16.04-64:tests/main/remove-errors google:ubuntu-16.04-64:tests/main/mounts-persist-refresh-content-snap google:ubuntu-16.04-64:tests/main/parallel-install-auto-aliases google:ubuntu-16.04-64:tests/main/install-errors:withreexec google:ubuntu-16.04-64:tests/main/searching google:ubuntu-16.04-64:tests/main/interfaces-mount-control google:ubuntu-16.04-64:tests/main/services-after-before-install google:ubuntu-16.04-64:tests/main/system-usernames-illegal google:ubuntu-16.04-64:tests/main/interfaces-system-files google:ubuntu-16.04-64:tests/main/interfaces-desktop-launch google:ubuntu-16.04-64:tests/main/snap-env:regular google:ubuntu-16.04-64:tests/main/refresh-devmode:remote google:ubuntu-16.04-64:tests/main/snapd-sigterm google:ubuntu-16.04-64:tests/main/interfaces-browser-support:disallow google:ubuntu-16.04-64:tests/main/install-store:reexec1 google:ubuntu-16.04-64:tests/main/parallel-install-aliases google:ubuntu-16.04-64:tests/main/user-data-handling google:ubuntu-16.04-64:tests/main/interfaces-shutdown-introspection google:ubuntu-16.04-64:tests/main/refresh:classic_remote google:ubuntu-16.04-64:tests/main/system-users-are-created:snap_daemon google:ubuntu-16.04-64:tests/main/snap-update-ns google:ubuntu-16.04-64:tests/main/find-private google:ubuntu-16.04-64:tests/main/interfaces-adb-support google:ubuntu-16.04-64:tests/main/interfaces-system-observe google:ubuntu-16.04-64:tests/main/refresh-devmode:fake google:ubuntu-16.04-64:tests/main/interfaces-content google:ubuntu-16.04-64:tests/main/server-snap:pythonServer google:ubuntu-16.04-64:tests/main/retry-network google:ubuntu-16.04-64:tests/main/snap-run-devmode-classic:core_first google:ubuntu-16.04-64:tests/main/system-core-alias google:ubuntu-16.04-64:tests/main/snap-run-devmode-classic:snapd_first
+echo ''
+------
diff --git a/tests/lib/external/snapd-testing-tools/tests/log-parser/with-failed-and-aborted.log.spread b/tests/lib/external/snapd-testing-tools/tests/log-parser/with-failed-and-aborted.log.spread
new file mode 100644
index 0000000..a5e0aeb
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/log-parser/with-failed-and-aborted.log.spread
@@ -0,0 +1,265 @@
+2020-11-27 19:30:40 Project content is packed for delivery (169.92KB).
+2020-11-27 19:30:40 Sequence of jobs produced with -seed=1606505440
+2020-11-27 19:30:40 If killed, discard servers with: spread -reuse-pid=313960 -discard
+2020-11-27 19:30:40 Allocating google:opensuse-tumbleweed-64...
+2020-11-27 19:30:40 Allocating google:opensuse-tumbleweed-64...
+2020-11-27 19:30:40 Allocating google:opensuse-tumbleweed-64...
+2020-11-27 19:30:40 Allocating google:opensuse-tumbleweed-64...
+2020-11-27 19:30:40 Allocating google:opensuse-tumbleweed-64...
+2020-11-27 19:30:40 Allocating google:opensuse-tumbleweed-64...
+2020-11-27 19:30:49 Waiting for google:opensuse-tumbleweed-64 (nov271930-268545) to boot at 35.229.111.181...
+2020-11-27 19:30:52 Waiting for google:opensuse-tumbleweed-64 (nov271930-268322) to boot at 35.243.146.54...
+2020-11-27 19:30:53 Waiting for google:opensuse-tumbleweed-64 (nov271930-268530) to boot at 35.243.170.137...
+2020-11-27 19:30:54 Waiting for google:opensuse-tumbleweed-64 (nov271930-268552) to boot at 34.73.120.227...
+2020-11-27 19:30:54 Waiting for google:opensuse-tumbleweed-64 (nov271930-268558) to boot at 35.237.32.105...
+2020-11-27 19:30:54 Waiting for google:opensuse-tumbleweed-64 (nov271930-268563) to boot at 34.74.49.214...
+2020-11-27 19:31:29 Allocated google:opensuse-tumbleweed-64 (nov271930-268530).
+2020-11-27 19:31:29 Connecting to google:opensuse-tumbleweed-64 (nov271930-268530)...
+2020-11-27 19:31:29 Connected to google:opensuse-tumbleweed-64 (nov271930-268530) at 35.243.170.137.
+2020-11-27 19:31:29 Sending project content to google:opensuse-tumbleweed-64 (nov271930-268530)...
+2020-11-27 19:31:30 Allocated google:opensuse-tumbleweed-64 (nov271930-268552).
+2020-11-27 19:31:30 Connecting to google:opensuse-tumbleweed-64 (nov271930-268552)...
+2020-11-27 19:31:30 Connected to google:opensuse-tumbleweed-64 (nov271930-268552) at 34.73.120.227.
+2020-11-27 19:31:30 Sending project content to google:opensuse-tumbleweed-64 (nov271930-268552)...
+2020-11-27 19:31:30 Allocated google:opensuse-tumbleweed-64 (nov271930-268558).
+2020-11-27 19:31:30 Connecting to google:opensuse-tumbleweed-64 (nov271930-268558)...
+2020-11-27 19:31:30 Preparing google:opensuse-tumbleweed-64 (nov271930-268530)...
+2020-11-27 20:01:27 Preparing google:opensuse-tumbleweed-64:tests/main/user-data-handling (nov271930-268530)...
+2020-11-27 20:01:30 Error preparing google:opensuse-tumbleweed-64 (nov271930-268552) :
+-----
++ cat
++ mv gai.conf /etc/gai.conf
++ command -v restorecon
++ [[ opensuse-tumbleweed-64 == fedora-* ]]
++ [[ opensuse-tumbleweed-64 == opensuse-* ]]
++ zypper ref
+Retrieving repository 'Cloud:Tools (openSUSE_Tumbleweed)' metadata [.done]
+Building repository 'Cloud:Tools (openSUSE_Tumbleweed)' cache [....done]
+Retrieving repository 'kernel-repo' metadata [..done]
+Building repository 'kernel-repo' cache [....done]
+Retrieving repository 'repo-debug' metadata [............done]
+Building repository 'repo-debug' cache [....done]
+Retrieving repository 'repo-non-oss' metadata [..done]
+Building repository 'repo-non-oss' cache [....done]
+Retrieving repository 'repo-oss' metadata [......done]
+Building repository 'repo-oss' cache [....done]
+Repository 'repo-update' is up to date.
+All repositories have been refreshed.
++ cat
++ [[ opensuse-tumbleweed-64 == arch-* ]]
++ [[ opensuse-tumbleweed-64 == debian-* ]]
++ [[ opensuse-tumbleweed-64 == centos-* ]]
++ [[ opensuse-tumbleweed-64 == ubuntu-* ]]
++ '[' -f current.delta ']'
+++ mktemp
++ tf=/tmp/tmp.xq251br0RX
++ case "$SPREAD_SYSTEM" in
++ zypper -q --gpg-auto-import-keys refresh
++ zypper -q install -y xdelta3 curl
++ quiet govendor sync
+
+
+-----
+.
+2020-11-27 20:01:30 Discarding google:opensuse-tumbleweed-64 (nov271930-268552)...
+2020-11-27 20:01:30 Error preparing google:opensuse-tumbleweed-64 (nov271930-268558) :
+-----
++ cat
++ mv gai.conf /etc/gai.conf
++ command -v restorecon
++ [[ opensuse-tumbleweed-64 == fedora-* ]]
++ [[ opensuse-tumbleweed-64 == opensuse-* ]]
++ zypper ref
++ go get -u github.com/kardianos/govendor
+++ seq 10
++ for _ in $(seq 10)
++ quiet govendor sync
+
+
+-----
+.
+2020-11-27 20:01:30 Discarding google:opensuse-tumbleweed-64 (nov271930-268558)...
+2020-11-27 20:01:30 Error preparing google:opensuse-tumbleweed-64 (nov271930-268563) :
+-----
++ cat
++ mv gai.conf /etc/gai.conf
++ command -v restorecon
++ [[ opensuse-tumbleweed-64 == fedora-* ]]
++ [[ opensuse-tumbleweed-64 == opensuse-* ]]
++ zypper ref
+Retrieving repository 'Cloud:Tools (openSUSE_Tumbleweed)' metadata [...done]
+Building repository 'Cloud:Tools (openSUSE_Tumbleweed)' cache [....done]
+Retrieving repository 'kernel-repo' metadata [..done]
+Building repository 'kernel-repo' cache [....done]
++ loginctl enable-linger test
++ loginctl disable-linger test
++ '[' '' = yes ']'
++ case "$SPREAD_SYSTEM" in
+++ command -v govendor
++ '[' -z '' ']'
++ rm -rf /home/gopath/src/github.com/kardianos/govendor
++ go get -u github.com/kardianos/govendor
+++ seq 10
++ for _ in $(seq 10)
++ quiet govendor sync
+
+
+-----
+.
+2020-11-27 20:01:30 Discarding google:opensuse-tumbleweed-64 (nov271930-268563)...
+2020-11-27 20:01:30 Error preparing google:opensuse-tumbleweed-64 (nov271930-268545) :
+-----
++ cat
++ mv gai.conf /etc/gai.conf
++ command -v restorecon
++ [[ opensuse-tumbleweed-64 == fedora-* ]]
+++ command -v govendor
++ '[' -z '' ']'
++ rm -rf /home/gopath/src/github.com/kardianos/govendor
++ go get -u github.com/kardianos/govendor
+++ seq 10
++ for _ in $(seq 10)
++ quiet govendor sync
+
+
+-----
+.
+2020-11-27 20:01:30 Discarding google:opensuse-tumbleweed-64 (nov271930-268545)...
+2020-11-27 20:51:19 Preparing google:opensuse-tumbleweed-64:tests/main/interfaces-kvm (nov271930-268530)...
+2020-11-27 20:51:30 Executing google:opensuse-tumbleweed-64:tests/main/interfaces-kvm (nov271930-268530) (338/425)...
+2020-11-27 20:51:30 Restoring google:opensuse-tumbleweed-64:tests/main/interfaces-kvm (nov271930-268530)...
+2020-11-27 20:51:31 Preparing google:opensuse-tumbleweed-64:tests/main/snap-disconnect (nov271930-268530)...
+2020-11-27 20:51:45 Executing google:opensuse-tumbleweed-64:tests/main/snap-disconnect (nov271930-268530) (339/425)...
+2020-11-27 20:51:59 Restoring google:opensuse-tumbleweed-64:tests/main/snap-disconnect (nov271930-268530)...
+2020-11-27 20:52:00 Preparing google:opensuse-tumbleweed-64:tests/main/services-disable-install-hook (nov271930-268530)...
+2020-11-27 20:52:02 Error executing google:opensuse-tumbleweed-64:tests/main/interfaces-x11-unix-socket (nov271930-268322) :
+-----
++ echo 'Install test snaps'
+Install test snaps
++ /home/gopath/src/github.com/snapcore/snapd/tests/lib/tools/snaps-state install-local x11-client
+x11-client 1.0 installed
++ /home/gopath/src/github.com/snapcore/snapd/tests/lib/tools/snaps-state install-local x11-server
+x11-server 1.0 installed
++ echo 'Ensure x11 plug is not connected to implicit slot'
+Ensure x11 plug is not connected to implicit slot
++ snap disconnect x11-client:x11
++ echo 'Connect x11-client to x11-server'
+Connect x11-client to x11-server
++ snap connect x11-client:x11 x11-server:x11
++ echo 'The snaps can communicate via the unix domain socket in /tmp'
+The snaps can communicate via the unix domain socket in /tmp
++ retry -n 4 --wait 0.5 test -e /tmp/snap.x11-server/tmp/.X11-unix/X0
++ x11-server
+retry: command test -e /tmp/snap.x11-server/tmp/.X11-unix/X0 failed with code 1
+retry: next attempt in 0.5 second(s) (attempt 1 of 4)
++ MATCH 'Hello from xserver'
++ x11-client
+nc: create unix socket failed
+grep error: pattern not found, got:
+
+
+
+-----
+.
+2020-11-27 20:52:02 Debug output for google:opensuse-tumbleweed-64:tests/main/interfaces-x11-unix-socket (nov271930-268322) :
+-----
++ '[' 1 = 1 ']'
++ . /home/gopath/src/github.com/snapcore/snapd/tests/lib/state.sh
+++ SNAPD_STATE_PATH=/home/gopath/src/github.com/snapcore/snapd/tests/snapd-state
+++ SNAPD_STATE_FILE=/home/gopath/src/github.com/snapcore/snapd/tests/snapd-state/snapd-state.tar
+++ RUNTIME_STATE_PATH=/home/gopath/src/github.com/snapcore/snapd/tests/runtime-state
+++ SNAPD_ACTIVE_UNITS=/home/gopath/src/github.com/snapcore/snapd/tests/runtime-state/snapd-active-units
+++ . /home/gopath/src/github.com/snapcore/snapd/tests/lib/dirs.sh
++++ export SNAP_MOUNT_DIR=/snap
++++ SNAP_MOUNT_DIR=/snap
++++ export LIBEXECDIR=/usr/lib
++++ LIBEXECDIR=/usr/lib
++++ export MEDIA_DIR=/media
+-----
+.
+2020-11-27 20:52:03 Discarding google:opensuse-tumbleweed-64 (nov271930-268322)...
+2020-11-27 20:52:06 Executing google:opensuse-tumbleweed-64:tests/main/services-disable-install-hook (nov271930-268530) (340/425)...
+2020-11-27 20:52:12 Restoring google:opensuse-tumbleweed-64:tests/main/services-disable-install-hook (nov271930-268530)...
+2020-11-27 20:52:13 Preparing google:opensuse-tumbleweed-64:tests/main/snap-debug-state (nov271930-268530)...
+2020-11-27 20:52:24 Executing google:opensuse-tumbleweed-64:tests/main/snap-debug-state (nov271930-268530) (341/425)...
+2020-11-27 20:52:24 Restoring google:opensuse-tumbleweed-64:tests/main/snap-debug-state (nov271930-268530)...
+2020-11-27 20:52:26 Preparing google:opensuse-tumbleweed-64:tests/main/refresh-all (nov271930-268530)...
+2020-11-27 20:52:45 Executing google:opensuse-tumbleweed-64:tests/main/refresh-all (nov271930-268530) (342/425)...
+2020-11-27 20:53:06 Restoring google:opensuse-tumbleweed-64:tests/main/refresh-all (nov271930-268530)...
+2020-11-27 20:53:08 Preparing google:opensuse-tumbleweed-64:tests/main/snap-service (nov271930-268530)...
+2020-11-27 20:53:15 Executing google:opensuse-tumbleweed-64:tests/main/snap-service (nov271930-268530) (343/425)...
+2020-11-27 20:53:47 Restoring google:opensuse-tumbleweed-64:tests/main/snap-service (nov271930-268530)...
+2020-11-27 20:53:48 Preparing google:opensuse-tumbleweed-64:tests/main/snapctl-is-connected (nov271930-268530)...
+2020-11-27 20:54:03 Executing google:opensuse-tumbleweed-64:tests/main/snapctl-is-connected (nov271930-268530) (344/425)...
+2020-11-27 21:15:00 Preparing google:opensuse-tumbleweed-64:tests/main/install-sideload:reexec0 (nov271930-268530)...
+2020-11-27 21:15:04 Error preparing google:opensuse-tumbleweed-64:tests/main/install-sideload:reexec0 (nov271930-268530) :
+-----
++ /home/gopath/src/github.com/snapcore/snapd/tests/lib/prepare-restore.sh --prepare-project-each
++ set -e
++ . /home/gopath/src/github.com/snapcore/snapd/tests/lib/quiet.sh
++ . /home/gopath/src/github.com/snapcore/snapd/tests/lib/dirs.sh
+++ export SNAP_MOUNT_DIR=/snap
+++ SNAP_MOUNT_DIR=/snap
+++ export LIBEXECDIR=/usr/lib
+++ LIBEXECDIR=/usr/lib
+++ export MEDIA_DIR=/media
+++ MEDIA_DIR=/media
+++ case "$SPREAD_SYSTEM" in
+++ export SNAP_MOUNT_DIR=/snap
+++ SNAP_MOUNT_DIR=/snap
+++ export MEDIA_DIR=/run/media
+++ MEDIA_DIR=/run/media
++++ . /etc/os-release
+++++ NAME='openSUSE Tumbleweed'
+++++ ID=opensuse-tumbleweed
+++++ ID_LIKE='opensuse suse'
+++++ VERSION_ID=20201107
+++++ PRETTY_NAME='openSUSE Tumbleweed'
+++++ ANSI_COLOR='0;32'
+++++ CPE_NAME=cpe:/o:opensuse:tumbleweed:20201107
+++++ BUG_REPORT_URL=https://bugs.opensuse.org
+++++ HOME_URL=https://www.opensuse.org/
+++++ DOCUMENTATION_URL=https://en.opensuse.org/Portal:Tumbleweed
+++++ LOGO=distributor-logo
++++ echo opensuse-tumbleweed
+++ '[' opensuse-tumbleweed = opensuse-tumbleweed ']'
+++ export LIBEXECDIR=/usr/libexec
+++ LIBEXECDIR=/usr/libexec
++ . /home/gopath/src/github.com/snapcore/snapd/tests/lib/pkgdb.sh
+++ . /home/gopath/src/github.com/snapcore/snapd/tests/lib/quiet.sh
+Stopping snap-core-10475.mount
+Stopping snap-core-10475.mount
+Removing snap core
+Removing snap-core-10475.mount
+Stopping snap-core16-46.mount
+Stopping snap-core16-46.mount
+Removing snap core16
+Removing snap-core16-46.mount
+Stopping snap-test\x2dsnapd\x2dsh\x2dcore16-x1.mount
+Stopping snap-test\x2dsnapd\x2dsh\x2dcore16-x1.mount
+Removing snap test-snapd-sh-core16
+Removing snap-test\x2dsnapd\x2dsh\x2dcore16-x1.mount
+Discarding preserved snap namespaces
+Removing downloaded snaps
+Removing features exported from snapd to helper tools
+Final directory cleanup
+Removing leftover snap shared state data
+Removing snapd catalog cache
+Removing extra snap-confine apparmor rules
+active
+-----
+.
+2020-11-27 21:15:04 Error debugging google:opensuse-tumbleweed-64:tests/main/install-sideload:reexec0 (nov271930-268530) : EOF
+2020-11-27 21:15:04 Discarding google:opensuse-tumbleweed-64 (nov271930-268530)...
+error: unsuccessful run
+2020-11-27 21:15:04 Successful tasks: 400
+2020-11-27 21:15:04 Aborted tasks: 24
+2020-11-27 21:15:04 Failed tasks: 1
+ - google:opensuse-tumbleweed-64:tests/main/interfaces-x11-unix-socket
+2020-11-27 21:15:04 Failed task prepare: 1
+ - google:opensuse-tumbleweed-64:tests/main/install-sideload:reexec0
+2020-11-27 21:15:04 Failed project prepare: 4
+ - google:opensuse-tumbleweed-64:project
+ - google:opensuse-tumbleweed-64:project
+ - google:opensuse-tumbleweed-64:project
+ - google:opensuse-tumbleweed-64:project
\ No newline at end of file
diff --git a/tests/lib/external/snapd-testing-tools/tests/log-parser/with-failed-project-restore.log.spread b/tests/lib/external/snapd-testing-tools/tests/log-parser/with-failed-project-restore.log.spread
new file mode 100644
index 0000000..5253c5a
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/log-parser/with-failed-project-restore.log.spread
@@ -0,0 +1,146 @@
+2022-05-18 17:49:24 Project content is packed for delivery (8.86MB).
+2022-05-18 17:49:24 Sequence of jobs produced with -seed=1652896164
+2022-05-18 17:49:24 If killed, discard servers with: spread -reuse-pid=9308 -discard
+2022-05-18 17:49:24 Allocating google-nested:ubuntu-16.04-64...
+2022-05-18 17:49:24 Allocating google-nested:ubuntu-16.04-64...
+2022-05-18 17:49:24 Allocating google-nested:ubuntu-16.04-64...
+2022-05-18 17:49:37 Waiting for google-nested:ubuntu-16.04-64 (may181749-432987) to boot at 104.196.14.127...
+2022-05-18 17:49:37 Waiting for google-nested:ubuntu-16.04-64 (may181749-433163) to boot at 35.237.37.145...
+2022-05-18 17:49:47 Waiting for google-nested:ubuntu-16.04-64 (may181749-433044) to boot at 34.148.64.110...
+2022-05-18 17:50:03 Allocated google-nested:ubuntu-16.04-64 (may181749-432987).
+2022-05-18 17:50:03 Connecting to google-nested:ubuntu-16.04-64 (may181749-432987)...
+2022-05-18 17:50:04 Connected to google-nested:ubuntu-16.04-64 (may181749-432987) at 104.196.14.127.
+2022-05-18 17:50:04 Sending project content to google-nested:ubuntu-16.04-64 (may181749-432987)...
+2022-05-18 17:50:06 Preparing google-nested:ubuntu-16.04-64 (may181749-432987)...
+2022-05-18 17:50:08 Allocated google-nested:ubuntu-16.04-64 (may181749-433163).
+2022-05-18 17:50:08 Connecting to google-nested:ubuntu-16.04-64 (may181749-433163)...
+2022-05-18 17:50:09 Connected to google-nested:ubuntu-16.04-64 (may181749-433163) at 35.237.37.145.
+2022-05-18 17:50:09 Sending project content to google-nested:ubuntu-16.04-64 (may181749-433163)...
+2022-05-18 17:50:11 Preparing google-nested:ubuntu-16.04-64 (may181749-433163)...
+2022-05-18 17:50:13 Allocated google-nested:ubuntu-16.04-64 (may181749-433044).
+2022-05-18 17:50:13 Connecting to google-nested:ubuntu-16.04-64 (may181749-433044)...
+2022-05-18 17:50:13 Connected to google-nested:ubuntu-16.04-64 (may181749-433044) at 34.148.64.110.
+2022-05-18 17:50:13 Sending project content to google-nested:ubuntu-16.04-64 (may181749-433044)...
+2022-05-18 17:50:15 Preparing google-nested:ubuntu-16.04-64 (may181749-433044)...
+2022-05-18 17:55:06 WARNING: may181749-432987 (google-nested:ubuntu-16.04-64) running late. Current output:
+-----
+(... 365 lines above ...)
++ restart_logind=
++ loginctl enable-linger test
++ loginctl disable-linger test
+Unpacking systemd (229-4ubuntu21.31) over (229-4ubuntu21.28) ...
+Processing triggers for dbus (1.10.6-1ubuntu3.6) ...
+Processing triggers for ureadahead (0.100.0-19.1) ...
+Processing triggers for man-db (2.7.5-1) ...
+Setting up systemd (229-4ubuntu21.31) ...
+addgroup: The group `systemd-journal' already exists as a system group. Exiting.
+[/usr/lib/tmpfiles.d/var.conf:14] Duplicate line for path "/var/log", ignoring.
+Setting up libpam-systemd:amd64 (229-4ubuntu21.31) ...
+-----
+.
+2022-05-18 18:00:06 WARNING: may181749-432987 (google-nested:ubuntu-16.04-64) running late. Current output:
+-----
+(... 1334 lines above ...)
+dpkg-gencontrol: warning: File::FcntlLock not available; using flock which is not NFS-safe
+dpkg-gencontrol: warning: File::FcntlLock not available; using flock which is not NFS-safe
+dpkg-gencontrol: warning: File::FcntlLock not available; using flock which is not NFS-safe
+dpkg-gencontrol: warning: File::FcntlLock not available; using flock which is not NFS-safe
+make[1]: Leaving directory '/home/gopath/src/github.com/snapcore/snapd'
+ dh_md5sums -O--buildsystem=golang -O--fail-missing -O--builddirectory=_build
+ dh_builddeb -u-Zxz -O--buildsystem=golang -O--fail-missing -O--builddirectory=_build
+dpkg-deb: building package 'golang-github-ubuntu-core-snappy-dev' in '../golang-github-ubuntu-core-snappy-dev_1337.2.55.5_all.deb'.
+dpkg-deb: building package 'golang-github-snapcore-snapd-dev' in '../golang-github-snapcore-snapd-dev_1337.2.55.5_all.deb'.
+dpkg-deb: building package 'snapd' in '../snapd_1337.2.55.5_amd64.deb'.
+-----
+.
+2022-05-18 18:00:11 WARNING: may181749-433163 (google-nested:ubuntu-16.04-64) running late. Current output:
+-----
+(... 1334 lines above ...)
+dpkg-gencontrol: warning: File::FcntlLock not available; using flock which is not NFS-safe
+dpkg-gencontrol: warning: File::FcntlLock not available; using flock which is not NFS-safe
+dpkg-gencontrol: warning: File::FcntlLock not available; using flock which is not NFS-safe
+dpkg-gencontrol: warning: File::FcntlLock not available; using flock which is not NFS-safe
+make[1]: Leaving directory '/home/gopath/src/github.com/snapcore/snapd'
+ dh_md5sums -O--buildsystem=golang -O--fail-missing -O--builddirectory=_build
+ dh_builddeb -u-Zxz -O--buildsystem=golang -O--fail-missing -O--builddirectory=_build
+dpkg-deb: building package 'golang-github-ubuntu-core-snappy-dev' in '../golang-github-ubuntu-core-snappy-dev_1337.2.55.5_all.deb'.
+dpkg-deb: building package 'golang-github-snapcore-snapd-dev' in '../golang-github-snapcore-snapd-dev_1337.2.55.5_all.deb'.
+dpkg-deb: building package 'snapd' in '../snapd_1337.2.55.5_amd64.deb'.
+-----
+.
+2022-05-18 18:00:16 WARNING: may181749-433044 (google-nested:ubuntu-16.04-64) running late. Output unchanged.
+2022-05-18 18:01:03 Preparing google-nested:ubuntu-16.04-64:tests/nested/core/ (may181749-432987)...
+2022-05-18 18:01:17 Preparing google-nested:ubuntu-16.04-64:tests/nested/manual/ (may181749-433163)...
+2022-05-18 18:03:19 Preparing google-nested:ubuntu-16.04-64:tests/nested/manual/cloud-init-never-used-not-vuln:firstboot (may181749-433163)...
+2022-05-18 18:05:16 WARNING: may181749-433044 (google-nested:ubuntu-16.04-64) running late. Output unchanged.
+2022-05-18 18:06:03 WARNING: may181749-432987 (google-nested:ubuntu-16.04-64:tests/nested/core/) running late. Current output:
+-----
+(... 97 lines above ...)
+Parallel unsquashfs: Using 2 processors
+11398 inodes (13347 blocks) to write
+
+[===========================================================\] 13347/13347 100%
+
+created 8997 files
+created 1461 directories
+created 2312 symlinks
+created 79 devices
+created 0 fifos
+-----
+.
+2022-05-18 18:06:52 Executing google-nested:ubuntu-16.04-64:tests/nested/manual/cloud-init-never-used-not-vuln:firstboot (may181749-433163) (1/13)...
+2022-05-18 18:06:58 Preparing google-nested:ubuntu-16.04-64:tests/nested/core/image-build (may181749-432987)...
+2022-05-18 18:07:57 Restoring google-nested:ubuntu-16.04-64:tests/nested/manual/cloud-init-never-used-not-vuln:firstboot (may181749-433163)...
+2022-05-18 18:08:00 Preparing google-nested:ubuntu-16.04-64:tests/nested/manual/cloud-init-nocloud-not-vuln:firstboot (may181749-433163)...
+2022-05-18 18:08:40 Executing google-nested:ubuntu-16.04-64:tests/nested/core/image-build (may181749-432987) (2/13)...
+Error: 2022-05-18 18:09:34 Error preparing google-nested:ubuntu-16.04-64:tests/nested/manual/cloud-init-nocloud-not-vuln:firstboot (may181749-433163) :
+-----
++ /home/gopath/src/github.com/snapcore/snapd/tests/lib/prepare-restore.sh --prepare-project-each
++ set -e
++ . /home/gopath/src/github.com/snapcore/snapd/tests/lib/quiet.sh
++ . /home/gopath/src/github.com/snapcore/snapd/tests/lib/pkgdb.sh
+++ . /home/gopath/src/github.com/snapcore/snapd/tests/lib/quiet.sh
++ . /home/gopath/src/github.com/snapcore/snapd/tests/lib/random.sh
++ . /home/gopath/src/github.com/snapcore/snapd/tests/lib/state.sh
+++ SNAPD_STATE_PATH=/var/tmp/snapd-tools/snapd-state
+++ SNAPD_STATE_FILE=/var/tmp/snapd-tools/snapd-state/snapd-state.tar
+++ RUNTIME_STATE_PATH=/var/tmp/snapd-tools/runtime-state
+++ SNAPD_ACTIVE_UNITS=/var/tmp/snapd-tools/runtime-state/snapd-active-units
+++ . /home/gopath/src/github.com/snapcore/snapd/tests/lib/systemd.sh
++ case "$1" in
++ prepare_project_each
++ dmesg -c
++ fixup_dev_random
++ mv /dev/random /dev/random.orig
++ mknod /dev/random c 1 9
++ kill_gpg_agent
++ pkill -9 -e gpg-agent
+
+May 18 18:50:05 may181749-433044 kernel: veth33879c75: renamed from physLGGOmx
+May 18 18:50:05 may181749-433044 libvirtd[21899]: Failed to open file '/sys/class/net/eth0/operstate': No such file or directory
+May 18 18:50:05 may181749-433044 libvirtd[21899]: unable to read: /sys/class/net/eth0/operstate: No such file or directory
+May 18 18:50:05 may181749-433044 kernel: device veth690dd0d2 left promiscuous mode
+May 18 18:50:05 may181749-433044 kernel: lxdbr0: port 1(veth690dd0d2) entered disabled state
+May 18 18:50:05 may181749-433044 audit[20780]: AVC apparmor="STATUS" operation="profile_remove" profile="unconfined" name="lxd-snapcraft-snapd_" pid=20780 comm="apparmor_parser"
+May 18 18:50:05 may181749-433044 kernel: audit: type=1400 audit(1652899805.951:102): apparmor="STATUS" operation="profile_remove" profile="unconfined" name="lxd-snapcraft-snapd_" pid=20780 comm="apparmor_parser"
+May 18 18:50:07 may181749-433044 ntpd[1698]: Deleting interface #8 lxdbr0, 10.253.239.1#123, interface stats: received=0, sent=0, dropped=0, active_time=851 secs
+May 18 18:50:07 may181749-433044 ntpd[1698]: Deleting interface #9 lxdbr0, fd42:9473:7f21:63ab::1#123, interface stats: received=0, sent=0, dropped=0, active_time=851 secs
+May 18 18:50:07 may181749-433044 ntpd[1698]: Deleting interface #10 lxdbr0, fe80::216:3eff:feec:f39f%5#123, interface stats: received=0, sent=0, dropped=0, active_time=851 secs
++ echo '# tasks executed on system'
+# tasks executed on system
++ echo ''
++ cat /var/tmp/snapd-tools/runtime-state/runs -
+google-nested:ubuntu-16.04-64:tests/nested/classic/hotplug google-nested:ubuntu-16.04-64:tests/nested/classic/snapshots-with-core-refresh-revert google-nested:ubuntu-16.04-64:tests/nested/manual/minimal-smoke:secboot_disabled google-nested:ubuntu-16.04-64:tests/nested/manual/minimal-smoke:secboot_enabled google-nested:ubuntu-16.04-64:tests/nested/manual/devmode-snaps-can-run-other-snaps
+-----
+.
+2022-05-18 16:45:15 Error restoring google:ubuntu-core-22-64:tests/main/ (may181431-047051) : EOF
+2022-05-18 16:45:15 Error debugging google:ubuntu-core-22-64:tests/main/ (may181431-047051) : EOF
+2022-05-18 16:45:15 Restoring google:ubuntu-core-22-64 (may181431-047051)...
+2022-05-18 16:45:15 Error restoring google:ubuntu-core-22-64 (may181431-047051) : EOF
+2022-05-18 16:45:15 Discarding google:ubuntu-core-22-64 (may181431-047051)...
+2022-05-18 16:45:15 Successful tasks: 7376
+2022-05-18 16:45:15 Aborted tasks: 0
+2022-05-18 16:45:15 Failed project restore: 2
+ - google:ubuntu-core-22-64:project
+ - google:ubuntu-core-22-64:project
+error: unsuccessful run
\ No newline at end of file
diff --git a/tests/lib/external/snapd-testing-tools/tests/log-parser/with-failed-repeated-and-aborted.log.spread b/tests/lib/external/snapd-testing-tools/tests/log-parser/with-failed-repeated-and-aborted.log.spread
new file mode 100644
index 0000000..6e198f0
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/log-parser/with-failed-repeated-and-aborted.log.spread
@@ -0,0 +1,154 @@
+2022-05-18 17:49:24 Project content is packed for delivery (8.86MB).
+2022-05-18 17:49:24 Sequence of jobs produced with -seed=1652896164
+2022-05-18 17:49:24 If killed, discard servers with: spread -reuse-pid=9308 -discard
+2022-05-18 17:49:24 Allocating google-nested:ubuntu-16.04-64...
+2022-05-18 17:49:24 Allocating google-nested:ubuntu-16.04-64...
+2022-05-18 17:49:24 Allocating google-nested:ubuntu-16.04-64...
+2022-05-18 17:49:37 Waiting for google-nested:ubuntu-16.04-64 (may181749-432987) to boot at 104.196.14.127...
+2022-05-18 17:49:37 Waiting for google-nested:ubuntu-16.04-64 (may181749-433163) to boot at 35.237.37.145...
+2022-05-18 17:49:47 Waiting for google-nested:ubuntu-16.04-64 (may181749-433044) to boot at 34.148.64.110...
+2022-05-18 17:50:03 Allocated google-nested:ubuntu-16.04-64 (may181749-432987).
+2022-05-18 17:50:03 Connecting to google-nested:ubuntu-16.04-64 (may181749-432987)...
+2022-05-18 17:50:04 Connected to google-nested:ubuntu-16.04-64 (may181749-432987) at 104.196.14.127.
+2022-05-18 17:50:04 Sending project content to google-nested:ubuntu-16.04-64 (may181749-432987)...
+2022-05-18 17:50:06 Preparing google-nested:ubuntu-16.04-64 (may181749-432987)...
+2022-05-18 17:50:08 Allocated google-nested:ubuntu-16.04-64 (may181749-433163).
+2022-05-18 17:50:08 Connecting to google-nested:ubuntu-16.04-64 (may181749-433163)...
+2022-05-18 17:50:09 Connected to google-nested:ubuntu-16.04-64 (may181749-433163) at 35.237.37.145.
+2022-05-18 17:50:09 Sending project content to google-nested:ubuntu-16.04-64 (may181749-433163)...
+2022-05-18 17:50:11 Preparing google-nested:ubuntu-16.04-64 (may181749-433163)...
+2022-05-18 17:50:13 Allocated google-nested:ubuntu-16.04-64 (may181749-433044).
+2022-05-18 17:50:13 Connecting to google-nested:ubuntu-16.04-64 (may181749-433044)...
+2022-05-18 17:50:13 Connected to google-nested:ubuntu-16.04-64 (may181749-433044) at 34.148.64.110.
+2022-05-18 17:50:13 Sending project content to google-nested:ubuntu-16.04-64 (may181749-433044)...
+2022-05-18 17:50:15 Preparing google-nested:ubuntu-16.04-64 (may181749-433044)...
+2022-05-18 17:55:06 WARNING: may181749-432987 (google-nested:ubuntu-16.04-64) running late. Current output:
+-----
+(... 365 lines above ...)
++ restart_logind=
++ loginctl enable-linger test
++ loginctl disable-linger test
+Unpacking systemd (229-4ubuntu21.31) over (229-4ubuntu21.28) ...
+Processing triggers for dbus (1.10.6-1ubuntu3.6) ...
+Processing triggers for ureadahead (0.100.0-19.1) ...
+Processing triggers for man-db (2.7.5-1) ...
+Setting up systemd (229-4ubuntu21.31) ...
+addgroup: The group `systemd-journal' already exists as a system group. Exiting.
+[/usr/lib/tmpfiles.d/var.conf:14] Duplicate line for path "/var/log", ignoring.
+Setting up libpam-systemd:amd64 (229-4ubuntu21.31) ...
+-----
+.
+2022-05-18 18:00:06 WARNING: may181749-432987 (google-nested:ubuntu-16.04-64) running late. Current output:
+-----
+(... 1334 lines above ...)
+dpkg-gencontrol: warning: File::FcntlLock not available; using flock which is not NFS-safe
+dpkg-gencontrol: warning: File::FcntlLock not available; using flock which is not NFS-safe
+dpkg-gencontrol: warning: File::FcntlLock not available; using flock which is not NFS-safe
+dpkg-gencontrol: warning: File::FcntlLock not available; using flock which is not NFS-safe
+make[1]: Leaving directory '/home/gopath/src/github.com/snapcore/snapd'
+ dh_md5sums -O--buildsystem=golang -O--fail-missing -O--builddirectory=_build
+ dh_builddeb -u-Zxz -O--buildsystem=golang -O--fail-missing -O--builddirectory=_build
+dpkg-deb: building package 'golang-github-ubuntu-core-snappy-dev' in '../golang-github-ubuntu-core-snappy-dev_1337.2.55.5_all.deb'.
+dpkg-deb: building package 'golang-github-snapcore-snapd-dev' in '../golang-github-snapcore-snapd-dev_1337.2.55.5_all.deb'.
+dpkg-deb: building package 'snapd' in '../snapd_1337.2.55.5_amd64.deb'.
+-----
+.
+2022-05-18 18:00:11 WARNING: may181749-433163 (google-nested:ubuntu-16.04-64) running late. Current output:
+-----
+(... 1334 lines above ...)
+dpkg-gencontrol: warning: File::FcntlLock not available; using flock which is not NFS-safe
+dpkg-gencontrol: warning: File::FcntlLock not available; using flock which is not NFS-safe
+dpkg-gencontrol: warning: File::FcntlLock not available; using flock which is not NFS-safe
+dpkg-gencontrol: warning: File::FcntlLock not available; using flock which is not NFS-safe
+make[1]: Leaving directory '/home/gopath/src/github.com/snapcore/snapd'
+ dh_md5sums -O--buildsystem=golang -O--fail-missing -O--builddirectory=_build
+ dh_builddeb -u-Zxz -O--buildsystem=golang -O--fail-missing -O--builddirectory=_build
+dpkg-deb: building package 'golang-github-ubuntu-core-snappy-dev' in '../golang-github-ubuntu-core-snappy-dev_1337.2.55.5_all.deb'.
+dpkg-deb: building package 'golang-github-snapcore-snapd-dev' in '../golang-github-snapcore-snapd-dev_1337.2.55.5_all.deb'.
+dpkg-deb: building package 'snapd' in '../snapd_1337.2.55.5_amd64.deb'.
+-----
+.
+2022-05-18 18:00:16 WARNING: may181749-433044 (google-nested:ubuntu-16.04-64) running late. Output unchanged.
+2022-05-18 18:01:03 Preparing google-nested:ubuntu-16.04-64:tests/nested/core/ (may181749-432987)...
+2022-05-18 18:01:17 Preparing google-nested:ubuntu-16.04-64:tests/nested/manual/ (may181749-433163)...
+2022-05-18 18:03:19 Preparing google-nested:ubuntu-16.04-64:tests/nested/manual/cloud-init-never-used-not-vuln:firstboot (may181749-433163)...
+2022-05-18 18:05:16 WARNING: may181749-433044 (google-nested:ubuntu-16.04-64) running late. Output unchanged.
+2022-05-18 18:06:03 WARNING: may181749-432987 (google-nested:ubuntu-16.04-64:tests/nested/core/) running late. Current output:
+-----
+(... 97 lines above ...)
+Parallel unsquashfs: Using 2 processors
+11398 inodes (13347 blocks) to write
+
+[===========================================================\] 13347/13347 100%
+
+created 8997 files
+created 1461 directories
+created 2312 symlinks
+created 79 devices
+created 0 fifos
+-----
+.
+2022-05-18 18:06:52 Executing google-nested:ubuntu-16.04-64:tests/nested/manual/cloud-init-never-used-not-vuln:firstboot (may181749-433163) (1/13)...
+2022-05-18 18:06:58 Preparing google-nested:ubuntu-16.04-64:tests/nested/core/image-build (may181749-432987)...
+2022-05-18 18:07:57 Restoring google-nested:ubuntu-16.04-64:tests/nested/manual/cloud-init-never-used-not-vuln:firstboot (may181749-433163)...
+2022-05-18 18:08:00 Preparing google-nested:ubuntu-16.04-64:tests/nested/manual/cloud-init-nocloud-not-vuln:firstboot (may181749-433163)...
+2022-05-18 18:08:40 Executing google-nested:ubuntu-16.04-64:tests/nested/core/image-build (may181749-432987) (2/13)...
+Error: 2022-05-18 18:09:34 Error preparing google-nested:ubuntu-16.04-64:tests/nested/manual/cloud-init-nocloud-not-vuln:firstboot (may181749-433163) :
+-----
++ /home/gopath/src/github.com/snapcore/snapd/tests/lib/prepare-restore.sh --prepare-project-each
++ set -e
++ . /home/gopath/src/github.com/snapcore/snapd/tests/lib/quiet.sh
++ . /home/gopath/src/github.com/snapcore/snapd/tests/lib/pkgdb.sh
+++ . /home/gopath/src/github.com/snapcore/snapd/tests/lib/quiet.sh
++ . /home/gopath/src/github.com/snapcore/snapd/tests/lib/random.sh
++ . /home/gopath/src/github.com/snapcore/snapd/tests/lib/state.sh
+++ SNAPD_STATE_PATH=/var/tmp/snapd-tools/snapd-state
+++ SNAPD_STATE_FILE=/var/tmp/snapd-tools/snapd-state/snapd-state.tar
+++ RUNTIME_STATE_PATH=/var/tmp/snapd-tools/runtime-state
+++ SNAPD_ACTIVE_UNITS=/var/tmp/snapd-tools/runtime-state/snapd-active-units
+++ . /home/gopath/src/github.com/snapcore/snapd/tests/lib/systemd.sh
++ case "$1" in
++ prepare_project_each
++ dmesg -c
++ fixup_dev_random
++ mv /dev/random /dev/random.orig
++ mknod /dev/random c 1 9
++ kill_gpg_agent
++ pkill -9 -e gpg-agent
+
+May 18 18:50:05 may181749-433044 kernel: veth33879c75: renamed from physLGGOmx
+May 18 18:50:05 may181749-433044 libvirtd[21899]: Failed to open file '/sys/class/net/eth0/operstate': No such file or directory
+May 18 18:50:05 may181749-433044 libvirtd[21899]: unable to read: /sys/class/net/eth0/operstate: No such file or directory
+May 18 18:50:05 may181749-433044 kernel: device veth690dd0d2 left promiscuous mode
+May 18 18:50:05 may181749-433044 kernel: lxdbr0: port 1(veth690dd0d2) entered disabled state
+May 18 18:50:05 may181749-433044 audit[20780]: AVC apparmor="STATUS" operation="profile_remove" profile="unconfined" name="lxd-snapcraft-snapd_" pid=20780 comm="apparmor_parser"
+May 18 18:50:05 may181749-433044 kernel: audit: type=1400 audit(1652899805.951:102): apparmor="STATUS" operation="profile_remove" profile="unconfined" name="lxd-snapcraft-snapd_" pid=20780 comm="apparmor_parser"
+May 18 18:50:07 may181749-433044 ntpd[1698]: Deleting interface #8 lxdbr0, 10.253.239.1#123, interface stats: received=0, sent=0, dropped=0, active_time=851 secs
+May 18 18:50:07 may181749-433044 ntpd[1698]: Deleting interface #9 lxdbr0, fd42:9473:7f21:63ab::1#123, interface stats: received=0, sent=0, dropped=0, active_time=851 secs
+May 18 18:50:07 may181749-433044 ntpd[1698]: Deleting interface #10 lxdbr0, fe80::216:3eff:feec:f39f%5#123, interface stats: received=0, sent=0, dropped=0, active_time=851 secs
++ echo '# tasks executed on system'
+# tasks executed on system
++ echo ''
++ cat /var/tmp/snapd-tools/runtime-state/runs -
+google-nested:ubuntu-16.04-64:tests/nested/classic/hotplug google-nested:ubuntu-16.04-64:tests/nested/classic/snapshots-with-core-refresh-revert google-nested:ubuntu-16.04-64:tests/nested/manual/minimal-smoke:secboot_disabled google-nested:ubuntu-16.04-64:tests/nested/manual/minimal-smoke:secboot_enabled google-nested:ubuntu-16.04-64:tests/nested/manual/devmode-snaps-can-run-other-snaps
+-----
+.
+2022-05-18 16:45:15 Error restoring google:ubuntu-core-22-64:tests/main/ (may181431-047051) : EOF
+2022-05-18 16:45:15 Error debugging google:ubuntu-core-22-64:tests/main/ (may181431-047051) : EOF
+2022-05-18 16:45:15 Restoring google:ubuntu-core-22-64 (may181431-047051)...
+2022-05-18 16:45:15 Error restoring google:ubuntu-core-22-64 (may181431-047051) : EOF
+2022-05-18 16:45:15 Discarding google:ubuntu-core-22-64 (may181431-047051)...
+2022-05-18 16:45:15 Successful tasks: 7376
+2022-05-18 16:45:15 Aborted tasks: 4
+2022-05-18 16:45:15 Failed tasks: 2
+ - google:ubuntu-20.04-64:tests/main/degraded
+ - google:ubuntu-core-22-64:tests/main/dbus-activation-session
+2022-05-18 16:45:15 Failed task prepare: 3
+ - google-nested:ubuntu-16.04-64:tests/nested/manual/cloud-init-never-used-not-vuln:refresh
+ - google-nested:ubuntu-16.04-64:tests/nested/manual/cloud-init-nocloud-not-vuln:firstboot
+ - google:ubuntu-core-22-64:tests/main/parallel-install-desktop
+2022-05-18 16:45:15 Failed task restore: 3
+ - google:ubuntu-20.04-64:tests/main/degraded
+ - google:ubuntu-core-22-64:tests/main/parallel-install-desktop
+ - google-nested:ubuntu-16.04-64:tests/nested/manual/devmode-snaps-can-run-other-snaps
+error: unsuccessful run
\ No newline at end of file
diff --git a/tests/lib/external/snapd-testing-tools/tests/log-parser/with-failed-repeated.log.spread b/tests/lib/external/snapd-testing-tools/tests/log-parser/with-failed-repeated.log.spread
new file mode 100644
index 0000000..76cc171
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/log-parser/with-failed-repeated.log.spread
@@ -0,0 +1,154 @@
+2022-05-18 17:49:24 Project content is packed for delivery (8.86MB).
+2022-05-18 17:49:24 Sequence of jobs produced with -seed=1652896164
+2022-05-18 17:49:24 If killed, discard servers with: spread -reuse-pid=9308 -discard
+2022-05-18 17:49:24 Allocating google-nested:ubuntu-16.04-64...
+2022-05-18 17:49:24 Allocating google-nested:ubuntu-16.04-64...
+2022-05-18 17:49:24 Allocating google-nested:ubuntu-16.04-64...
+2022-05-18 17:49:37 Waiting for google-nested:ubuntu-16.04-64 (may181749-432987) to boot at 104.196.14.127...
+2022-05-18 17:49:37 Waiting for google-nested:ubuntu-16.04-64 (may181749-433163) to boot at 35.237.37.145...
+2022-05-18 17:49:47 Waiting for google-nested:ubuntu-16.04-64 (may181749-433044) to boot at 34.148.64.110...
+2022-05-18 17:50:03 Allocated google-nested:ubuntu-16.04-64 (may181749-432987).
+2022-05-18 17:50:03 Connecting to google-nested:ubuntu-16.04-64 (may181749-432987)...
+2022-05-18 17:50:04 Connected to google-nested:ubuntu-16.04-64 (may181749-432987) at 104.196.14.127.
+2022-05-18 17:50:04 Sending project content to google-nested:ubuntu-16.04-64 (may181749-432987)...
+2022-05-18 17:50:06 Preparing google-nested:ubuntu-16.04-64 (may181749-432987)...
+2022-05-18 17:50:08 Allocated google-nested:ubuntu-16.04-64 (may181749-433163).
+2022-05-18 17:50:08 Connecting to google-nested:ubuntu-16.04-64 (may181749-433163)...
+2022-05-18 17:50:09 Connected to google-nested:ubuntu-16.04-64 (may181749-433163) at 35.237.37.145.
+2022-05-18 17:50:09 Sending project content to google-nested:ubuntu-16.04-64 (may181749-433163)...
+2022-05-18 17:50:11 Preparing google-nested:ubuntu-16.04-64 (may181749-433163)...
+2022-05-18 17:50:13 Allocated google-nested:ubuntu-16.04-64 (may181749-433044).
+2022-05-18 17:50:13 Connecting to google-nested:ubuntu-16.04-64 (may181749-433044)...
+2022-05-18 17:50:13 Connected to google-nested:ubuntu-16.04-64 (may181749-433044) at 34.148.64.110.
+2022-05-18 17:50:13 Sending project content to google-nested:ubuntu-16.04-64 (may181749-433044)...
+2022-05-18 17:50:15 Preparing google-nested:ubuntu-16.04-64 (may181749-433044)...
+2022-05-18 17:55:06 WARNING: may181749-432987 (google-nested:ubuntu-16.04-64) running late. Current output:
+-----
+(... 365 lines above ...)
++ restart_logind=
++ loginctl enable-linger test
++ loginctl disable-linger test
+Unpacking systemd (229-4ubuntu21.31) over (229-4ubuntu21.28) ...
+Processing triggers for dbus (1.10.6-1ubuntu3.6) ...
+Processing triggers for ureadahead (0.100.0-19.1) ...
+Processing triggers for man-db (2.7.5-1) ...
+Setting up systemd (229-4ubuntu21.31) ...
+addgroup: The group `systemd-journal' already exists as a system group. Exiting.
+[/usr/lib/tmpfiles.d/var.conf:14] Duplicate line for path "/var/log", ignoring.
+Setting up libpam-systemd:amd64 (229-4ubuntu21.31) ...
+-----
+.
+2022-05-18 18:00:06 WARNING: may181749-432987 (google-nested:ubuntu-16.04-64) running late. Current output:
+-----
+(... 1334 lines above ...)
+dpkg-gencontrol: warning: File::FcntlLock not available; using flock which is not NFS-safe
+dpkg-gencontrol: warning: File::FcntlLock not available; using flock which is not NFS-safe
+dpkg-gencontrol: warning: File::FcntlLock not available; using flock which is not NFS-safe
+dpkg-gencontrol: warning: File::FcntlLock not available; using flock which is not NFS-safe
+make[1]: Leaving directory '/home/gopath/src/github.com/snapcore/snapd'
+ dh_md5sums -O--buildsystem=golang -O--fail-missing -O--builddirectory=_build
+ dh_builddeb -u-Zxz -O--buildsystem=golang -O--fail-missing -O--builddirectory=_build
+dpkg-deb: building package 'golang-github-ubuntu-core-snappy-dev' in '../golang-github-ubuntu-core-snappy-dev_1337.2.55.5_all.deb'.
+dpkg-deb: building package 'golang-github-snapcore-snapd-dev' in '../golang-github-snapcore-snapd-dev_1337.2.55.5_all.deb'.
+dpkg-deb: building package 'snapd' in '../snapd_1337.2.55.5_amd64.deb'.
+-----
+.
+2022-05-18 18:00:11 WARNING: may181749-433163 (google-nested:ubuntu-16.04-64) running late. Current output:
+-----
+(... 1334 lines above ...)
+dpkg-gencontrol: warning: File::FcntlLock not available; using flock which is not NFS-safe
+dpkg-gencontrol: warning: File::FcntlLock not available; using flock which is not NFS-safe
+dpkg-gencontrol: warning: File::FcntlLock not available; using flock which is not NFS-safe
+dpkg-gencontrol: warning: File::FcntlLock not available; using flock which is not NFS-safe
+make[1]: Leaving directory '/home/gopath/src/github.com/snapcore/snapd'
+ dh_md5sums -O--buildsystem=golang -O--fail-missing -O--builddirectory=_build
+ dh_builddeb -u-Zxz -O--buildsystem=golang -O--fail-missing -O--builddirectory=_build
+dpkg-deb: building package 'golang-github-ubuntu-core-snappy-dev' in '../golang-github-ubuntu-core-snappy-dev_1337.2.55.5_all.deb'.
+dpkg-deb: building package 'golang-github-snapcore-snapd-dev' in '../golang-github-snapcore-snapd-dev_1337.2.55.5_all.deb'.
+dpkg-deb: building package 'snapd' in '../snapd_1337.2.55.5_amd64.deb'.
+-----
+.
+2022-05-18 18:00:16 WARNING: may181749-433044 (google-nested:ubuntu-16.04-64) running late. Output unchanged.
+2022-05-18 18:01:03 Preparing google-nested:ubuntu-16.04-64:tests/nested/core/ (may181749-432987)...
+2022-05-18 18:01:17 Preparing google-nested:ubuntu-16.04-64:tests/nested/manual/ (may181749-433163)...
+2022-05-18 18:03:19 Preparing google-nested:ubuntu-16.04-64:tests/nested/manual/cloud-init-never-used-not-vuln:firstboot (may181749-433163)...
+2022-05-18 18:05:16 WARNING: may181749-433044 (google-nested:ubuntu-16.04-64) running late. Output unchanged.
+2022-05-18 18:06:03 WARNING: may181749-432987 (google-nested:ubuntu-16.04-64:tests/nested/core/) running late. Current output:
+-----
+(... 97 lines above ...)
+Parallel unsquashfs: Using 2 processors
+11398 inodes (13347 blocks) to write
+
+[===========================================================\] 13347/13347 100%
+
+created 8997 files
+created 1461 directories
+created 2312 symlinks
+created 79 devices
+created 0 fifos
+-----
+.
+2022-05-18 18:06:52 Executing google-nested:ubuntu-16.04-64:tests/nested/manual/cloud-init-never-used-not-vuln:firstboot (may181749-433163) (1/13)...
+2022-05-18 18:06:58 Preparing google-nested:ubuntu-16.04-64:tests/nested/core/image-build (may181749-432987)...
+2022-05-18 18:07:57 Restoring google-nested:ubuntu-16.04-64:tests/nested/manual/cloud-init-never-used-not-vuln:firstboot (may181749-433163)...
+2022-05-18 18:08:00 Preparing google-nested:ubuntu-16.04-64:tests/nested/manual/cloud-init-nocloud-not-vuln:firstboot (may181749-433163)...
+2022-05-18 18:08:40 Executing google-nested:ubuntu-16.04-64:tests/nested/core/image-build (may181749-432987) (2/13)...
+Error: 2022-05-18 18:09:34 Error preparing google-nested:ubuntu-16.04-64:tests/nested/manual/cloud-init-nocloud-not-vuln:firstboot (may181749-433163) :
+-----
++ /home/gopath/src/github.com/snapcore/snapd/tests/lib/prepare-restore.sh --prepare-project-each
++ set -e
++ . /home/gopath/src/github.com/snapcore/snapd/tests/lib/quiet.sh
++ . /home/gopath/src/github.com/snapcore/snapd/tests/lib/pkgdb.sh
+++ . /home/gopath/src/github.com/snapcore/snapd/tests/lib/quiet.sh
++ . /home/gopath/src/github.com/snapcore/snapd/tests/lib/random.sh
++ . /home/gopath/src/github.com/snapcore/snapd/tests/lib/state.sh
+++ SNAPD_STATE_PATH=/var/tmp/snapd-tools/snapd-state
+++ SNAPD_STATE_FILE=/var/tmp/snapd-tools/snapd-state/snapd-state.tar
+++ RUNTIME_STATE_PATH=/var/tmp/snapd-tools/runtime-state
+++ SNAPD_ACTIVE_UNITS=/var/tmp/snapd-tools/runtime-state/snapd-active-units
+++ . /home/gopath/src/github.com/snapcore/snapd/tests/lib/systemd.sh
++ case "$1" in
++ prepare_project_each
++ dmesg -c
++ fixup_dev_random
++ mv /dev/random /dev/random.orig
++ mknod /dev/random c 1 9
++ kill_gpg_agent
++ pkill -9 -e gpg-agent
+
+May 18 18:50:05 may181749-433044 kernel: veth33879c75: renamed from physLGGOmx
+May 18 18:50:05 may181749-433044 libvirtd[21899]: Failed to open file '/sys/class/net/eth0/operstate': No such file or directory
+May 18 18:50:05 may181749-433044 libvirtd[21899]: unable to read: /sys/class/net/eth0/operstate: No such file or directory
+May 18 18:50:05 may181749-433044 kernel: device veth690dd0d2 left promiscuous mode
+May 18 18:50:05 may181749-433044 kernel: lxdbr0: port 1(veth690dd0d2) entered disabled state
+May 18 18:50:05 may181749-433044 audit[20780]: AVC apparmor="STATUS" operation="profile_remove" profile="unconfined" name="lxd-snapcraft-snapd_" pid=20780 comm="apparmor_parser"
+May 18 18:50:05 may181749-433044 kernel: audit: type=1400 audit(1652899805.951:102): apparmor="STATUS" operation="profile_remove" profile="unconfined" name="lxd-snapcraft-snapd_" pid=20780 comm="apparmor_parser"
+May 18 18:50:07 may181749-433044 ntpd[1698]: Deleting interface #8 lxdbr0, 10.253.239.1#123, interface stats: received=0, sent=0, dropped=0, active_time=851 secs
+May 18 18:50:07 may181749-433044 ntpd[1698]: Deleting interface #9 lxdbr0, fd42:9473:7f21:63ab::1#123, interface stats: received=0, sent=0, dropped=0, active_time=851 secs
+May 18 18:50:07 may181749-433044 ntpd[1698]: Deleting interface #10 lxdbr0, fe80::216:3eff:feec:f39f%5#123, interface stats: received=0, sent=0, dropped=0, active_time=851 secs
++ echo '# tasks executed on system'
+# tasks executed on system
++ echo ''
++ cat /var/tmp/snapd-tools/runtime-state/runs -
+google-nested:ubuntu-16.04-64:tests/nested/classic/hotplug google-nested:ubuntu-16.04-64:tests/nested/classic/snapshots-with-core-refresh-revert google-nested:ubuntu-16.04-64:tests/nested/manual/minimal-smoke:secboot_disabled google-nested:ubuntu-16.04-64:tests/nested/manual/minimal-smoke:secboot_enabled google-nested:ubuntu-16.04-64:tests/nested/manual/devmode-snaps-can-run-other-snaps
+-----
+.
+2022-05-18 16:45:15 Error restoring google:ubuntu-core-22-64:tests/main/ (may181431-047051) : EOF
+2022-05-18 16:45:15 Error debugging google:ubuntu-core-22-64:tests/main/ (may181431-047051) : EOF
+2022-05-18 16:45:15 Restoring google:ubuntu-core-22-64 (may181431-047051)...
+2022-05-18 16:45:15 Error restoring google:ubuntu-core-22-64 (may181431-047051) : EOF
+2022-05-18 16:45:15 Discarding google:ubuntu-core-22-64 (may181431-047051)...
+2022-05-18 16:45:15 Successful tasks: 7376
+2022-05-18 16:45:15 Aborted tasks: 3
+2022-05-18 16:45:15 Failed tasks: 2
+ - google:ubuntu-20.04-64:tests/main/degraded
+ - google:ubuntu-core-22-64:tests/main/dbus-activation-session
+2022-05-18 16:45:15 Failed task prepare: 3
+ - google-nested:ubuntu-16.04-64:tests/nested/manual/cloud-init-never-used-not-vuln:refresh
+ - google-nested:ubuntu-16.04-64:tests/nested/manual/cloud-init-nocloud-not-vuln:firstboot
+ - google:ubuntu-core-22-64:tests/main/parallel-install-desktop
+2022-05-18 16:45:15 Failed task restore: 3
+ - google:ubuntu-20.04-64:tests/main/degraded
+ - google:ubuntu-core-22-64:tests/main/parallel-install-desktop
+ - google-nested:ubuntu-16.04-64:tests/nested/manual/devmode-snaps-can-run-other-snaps
+error: unsuccessful run
\ No newline at end of file
diff --git a/tests/lib/external/snapd-testing-tools/tests/log-parser/with-failed-suite-restore.log.spread b/tests/lib/external/snapd-testing-tools/tests/log-parser/with-failed-suite-restore.log.spread
new file mode 100644
index 0000000..21f7c19
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/log-parser/with-failed-suite-restore.log.spread
@@ -0,0 +1,146 @@
+2022-05-18 17:49:24 Project content is packed for delivery (8.86MB).
+2022-05-18 17:49:24 Sequence of jobs produced with -seed=1652896164
+2022-05-18 17:49:24 If killed, discard servers with: spread -reuse-pid=9308 -discard
+2022-05-18 17:49:24 Allocating google-nested:ubuntu-16.04-64...
+2022-05-18 17:49:24 Allocating google-nested:ubuntu-16.04-64...
+2022-05-18 17:49:24 Allocating google-nested:ubuntu-16.04-64...
+2022-05-18 17:49:37 Waiting for google-nested:ubuntu-16.04-64 (may181749-432987) to boot at 104.196.14.127...
+2022-05-18 17:49:37 Waiting for google-nested:ubuntu-16.04-64 (may181749-433163) to boot at 35.237.37.145...
+2022-05-18 17:49:47 Waiting for google-nested:ubuntu-16.04-64 (may181749-433044) to boot at 34.148.64.110...
+2022-05-18 17:50:03 Allocated google-nested:ubuntu-16.04-64 (may181749-432987).
+2022-05-18 17:50:03 Connecting to google-nested:ubuntu-16.04-64 (may181749-432987)...
+2022-05-18 17:50:04 Connected to google-nested:ubuntu-16.04-64 (may181749-432987) at 104.196.14.127.
+2022-05-18 17:50:04 Sending project content to google-nested:ubuntu-16.04-64 (may181749-432987)...
+2022-05-18 17:50:06 Preparing google-nested:ubuntu-16.04-64 (may181749-432987)...
+2022-05-18 17:50:08 Allocated google-nested:ubuntu-16.04-64 (may181749-433163).
+2022-05-18 17:50:08 Connecting to google-nested:ubuntu-16.04-64 (may181749-433163)...
+2022-05-18 17:50:09 Connected to google-nested:ubuntu-16.04-64 (may181749-433163) at 35.237.37.145.
+2022-05-18 17:50:09 Sending project content to google-nested:ubuntu-16.04-64 (may181749-433163)...
+2022-05-18 17:50:11 Preparing google-nested:ubuntu-16.04-64 (may181749-433163)...
+2022-05-18 17:50:13 Allocated google-nested:ubuntu-16.04-64 (may181749-433044).
+2022-05-18 17:50:13 Connecting to google-nested:ubuntu-16.04-64 (may181749-433044)...
+2022-05-18 17:50:13 Connected to google-nested:ubuntu-16.04-64 (may181749-433044) at 34.148.64.110.
+2022-05-18 17:50:13 Sending project content to google-nested:ubuntu-16.04-64 (may181749-433044)...
+2022-05-18 17:50:15 Preparing google-nested:ubuntu-16.04-64 (may181749-433044)...
+2022-05-18 17:55:06 WARNING: may181749-432987 (google-nested:ubuntu-16.04-64) running late. Current output:
+-----
+(... 365 lines above ...)
++ restart_logind=
++ loginctl enable-linger test
++ loginctl disable-linger test
+Unpacking systemd (229-4ubuntu21.31) over (229-4ubuntu21.28) ...
+Processing triggers for dbus (1.10.6-1ubuntu3.6) ...
+Processing triggers for ureadahead (0.100.0-19.1) ...
+Processing triggers for man-db (2.7.5-1) ...
+Setting up systemd (229-4ubuntu21.31) ...
+addgroup: The group `systemd-journal' already exists as a system group. Exiting.
+[/usr/lib/tmpfiles.d/var.conf:14] Duplicate line for path "/var/log", ignoring.
+Setting up libpam-systemd:amd64 (229-4ubuntu21.31) ...
+-----
+.
+2022-05-18 18:00:06 WARNING: may181749-432987 (google-nested:ubuntu-16.04-64) running late. Current output:
+-----
+(... 1334 lines above ...)
+dpkg-gencontrol: warning: File::FcntlLock not available; using flock which is not NFS-safe
+dpkg-gencontrol: warning: File::FcntlLock not available; using flock which is not NFS-safe
+dpkg-gencontrol: warning: File::FcntlLock not available; using flock which is not NFS-safe
+dpkg-gencontrol: warning: File::FcntlLock not available; using flock which is not NFS-safe
+make[1]: Leaving directory '/home/gopath/src/github.com/snapcore/snapd'
+ dh_md5sums -O--buildsystem=golang -O--fail-missing -O--builddirectory=_build
+ dh_builddeb -u-Zxz -O--buildsystem=golang -O--fail-missing -O--builddirectory=_build
+dpkg-deb: building package 'golang-github-ubuntu-core-snappy-dev' in '../golang-github-ubuntu-core-snappy-dev_1337.2.55.5_all.deb'.
+dpkg-deb: building package 'golang-github-snapcore-snapd-dev' in '../golang-github-snapcore-snapd-dev_1337.2.55.5_all.deb'.
+dpkg-deb: building package 'snapd' in '../snapd_1337.2.55.5_amd64.deb'.
+-----
+.
+2022-05-18 18:00:11 WARNING: may181749-433163 (google-nested:ubuntu-16.04-64) running late. Current output:
+-----
+(... 1334 lines above ...)
+dpkg-gencontrol: warning: File::FcntlLock not available; using flock which is not NFS-safe
+dpkg-gencontrol: warning: File::FcntlLock not available; using flock which is not NFS-safe
+dpkg-gencontrol: warning: File::FcntlLock not available; using flock which is not NFS-safe
+dpkg-gencontrol: warning: File::FcntlLock not available; using flock which is not NFS-safe
+make[1]: Leaving directory '/home/gopath/src/github.com/snapcore/snapd'
+ dh_md5sums -O--buildsystem=golang -O--fail-missing -O--builddirectory=_build
+ dh_builddeb -u-Zxz -O--buildsystem=golang -O--fail-missing -O--builddirectory=_build
+dpkg-deb: building package 'golang-github-ubuntu-core-snappy-dev' in '../golang-github-ubuntu-core-snappy-dev_1337.2.55.5_all.deb'.
+dpkg-deb: building package 'golang-github-snapcore-snapd-dev' in '../golang-github-snapcore-snapd-dev_1337.2.55.5_all.deb'.
+dpkg-deb: building package 'snapd' in '../snapd_1337.2.55.5_amd64.deb'.
+-----
+.
+2022-05-18 18:00:16 WARNING: may181749-433044 (google-nested:ubuntu-16.04-64) running late. Output unchanged.
+2022-05-18 18:01:03 Preparing google-nested:ubuntu-16.04-64:tests/nested/core/ (may181749-432987)...
+2022-05-18 18:01:17 Preparing google-nested:ubuntu-16.04-64:tests/nested/manual/ (may181749-433163)...
+2022-05-18 18:03:19 Preparing google-nested:ubuntu-16.04-64:tests/nested/manual/cloud-init-never-used-not-vuln:firstboot (may181749-433163)...
+2022-05-18 18:05:16 WARNING: may181749-433044 (google-nested:ubuntu-16.04-64) running late. Output unchanged.
+2022-05-18 18:06:03 WARNING: may181749-432987 (google-nested:ubuntu-16.04-64:tests/nested/core/) running late. Current output:
+-----
+(... 97 lines above ...)
+Parallel unsquashfs: Using 2 processors
+11398 inodes (13347 blocks) to write
+
+[===========================================================\] 13347/13347 100%
+
+created 8997 files
+created 1461 directories
+created 2312 symlinks
+created 79 devices
+created 0 fifos
+-----
+.
+2022-05-18 18:06:52 Executing google-nested:ubuntu-16.04-64:tests/nested/manual/cloud-init-never-used-not-vuln:firstboot (may181749-433163) (1/13)...
+2022-05-18 18:06:58 Preparing google-nested:ubuntu-16.04-64:tests/nested/core/image-build (may181749-432987)...
+2022-05-18 18:07:57 Restoring google-nested:ubuntu-16.04-64:tests/nested/manual/cloud-init-never-used-not-vuln:firstboot (may181749-433163)...
+2022-05-18 18:08:00 Preparing google-nested:ubuntu-16.04-64:tests/nested/manual/cloud-init-nocloud-not-vuln:firstboot (may181749-433163)...
+2022-05-18 18:08:40 Executing google-nested:ubuntu-16.04-64:tests/nested/core/image-build (may181749-432987) (2/13)...
+Error: 2022-05-18 18:09:34 Error preparing google-nested:ubuntu-16.04-64:tests/nested/manual/cloud-init-nocloud-not-vuln:firstboot (may181749-433163) :
+-----
++ /home/gopath/src/github.com/snapcore/snapd/tests/lib/prepare-restore.sh --prepare-project-each
++ set -e
++ . /home/gopath/src/github.com/snapcore/snapd/tests/lib/quiet.sh
++ . /home/gopath/src/github.com/snapcore/snapd/tests/lib/pkgdb.sh
+++ . /home/gopath/src/github.com/snapcore/snapd/tests/lib/quiet.sh
++ . /home/gopath/src/github.com/snapcore/snapd/tests/lib/random.sh
++ . /home/gopath/src/github.com/snapcore/snapd/tests/lib/state.sh
+++ SNAPD_STATE_PATH=/var/tmp/snapd-tools/snapd-state
+++ SNAPD_STATE_FILE=/var/tmp/snapd-tools/snapd-state/snapd-state.tar
+++ RUNTIME_STATE_PATH=/var/tmp/snapd-tools/runtime-state
+++ SNAPD_ACTIVE_UNITS=/var/tmp/snapd-tools/runtime-state/snapd-active-units
+++ . /home/gopath/src/github.com/snapcore/snapd/tests/lib/systemd.sh
++ case "$1" in
++ prepare_project_each
++ dmesg -c
++ fixup_dev_random
++ mv /dev/random /dev/random.orig
++ mknod /dev/random c 1 9
++ kill_gpg_agent
++ pkill -9 -e gpg-agent
+
+May 18 18:50:05 may181749-433044 kernel: veth33879c75: renamed from physLGGOmx
+May 18 18:50:05 may181749-433044 libvirtd[21899]: Failed to open file '/sys/class/net/eth0/operstate': No such file or directory
+May 18 18:50:05 may181749-433044 libvirtd[21899]: unable to read: /sys/class/net/eth0/operstate: No such file or directory
+May 18 18:50:05 may181749-433044 kernel: device veth690dd0d2 left promiscuous mode
+May 18 18:50:05 may181749-433044 kernel: lxdbr0: port 1(veth690dd0d2) entered disabled state
+May 18 18:50:05 may181749-433044 audit[20780]: AVC apparmor="STATUS" operation="profile_remove" profile="unconfined" name="lxd-snapcraft-snapd_" pid=20780 comm="apparmor_parser"
+May 18 18:50:05 may181749-433044 kernel: audit: type=1400 audit(1652899805.951:102): apparmor="STATUS" operation="profile_remove" profile="unconfined" name="lxd-snapcraft-snapd_" pid=20780 comm="apparmor_parser"
+May 18 18:50:07 may181749-433044 ntpd[1698]: Deleting interface #8 lxdbr0, 10.253.239.1#123, interface stats: received=0, sent=0, dropped=0, active_time=851 secs
+May 18 18:50:07 may181749-433044 ntpd[1698]: Deleting interface #9 lxdbr0, fd42:9473:7f21:63ab::1#123, interface stats: received=0, sent=0, dropped=0, active_time=851 secs
+May 18 18:50:07 may181749-433044 ntpd[1698]: Deleting interface #10 lxdbr0, fe80::216:3eff:feec:f39f%5#123, interface stats: received=0, sent=0, dropped=0, active_time=851 secs
++ echo '# tasks executed on system'
+# tasks executed on system
++ echo ''
++ cat /var/tmp/snapd-tools/runtime-state/runs -
+google-nested:ubuntu-16.04-64:tests/nested/classic/hotplug google-nested:ubuntu-16.04-64:tests/nested/classic/snapshots-with-core-refresh-revert google-nested:ubuntu-16.04-64:tests/nested/manual/minimal-smoke:secboot_disabled google-nested:ubuntu-16.04-64:tests/nested/manual/minimal-smoke:secboot_enabled google-nested:ubuntu-16.04-64:tests/nested/manual/devmode-snaps-can-run-other-snaps
+-----
+.
+2022-05-18 16:45:15 Error restoring google:ubuntu-core-22-64:tests/main/ (may181431-047051) : EOF
+2022-05-18 16:45:15 Error debugging google:ubuntu-core-22-64:tests/main/ (may181431-047051) : EOF
+2022-05-18 16:45:15 Restoring google:ubuntu-core-22-64 (may181431-047051)...
+2022-05-18 16:45:15 Error restoring google:ubuntu-core-22-64 (may181431-047051) : EOF
+2022-05-18 16:45:15 Discarding google:ubuntu-core-22-64 (may181431-047051)...
+2022-05-18 16:45:15 Successful tasks: 7376
+2022-05-18 16:45:15 Aborted tasks: 0
+2022-05-18 16:45:15 Failed suite restore: 2
+ - google:ubuntu-core-22-64:tests/main/
+ - google:ubuntu-core-22-64:tests/main/
+error: unsuccessful run
\ No newline at end of file
diff --git a/tests/lib/external/snapd-testing-tools/tests/log-parser/with-failed.log.spread b/tests/lib/external/snapd-testing-tools/tests/log-parser/with-failed.log.spread
new file mode 100644
index 0000000..a98e1f5
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/log-parser/with-failed.log.spread
@@ -0,0 +1,216 @@
+2021-01-20 12:28:57 Project content is packed for delivery (266.41KB).
+2021-01-20 12:28:57 Sequence of jobs produced with -seed=1611145737
+2021-01-20 12:28:57 If killed, discard servers with: spread -reuse-pid=24265 -discard
+2021-01-20 12:28:57 Allocating google:centos-8-64...
+2021-01-20 12:29:10 Waiting for google:centos-8-64 (jan201228-089626) to boot at 35.229.96.196...
+2021-01-20 12:29:33 Sending project content to google:centos-8-64 (jan201228-089626)...
+2021-01-20 12:29:33 Connected to google:centos-8-64 (jan201228-089359) at 34.73.106.247.
+2021-01-20 12:29:33 Sending project content to google:centos-8-64 (jan201228-089359)...
+2021-01-20 12:29:33 Connected to google:centos-8-64 (jan201228-089636) at 35.227.44.209.
+2021-01-20 12:29:33 Sending project content to google:centos-8-64 (jan201228-089636)...
+2021-01-20 12:29:35 Preparing google:centos-8-64 (jan201228-089626)...
+2021-01-20 12:29:35 Preparing google:centos-8-64 (jan201228-089596)...
+2021-01-20 12:29:35 Preparing google:centos-8-64 (jan201228-089359)...
+2021-01-20 12:29:35 Preparing google:centos-8-64 (jan201228-089636)...
+2021-01-20 12:34:36 WARNING: jan201228-089636 (google:centos-8-64) running late. Current output:
+-----
+(... 376 lines above ...)
+pkgconfig(libseccomp)
+pkgconfig(libselinux)
+pkgconfig(libudev)
+pkgconfig(systemd)
+pkgconfig(udev)
+selinux-policy
+selinux-policy-devel
+systemd
+valgrind
+xfsprogs-devel
+-----
+.
+2021-01-20 12:42:47 Restoring google:centos-8-64:tests/completion/snippets:hosts (jan201228-089636)...
+2021-01-20 12:42:48 Preparing google:centos-8-64:tests/completion/indirect:func (jan201228-089636)...
+2021-01-20 12:42:50 Preparing google:centos-8-64:tests/regression/lp-1844496 (jan201228-089359)...
+2021-01-20 12:42:54 Executing google:centos-8-64:tests/completion/indirect:func (jan201228-089636) (2/435)...
+2021-01-20 12:42:55 Preparing google:centos-8-64:tests/main/degraded (jan201228-089626)...
+2021-01-20 12:43:01 Executing google:centos-8-64:tests/main/degraded (jan201228-089626) (3/435)...
+2021-01-20 12:43:01 Restoring google:centos-8-64:tests/main/degraded (jan201228-089626)...
+2021-01-20 12:43:02 Preparing google:centos-8-64:tests/main/base-snaps (jan201228-089626)...
+2021-01-20 12:43:03 Executing google:centos-8-64:tests/regression/lp-1844496 (jan201228-089359) (4/435)...
+2021-01-20 12:43:06 Executing google:centos-8-64:tests/main/base-snaps (jan201228-089626) (5/435)...
+2021-01-20 12:43:07 Restoring google:centos-8-64:tests/regression/lp-1844496 (jan201228-089359)...
+2021-01-20 12:43:09 Preparing google:centos-8-64:tests/regression/lp-1812973 (jan201228-089359)...
+2021-01-20 12:43:15 Executing google:centos-8-64:tests/regression/lp-1812973 (jan201228-089359) (6/435)...
+2021-01-20 12:43:16 Restoring google:centos-8-64:tests/regression/lp-1812973 (jan201228-089359)...
+2021-01-20 12:43:16 Restoring google:centos-8-64:tests/main/base-snaps (jan201228-089626)...
+2021-01-20 12:43:17 Preparing google:centos-8-64:tests/main/snap-validate-basic (jan201228-089626)...
+2021-01-20 12:43:18 Preparing google:centos-8-64:tests/regression/lp-1665004 (jan201228-089359)...
+2021-01-20 12:43:22 Executing google:centos-8-64:tests/main/snap-validate-basic (jan201228-089626) (7/435)...
+2021-01-20 12:43:22 Restoring google:centos-8-64:tests/main/snap-validate-basic (jan201228-089626)...
+2021-01-20 12:43:23 Preparing google:centos-8-64:tests/main/interfaces-broadcom-asic-control (jan201228-089596)...
+2021-01-20 12:43:23 Preparing google:centos-8-64:tests/main/media-sharing (jan201228-089626)...
+2021-01-20 12:43:24 Executing google:centos-8-64:tests/regression/lp-1665004 (jan201228-089359) (8/435)...
+2021-01-20 12:43:24 Restoring google:centos-8-64:tests/regression/lp-1665004 (jan201228-089359)...
+2021-01-20 12:43:25 Preparing google:centos-8-64:tests/regression/lp-1797556 (jan201228-089359)...
+2021-01-20 12:43:30 Executing google:centos-8-64:tests/main/interfaces-broadcom-asic-control (jan201228-089596) (9/435)...
+2021-01-20 12:43:32 Executing google:centos-8-64:tests/regression/lp-1797556 (jan201228-089359) (10/435)...
+2021-01-20 12:43:32 Restoring google:centos-8-64:tests/regression/lp-1797556 (jan201228-089359)...
+2021-01-20 12:43:32 Restoring google:centos-8-64:tests/main/interfaces-broadcom-asic-control (jan201228-089596)...
+2021-01-20 12:48:55 Restoring google:centos-8-64:tests/lib/tools/suite/mountinfo.query (jan201228-089359)...
+2021-01-20 12:48:56 Preparing google:centos-8-64:tests/lib/tools/suite/tests.pkgs (jan201228-089359)...
+2021-01-20 12:48:57 Executing google:centos-8-64:tests/lib/tools/suite/tests.pkgs (jan201228-089359) (90/435)...
+2021-01-20 12:48:58 Executing google:centos-8-64:tests/main/security-devpts (jan201228-089596) (91/435)...
+2021-01-20 12:49:00 Restoring google:centos-8-64:tests/main/security-devpts (jan201228-089596)...
+2021-01-20 12:49:01 Preparing google:centos-8-64:tests/main/interfaces-content-circular (jan201228-089596)...
+2021-01-20 12:49:06 Executing google:centos-8-64:tests/main/interfaces-content-circular (jan201228-089596) (92/435)...
+2021-01-20 12:49:11 Restoring google:centos-8-64:tests/completion/indirect:funcarg (jan201228-089636)...
+2021-01-20 12:49:13 Preparing google:centos-8-64:tests/completion/simple:hosts (jan201228-089636)...
+2021-01-20 12:49:17 Restoring google:centos-8-64:tests/main/services-snapctl (jan201228-089626)...
+2021-01-20 12:49:17 Executing google:centos-8-64:tests/completion/simple:hosts (jan201228-089636) (93/435)...
+2021-01-20 12:49:17 Preparing google:centos-8-64:tests/main/services-refresh-mode (jan201228-089626)...
+2021-01-20 12:49:21 Error executing google:centos-8-64:tests/lib/tools/suite/tests.pkgs (jan201228-089359) :
+-----
++ os.query is-core
++ MATCH 'usage: tests.pkgs {install,remove} \[PACKAGE...\]'
++ tests.pkgs
++ tests.pkgs -h
++ MATCH 'usage: tests.pkgs {install,remove} \[PACKAGE...\]'
++ tests.pkgs --help
++ MATCH 'usage: tests.pkgs {install,remove} \[PACKAGE...\]'
++ not tests.pkgs is-installed test-snapd-pkg-1
++ rpm -qi robotfindskitten
++ tests.pkgs install test-snapd-pkg-1
+++ command -v dnf
++ '[' /usr/bin/dnf '!=' '' ']'
++ dnf install -y robotfindskitten
+CentOS-8 - AppStream 3.6 MB/s | 6.3 MB 00:01
+CentOS-8 - Base 5.3 MB/s | 2.3 MB 00:00
+CentOS-8 - Extras 3.5 kB/s | 8.6 kB 00:02
+CentOS-8 - PowerTools 1.3 MB/s | 2.0 MB 00:01
+Extra Packages for Enterprise Linux Modular 8 - 708 kB/s | 537 kB 00:00
+Extra Packages for Enterprise Linux 8 - x86_64 14 MB/s | 8.8 MB 00:00
+Google Compute Engine 8.9 kB/s | 7.4 kB 00:00
+Google Cloud SDK 18 MB/s | 24 MB 00:01
+No match for argument: robotfindskitten
+Error: Unable to find a match: robotfindskitten
+-----
+.
+2021-01-20 12:49:22 Debug output for google:centos-8-64:tests/lib/tools/suite/tests.pkgs (jan201228-089359) :
+-----
++ '[' 1 = 1 ']'
++ . /home/gopath/src/github.com/snapcore/snapd/tests/lib/state.sh
+++ SNAPD_STATE_PATH=/home/gopath/src/github.com/snapcore/snapd/tests/snapd-state
+++ SNAPD_STATE_FILE=/home/gopath/src/github.com/snapcore/snapd/tests/snapd-state/snapd-state.tar
+++ RUNTIME_STATE_PATH=/home/gopath/src/github.com/snapcore/snapd/tests/runtime-state
+++ SNAPD_ACTIVE_UNITS=/home/gopath/src/github.com/snapcore/snapd/tests/runtime-state/snapd-active-units
+++ . /home/gopath/src/github.com/snapcore/snapd/tests/lib/dirs.sh
++++ export SNAP_MOUNT_DIR=/snap
++++ SNAP_MOUNT_DIR=/snap
++++ export LIBEXECDIR=/usr/lib
++++ LIBEXECDIR=/usr/lib
++++ export MEDIA_DIR=/media
++++ MEDIA_DIR=/media
++++ case "$SPREAD_SYSTEM" in
++++ export SNAP_MOUNT_DIR=/var/lib/snapd/snap
++++ SNAP_MOUNT_DIR=/var/lib/snapd/snap
++++ export LIBEXECDIR=/usr/libexec
++++ LIBEXECDIR=/usr/libexec
++++ export MEDIA_DIR=/run/media
++++ MEDIA_DIR=/run/media
+++ . /home/gopath/src/github.com/snapcore/snapd/tests/lib/systemd.sh
++ . /home/gopath/src/github.com/snapcore/snapd/tests/lib/nested.sh
+++ . /home/gopath/src/github.com/snapcore/snapd/tests/lib/systemd.sh
+++ . /home/gopath/src/github.com/snapcore/snapd/tests/lib/store.sh
++++ STORE_CONFIG=/etc/systemd/system/snapd.service.d/store.conf
++++ . /home/gopath/src/github.com/snapcore/snapd/tests/lib/systemd.sh
+++ NESTED_WORK_DIR=/tmp/work-dir
+++ NESTED_IMAGES_DIR=/tmp/work-dir/images
+++ NESTED_RUNTIME_DIR=/tmp/work-dir/runtime
+++ NESTED_ASSETS_DIR=/tmp/work-dir/assets
+++ NESTED_LOGS_DIR=/tmp/work-dir/logs
+++ NESTED_VM=nested-vm
+++ NESTED_SSH_PORT=8022
+++ NESTED_MON_PORT=8888
+++ NESTED_CUSTOM_MODEL=
+++ NESTED_CUSTOM_AUTO_IMPORT_ASSERTION=
+++ NESTED_FAKESTORE_BLOB_DIR=/tmp/work-dir/fakestore/blobs
+++ NESTED_SIGN_SNAPS_FAKESTORE=false
+++ NESTED_UBUNTU_IMAGE_SNAPPY_FORCE_SAS_URL=
++ echo '# System information'
+# System information
++ cat /etc/os-release
+NAME="CentOS Linux"
+VERSION="8 (Core)"
+ID="centos"
+ID_LIKE="rhel fedora"
+VERSION_ID="8"
+PLATFORM_ID="platform:el8"
+PRETTY_NAME="CentOS Linux 8 (Core)"
+ANSI_COLOR="0;31"
+CPE_NAME="cpe:/o:centos:centos:8"
+HOME_URL="https://www.centos.org/"
+BUG_REPORT_URL="https://bugs.centos.org/"
+
+CENTOS_MANTISBT_PROJECT="CentOS-8"
+CENTOS_MANTISBT_PROJECT_VERSION="8"
+REDHAT_SUPPORT_PRODUCT="centos"
+REDHAT_SUPPORT_PRODUCT_VERSION="8"
+
+# snap connections --all
++ snap connections --all
+/bin/bash: line 129: snap: command not found
++ true
++ echo '# tasks executed on system'
+# tasks executed on system
++ cat /home/gopath/src/github.com/snapcore/snapd/tests/runtime-state/runs
+google:centos-8-64:tests/regression/lp-1844496 google:centos-8-64:tests/regression/lp-1812973 google:centos-8-64:tests/regression/lp-1665004 google:centos-8-64:tests/regression/lp-1797556 google:centos-8-64:tests/regression/lp-1803542:private google:centos-8-64:tests/regression/lp-1848567 google:centos-8-64:tests/regression/lp-1803535 google:centos-8-64:tests/regression/lp-1630479 google:centos-8-64:tests/regression/lp-1891371 google:centos-8-64:tests/regression/lp-1898038 google:centos-8-64:tests/regression/lp-1825883 google:centos-8-64:tests/regression/lp-1615113 google:centos-8-64:tests/regression/lp-1862637 google:centos-8-64:tests/regression/lp-1852361 google:centos-8-64:tests/regression/rhbz-1708991 google:centos-8-64:tests/regression/lp-1808821 google:centos-8-64:tests/regression/lp-1801955 google:centos-8-64:tests/regression/lp-1732555 google:centos-8-64:tests/regression/lp-1803542:shared google:centos-8-64:tests/regression/lp-1831010 google:centos-8-64:tests/regression/lp-1597839 google:centos-8-64:tests/regression/lp-1606277 google:centos-8-64:tests/regression/lp-1805838 google:centos-8-64:tests/regression/lp-1800004 google:centos-8-64:tests/regression/lp-1607796 google:centos-8-64:tests/regression/lp-1597842 google:centos-8-64:tests/regression/lp-1815722 google:centos-8-64:tests/regression/lp-1886786 google:centos-8-64:tests/regression/lp-1693042 google:centos-8-64:tests/regression/lp-1764977 google:centos-8-64:tests/regression/lp-1618683 google:centos-8-64:tests/regression/lp-1803542:absent google:centos-8-64:tests/regression/lp-1803542:present google:centos-8-64:tests/regression/lp-1849845 google:centos-8-64:tests/lib/tools/suite/mountinfo.query google:centos-8-64:tests/lib/tools/suite/tests.pkgs + echo '# free space'
+# /var/lib/snapd
++ find /var/lib/snapd/ -not -path '/var/lib/snapd/snap/*' -ls
+find: ‘/var/lib/snapd/’: No such file or directory
++ true
+-----
+.
+2021-01-20 12:49:22 Discarding google:centos-8-64 (jan201228-089359)...
+2021-01-20 12:49:24 Restoring google:centos-8-64:tests/completion/simple:hosts (jan201228-089636)...
+2021-01-20 12:49:24 Preparing google:centos-8-64:tests/completion/indirect:hosts_n_dirs (jan201228-089636)...
+2021-01-20 12:49:30 Executing google:centos-8-64:tests/completion/indirect:hosts_n_dirs (jan201228-089636) (94/435)...
+2021-01-20 12:49:36 Executing google:centos-8-64:tests/main/services-refresh-mode (jan201228-089626) (95/435)...
+2021-01-20 12:49:42 Restoring google:centos-8-64:tests/main/interfaces-content-circular (jan201228-089596)...
+2021-01-20 12:49:43 Preparing google:centos-8-64:tests/main/refresh:strict_remote (jan201228-089596)...
+2021-01-20 12:53:24 Preparing google:centos-8-64:tests/main/base-snaps-refresh (jan201228-089626)...
+2021-01-20 12:53:29 Executing google:centos-8-64:tests/main/base-snaps-refresh (jan201228-089626) (124/435)...
+2021-01-20 12:53:38 Restoring google:centos-8-64:tests/main/base-snaps-refresh (jan201228-089626)...
+2021-01-20 12:53:38 Restoring google:centos-8-64:tests/main/snapd-snap-removal (jan201228-089596)...
+2021-01-20 12:53:39 Preparing google:centos-8-64:tests/main/interfaces-daemon-notify (jan201228-089626)...
+2021-01-20 12:53:39 Preparing google:centos-8-64:tests/main/interfaces-content-mkdir-writable:data (jan201228-089596)...
+2021-01-20 12:53:45 Executing google:centos-8-64:tests/main/interfaces-daemon-notify (jan201228-089626) (125/435)...
+2021-01-20 12:53:46 Executing google:centos-8-64:tests/main/interfaces-content-mkdir-writable:data (jan201228-089596) (126/435)...
+2021-01-20 12:53:48 Restoring google:centos-8-64:tests/main/interfaces-content-mkdir-writable:data (jan201228-089596)...
+2021-01-20 12:53:49 Preparing google:centos-8-64:tests/main/refresh:parallel_strict_remote (jan201228-089596)...
+2021-01-20 12:53:56 Restoring google:centos-8-64:tests/completion/indirect:plain_plusdirs (jan201228-089636)...
+2021-01-20 12:53:57 Preparing google:centos-8-64:tests/completion/simple:hosts_n_dirs (jan201228-089636)...
+2021-01-20 12:53:57 Restoring google:centos-8-64:tests/main/interfaces-daemon-notify (jan201228-089626)...
+2021-01-20 12:53:58 Preparing google:centos-8-64:tests/main/core18-configure-hook (jan201228-089626)...
+2021-01-20 12:54:01 Executing google:centos-8-64:tests/main/refresh:parallel_strict_remote (jan201228-089596) (127/435)...
+2021-01-20 12:54:02 Executing google:centos-8-64:tests/completion/simple:hosts_n_dirs (jan201228-089636) (128/435)...
+2021-01-20 12:59:22 Preparing google:centos-8-64:tests/main/interfaces-fuse-support:regular (jan201228-089596)...
+2021-01-20 13:29:28 Restoring google:centos-8-64:tests/main/core18-with-hooks (jan201228-089626)...
+2021-01-20 13:29:31 Preparing google:centos-8-64:tests/main/proxy (jan201228-089626)...
+2021-01-20 13:29:32 Executing google:centos-8-64:tests/main/services-watchdog (jan201228-089596) (434/435)...
+2021-01-20 13:29:36 Executing google:centos-8-64:tests/main/proxy (jan201228-089626) (435/435)...
+2021-01-20 13:29:38 Restoring google:centos-8-64:tests/main/proxy (jan201228-089626)...
+2021-01-20 13:29:39 Restoring google:centos-8-64:tests/main/services-watchdog (jan201228-089596)...
+2021-01-20 13:29:39 Restoring google:centos-8-64:tests/main/ (jan201228-089626)...
+2021-01-20 13:29:40 Restoring google:centos-8-64:tests/main/ (jan201228-089596)...
+2021-01-20 13:29:46 Restoring google:centos-8-64 (jan201228-089626)...
+2021-01-20 13:29:47 Restoring google:centos-8-64 (jan201228-089596)...
+2021-01-20 13:29:48 Discarding google:centos-8-64 (jan201228-089626)...
+2021-01-20 13:29:49 Discarding google:centos-8-64 (jan201228-089596)...
+2021-01-20 13:30:03 Restoring google:centos-8-64:tests/main/auto-refresh-retry (jan201228-089636)...
+2021-01-20 13:30:04 Restoring google:centos-8-64:tests/main/ (jan201228-089636)...
+2021-01-20 13:30:11 Restoring google:centos-8-64 (jan201228-089636)...
+2021-01-20 13:30:12 Discarding google:centos-8-64 (jan201228-089636)...
+error: unsuccessful run
+2021-01-20 13:30:13 Successful tasks: 434
+2021-01-20 13:30:13 Aborted tasks: 0
+2021-01-20 13:30:13 Failed tasks: 1
+ - google:centos-8-64:tests/lib/tools/suite/tests.pkgs
\ No newline at end of file
diff --git a/tests/lib/external/snapd-testing-tools/tests/log-parser/with-results-in-detail.log.spread b/tests/lib/external/snapd-testing-tools/tests/log-parser/with-results-in-detail.log.spread
new file mode 100644
index 0000000..bd69f04
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/log-parser/with-results-in-detail.log.spread
@@ -0,0 +1,149 @@
+2022-05-18 17:49:24 Project content is packed for delivery (8.86MB).
+2022-05-18 17:49:24 Sequence of jobs produced with -seed=1652896164
+2022-05-18 17:49:24 If killed, discard servers with: spread -reuse-pid=9308 -discard
+2022-05-18 17:49:24 Allocating google-nested:ubuntu-16.04-64...
+2022-05-18 17:49:24 Allocating google-nested:ubuntu-16.04-64...
+2022-05-18 17:49:24 Allocating google-nested:ubuntu-16.04-64...
+2022-05-18 17:49:37 Waiting for google-nested:ubuntu-16.04-64 (may181749-432987) to boot at 104.196.14.127...
+2022-05-18 17:49:37 Waiting for google-nested:ubuntu-16.04-64 (may181749-433163) to boot at 35.237.37.145...
+2022-05-18 17:49:47 Waiting for google-nested:ubuntu-16.04-64 (may181749-433044) to boot at 34.148.64.110...
+2022-05-18 17:50:03 Allocated google-nested:ubuntu-16.04-64 (may181749-432987).
+2022-05-18 17:50:03 Connecting to google-nested:ubuntu-16.04-64 (may181749-432987)...
+2022-05-18 17:50:04 Connected to google-nested:ubuntu-16.04-64 (may181749-432987) at 104.196.14.127.
+2022-05-18 17:50:04 Sending project content to google-nested:ubuntu-16.04-64 (may181749-432987)...
+2022-05-18 17:50:06 Preparing google-nested:ubuntu-16.04-64 (may181749-432987)...
+2022-05-18 17:50:08 Allocated google-nested:ubuntu-16.04-64 (may181749-433163).
+2022-05-18 17:50:08 Connecting to google-nested:ubuntu-16.04-64 (may181749-433163)...
+2022-05-18 17:50:09 Connected to google-nested:ubuntu-16.04-64 (may181749-433163) at 35.237.37.145.
+2022-05-18 17:50:09 Sending project content to google-nested:ubuntu-16.04-64 (may181749-433163)...
+2022-05-18 17:50:11 Preparing google-nested:ubuntu-16.04-64 (may181749-433163)...
+2022-05-18 17:50:13 Allocated google-nested:ubuntu-16.04-64 (may181749-433044).
+2022-05-18 17:50:13 Connecting to google-nested:ubuntu-16.04-64 (may181749-433044)...
+2022-05-18 17:50:13 Connected to google-nested:ubuntu-16.04-64 (may181749-433044) at 34.148.64.110.
+2022-05-18 17:50:13 Sending project content to google-nested:ubuntu-16.04-64 (may181749-433044)...
+2022-05-18 17:50:15 Preparing google-nested:ubuntu-16.04-64 (may181749-433044)...
+2022-05-18 17:55:06 WARNING: may181749-432987 (google-nested:ubuntu-16.04-64) running late. Current output:
+-----
+(... 365 lines above ...)
++ restart_logind=
++ loginctl enable-linger test
++ loginctl disable-linger test
+Unpacking systemd (229-4ubuntu21.31) over (229-4ubuntu21.28) ...
+Processing triggers for dbus (1.10.6-1ubuntu3.6) ...
+Processing triggers for ureadahead (0.100.0-19.1) ...
+Processing triggers for man-db (2.7.5-1) ...
+Setting up systemd (229-4ubuntu21.31) ...
+addgroup: The group `systemd-journal' already exists as a system group. Exiting.
+[/usr/lib/tmpfiles.d/var.conf:14] Duplicate line for path "/var/log", ignoring.
+Setting up libpam-systemd:amd64 (229-4ubuntu21.31) ...
+-----
+.
+2022-05-18 18:00:06 WARNING: may181749-432987 (google-nested:ubuntu-16.04-64) running late. Current output:
+-----
+(... 1334 lines above ...)
+dpkg-gencontrol: warning: File::FcntlLock not available; using flock which is not NFS-safe
+dpkg-gencontrol: warning: File::FcntlLock not available; using flock which is not NFS-safe
+dpkg-gencontrol: warning: File::FcntlLock not available; using flock which is not NFS-safe
+dpkg-gencontrol: warning: File::FcntlLock not available; using flock which is not NFS-safe
+make[1]: Leaving directory '/home/gopath/src/github.com/snapcore/snapd'
+ dh_md5sums -O--buildsystem=golang -O--fail-missing -O--builddirectory=_build
+ dh_builddeb -u-Zxz -O--buildsystem=golang -O--fail-missing -O--builddirectory=_build
+dpkg-deb: building package 'golang-github-ubuntu-core-snappy-dev' in '../golang-github-ubuntu-core-snappy-dev_1337.2.55.5_all.deb'.
+dpkg-deb: building package 'golang-github-snapcore-snapd-dev' in '../golang-github-snapcore-snapd-dev_1337.2.55.5_all.deb'.
+dpkg-deb: building package 'snapd' in '../snapd_1337.2.55.5_amd64.deb'.
+-----
+.
+2022-05-18 18:00:11 WARNING: may181749-433163 (google-nested:ubuntu-16.04-64) running late. Current output:
+-----
+(... 1334 lines above ...)
+dpkg-gencontrol: warning: File::FcntlLock not available; using flock which is not NFS-safe
+dpkg-gencontrol: warning: File::FcntlLock not available; using flock which is not NFS-safe
+dpkg-gencontrol: warning: File::FcntlLock not available; using flock which is not NFS-safe
+dpkg-gencontrol: warning: File::FcntlLock not available; using flock which is not NFS-safe
+make[1]: Leaving directory '/home/gopath/src/github.com/snapcore/snapd'
+ dh_md5sums -O--buildsystem=golang -O--fail-missing -O--builddirectory=_build
+ dh_builddeb -u-Zxz -O--buildsystem=golang -O--fail-missing -O--builddirectory=_build
+dpkg-deb: building package 'golang-github-ubuntu-core-snappy-dev' in '../golang-github-ubuntu-core-snappy-dev_1337.2.55.5_all.deb'.
+dpkg-deb: building package 'golang-github-snapcore-snapd-dev' in '../golang-github-snapcore-snapd-dev_1337.2.55.5_all.deb'.
+dpkg-deb: building package 'snapd' in '../snapd_1337.2.55.5_amd64.deb'.
+-----
+.
+2022-05-18 18:00:16 WARNING: may181749-433044 (google-nested:ubuntu-16.04-64) running late. Output unchanged.
+2022-05-18 18:01:03 Preparing google-nested:ubuntu-16.04-64:tests/nested/core/ (may181749-432987)...
+2022-05-18 18:01:17 Preparing google-nested:ubuntu-16.04-64:tests/nested/manual/ (may181749-433163)...
+2022-05-18 18:03:19 Preparing google-nested:ubuntu-16.04-64:tests/nested/manual/cloud-init-never-used-not-vuln:firstboot (may181749-433163)...
+2022-05-18 18:05:16 WARNING: may181749-433044 (google-nested:ubuntu-16.04-64) running late. Output unchanged.
+2022-05-18 18:06:03 WARNING: may181749-432987 (google-nested:ubuntu-16.04-64:tests/nested/core/) running late. Current output:
+-----
+(... 97 lines above ...)
+Parallel unsquashfs: Using 2 processors
+11398 inodes (13347 blocks) to write
+
+[===========================================================\] 13347/13347 100%
+
+created 8997 files
+created 1461 directories
+created 2312 symlinks
+created 79 devices
+created 0 fifos
+-----
+.
+2022-05-18 18:06:52 Executing google-nested:ubuntu-16.04-64:tests/nested/manual/cloud-init-never-used-not-vuln:firstboot (may181749-433163) (1/13)...
+2022-05-18 18:06:58 Preparing google-nested:ubuntu-16.04-64:tests/nested/core/image-build (may181749-432987)...
+2022-05-18 18:07:57 Restoring google-nested:ubuntu-16.04-64:tests/nested/manual/cloud-init-never-used-not-vuln:firstboot (may181749-433163)...
+2022-05-18 18:08:00 Preparing google-nested:ubuntu-16.04-64:tests/nested/manual/cloud-init-nocloud-not-vuln:firstboot (may181749-433163)...
+2022-05-18 18:08:40 Executing google-nested:ubuntu-16.04-64:tests/nested/core/image-build (may181749-432987) (2/13)...
+2022-05-18 18:09:34 Error preparing google-nested:ubuntu-16.04-64:tests/nested/manual/cloud-init-nocloud-not-vuln:firstboot (may181749-433163) :
+-----
++ /home/gopath/src/github.com/snapcore/snapd/tests/lib/prepare-restore.sh --prepare-project-each
++ set -e
++ . /home/gopath/src/github.com/snapcore/snapd/tests/lib/quiet.sh
++ . /home/gopath/src/github.com/snapcore/snapd/tests/lib/pkgdb.sh
+++ . /home/gopath/src/github.com/snapcore/snapd/tests/lib/quiet.sh
++ . /home/gopath/src/github.com/snapcore/snapd/tests/lib/random.sh
++ . /home/gopath/src/github.com/snapcore/snapd/tests/lib/state.sh
+++ SNAPD_STATE_PATH=/var/tmp/snapd-tools/snapd-state
+++ SNAPD_STATE_FILE=/var/tmp/snapd-tools/snapd-state/snapd-state.tar
+++ RUNTIME_STATE_PATH=/var/tmp/snapd-tools/runtime-state
+++ SNAPD_ACTIVE_UNITS=/var/tmp/snapd-tools/runtime-state/snapd-active-units
+++ . /home/gopath/src/github.com/snapcore/snapd/tests/lib/systemd.sh
++ case "$1" in
++ prepare_project_each
++ dmesg -c
++ fixup_dev_random
++ mv /dev/random /dev/random.orig
++ mknod /dev/random c 1 9
++ kill_gpg_agent
++ pkill -9 -e gpg-agent
+2022-05-18 16:45:15 Failed tasks: 3
+ - google-nested:ubuntu-16.04-64:tests/nested/manual/cloud-init-never-used-not-vuln:refresh
+ - google-nested:ubuntu-16.04-64:tests/nested/manual/cloud-init-nocloud-not-vuln:firstboot
+ - google-nested:ubuntu-16.04-64:tests/nested/manual/devmode-snaps-can-run-other-snaps
+
+May 18 18:50:05 may181749-433044 kernel: veth33879c75: renamed from physLGGOmx
+May 18 18:50:05 may181749-433044 libvirtd[21899]: Failed to open file '/sys/class/net/eth0/operstate': No such file or directory
+May 18 18:50:05 may181749-433044 libvirtd[21899]: unable to read: /sys/class/net/eth0/operstate: No such file or directory
+May 18 18:50:05 may181749-433044 kernel: device veth690dd0d2 left promiscuous mode
+May 18 18:50:05 may181749-433044 kernel: lxdbr0: port 1(veth690dd0d2) entered disabled state
+May 18 18:50:05 may181749-433044 audit[20780]: AVC apparmor="STATUS" operation="profile_remove" profile="unconfined" name="lxd-snapcraft-snapd_" pid=20780 comm="apparmor_parser"
+May 18 18:50:05 may181749-433044 kernel: audit: type=1400 audit(1652899805.951:102): apparmor="STATUS" operation="profile_remove" profile="unconfined" name="lxd-snapcraft-snapd_" pid=20780 comm="apparmor_parser"
+May 18 18:50:07 may181749-433044 ntpd[1698]: Deleting interface #8 lxdbr0, 10.253.239.1#123, interface stats: received=0, sent=0, dropped=0, active_time=851 secs
+May 18 18:50:07 may181749-433044 ntpd[1698]: Deleting interface #9 lxdbr0, fd42:9473:7f21:63ab::1#123, interface stats: received=0, sent=0, dropped=0, active_time=851 secs
+May 18 18:50:07 may181749-433044 ntpd[1698]: Deleting interface #10 lxdbr0, fe80::216:3eff:feec:f39f%5#123, interface stats: received=0, sent=0, dropped=0, active_time=851 secs
++ echo '# tasks executed on system'
+# tasks executed on system
++ echo ''
++ cat /var/tmp/snapd-tools/runtime-state/runs -
+google-nested:ubuntu-16.04-64:tests/nested/classic/hotplug google-nested:ubuntu-16.04-64:tests/nested/classic/snapshots-with-core-refresh-revert google-nested:ubuntu-16.04-64:tests/nested/manual/minimal-smoke:secboot_disabled google-nested:ubuntu-16.04-64:tests/nested/manual/minimal-smoke:secboot_enabled google-nested:ubuntu-16.04-64:tests/nested/manual/devmode-snaps-can-run-other-snaps
+-----
+.
+2022-05-18 16:45:15 Error restoring google:ubuntu-core-22-64:tests/main/ (may181431-047051) : EOF
+2022-05-18 16:45:15 Error debugging google:ubuntu-core-22-64:tests/main/ (may181431-047051) : EOF
+2022-05-18 16:45:15 Restoring google:ubuntu-core-22-64 (may181431-047051)...
+2022-05-18 16:45:15 Error restoring google:ubuntu-core-22-64 (may181431-047051) : EOF
+2022-05-18 16:45:15 Discarding google:ubuntu-core-22-64 (may181431-047051)...
+2022-05-18 16:45:15 Successful tasks: 7376
+2022-05-18 16:45:15 Aborted tasks: 3061
+2022-05-18 16:45:15 Failed tasks: 2
+ - google:ubuntu-20.04-64:tests/main/degraded
+ - google:ubuntu-core-22-64:tests/main/dbus-activation-session
diff --git a/tests/lib/external/snapd-testing-tools/tests/not/task.yaml b/tests/lib/external/snapd-testing-tools/tests/not/task.yaml
new file mode 100644
index 0000000..3377d3c
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/not/task.yaml
@@ -0,0 +1,9 @@
+summary: test for the not tool
+
+details: |
+ Check the not tool works the same as the logical not (!).
+
+backends: [google]
+
+execute: |
+ not false && echo test | MATCH test
diff --git a/tests/lib/external/snapd-testing-tools/tests/os.paths/task.yaml b/tests/lib/external/snapd-testing-tools/tests/os.paths/task.yaml
new file mode 100644
index 0000000..e9f3444
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/os.paths/task.yaml
@@ -0,0 +1,45 @@
+summary: smoke test for the os.paths tool
+
+details: |
+ Check the os.paths tool properly retrieves the snap-mount-dir,
+ media-dir and libexec-dir in the current system.
+
+backends: [google]
+
+execute: |
+ os.paths --help | MATCH 'usage: os.paths snap-mount-dir, media-dir, libexec-dir'
+ os.paths -h | MATCH 'usage: os.paths snap-mount-dir, media-dir, libexec-dir'
+
+ case "$SPREAD_SYSTEM" in
+ ubuntu-*|debian-*)
+ test "$(os.paths snap-mount-dir)" = "/snap"
+ test "$(os.paths media-dir)" = "/media"
+ test "$(os.paths libexec-dir)" = "/usr/lib"
+ ;;
+ fedora-*|amazon-*|centos-*)
+ test "$(os.paths snap-mount-dir)" = "/var/lib/snapd/snap"
+ test "$(os.paths media-dir)" = "/run/media"
+ test "$(os.paths libexec-dir)" = "/usr/libexec"
+ ;;
+ arch-*)
+ test "$(os.paths snap-mount-dir)" = "/var/lib/snapd/snap"
+ test "$(os.paths media-dir)" = "/run/media"
+ test "$(os.paths libexec-dir)" = "/usr/lib"
+ ;;
+ opensuse-tumbleweed-*)
+ test "$(os.paths snap-mount-dir)" = "/snap"
+ test "$(os.paths media-dir)" = "/run/media"
+ test "$(os.paths libexec-dir)" = "/usr/libexec"
+ ;;
+ opensuse-*)
+ test "$(os.paths snap-mount-dir)" = "/snap"
+ test "$(os.paths media-dir)" = "/run/media"
+ test "$(os.paths libexec-dir)" = "/usr/lib"
+ ;;
+ *)
+ echo "System $SPREAD_SYSTEM not supported"
+ exit 1
+ ;;
+ esac
+
+ os.paths my-dir 2>&1 | MATCH "os.paths: unknown path my-dir"
diff --git a/tests/lib/external/snapd-testing-tools/tests/os.query/task.yaml b/tests/lib/external/snapd-testing-tools/tests/os.query/task.yaml
new file mode 100644
index 0000000..9a6cdcd
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/os.query/task.yaml
@@ -0,0 +1,174 @@
+summary: smoke test for the os.query tool
+
+details: |
+ Check the os.query tool properly detects the current system. Also
+ verify the version comparison works.
+
+backends: [google, openstack]
+
+execute: |
+ os.query --help | MATCH 'usage: os.query is-core, is-classic'
+ os.query -h | MATCH 'usage: os.query is-core, is-classic'
+
+ case "$SPREAD_SYSTEM" in
+ ubuntu-14.04-64)
+ os.query is-trusty
+ os.query is-classic
+ os.query is-ubuntu 14.04
+ ! os.query is-ubuntu 18.04
+ ! os.query is-core
+ ! os.query is-s390x
+ ;;
+ ubuntu-16.04-64)
+ os.query is-xenial
+ os.query is-classic
+ os.query is-ubuntu 16.04
+ ! os.query is-ubuntu 14.04
+ ! os.query is-core
+ ;;
+ ubuntu-18.04-32)
+ os.query is-bionic
+ os.query is-classic
+ os.query is-ubuntu 18.04
+ ! os.query is-ubuntu 14.04
+ ! os.query is-core
+ os.query is-pc-i386
+ ;;
+ ubuntu-18.04-64)
+ os.query is-bionic
+ os.query is-classic
+ os.query is-ubuntu 18.04
+ ! os.query is-ubuntu 14.04
+ ! os.query is-core
+ os.query is-pc-amd64
+
+ # check ubuntu comparisons
+ os.query is-ubuntu-ge 14.04
+ os.query is-ubuntu-ge 18.04
+ ! os.query is-ubuntu-ge 20.04
+ os.query is-ubuntu-gt 16.04
+ ! os.query is-ubuntu-gt 18.04
+ os.query is-ubuntu-le 18.04
+ os.query is-ubuntu-le 20.04
+ ! os.query is-ubuntu-le 16.04
+ os.query is-ubuntu-lt 20.04
+ ! os.query is-ubuntu-lt 18.04
+ os.query is-ubuntu-lt 2>&1 | MATCH "os.query: version id is expected"
+ os.query is-ubuntu-lt 20.04-64 2>&1 | MATCH 'os.query: invalid version format "20.04-64" provided'
+ ;;
+ ubuntu-20.04-64)
+ os.query is-focal
+ os.query is-ubuntu
+ os.query is-ubuntu 20.04
+ ! os.query is-ubuntu 21.04
+ ! os.query is-debian
+ os.query is-classic
+ ! os.query is-core
+ ! os.query is-core-ge 20
+ ! os.query is-core-le 20
+ os.query is-pc-amd64
+ ! os.query is-arm
+ ;;
+ ubuntu-22.04-64)
+ os.query is-jammy
+ os.query is-classic
+ os.query is-ubuntu 22.04
+ ! os.query is-ubuntu 20.04
+ ! os.query is-core
+ ! os.query is-core-ge 20
+ ! os.query is-core-le 20
+ ! os.query is-core-le 24
+ ! os.query is-core-ge 24
+ ;;
+ ubuntu-23.10-64)
+ os.query is-classic
+ os.query is-ubuntu 23.10
+ ! os.query is-ubuntu 21.04
+ ! os.query is-core
+ ;;
+ ubuntu-24.04-64)
+ os.query is-classic
+ os.query is-ubuntu 24.04
+ ! os.query is-ubuntu 21.04
+ ! os.query is-core
+ ;;
+ debian-11-64)
+ os.query is-debian
+ os.query is-debian 11
+ os.query is-classic
+ ! os.query is-core
+ ;;
+ debian-12-64)
+ os.query is-debian
+ os.query is-debian 12
+ os.query is-classic
+ ! os.query is-core
+ ;;
+ debian-sid-64)
+ os.query is-debian
+ os.query is-debian sid
+ os.query is-classic
+ ! os.query is-core
+ ;;
+ fedora-38-64)
+ os.query is-fedora
+ os.query is-fedora 38
+ ! os.query is-fedora rawhide
+ os.query is-classic
+ ! os.query is-core
+ ;;
+ fedora-39-64)
+ os.query is-fedora
+ os.query is-fedora 39
+ ! os.query is-fedora rawhide
+ os.query is-classic
+ ! os.query is-core
+ ;;
+ fedora-40-64)
+ os.query is-fedora
+ os.query is-fedora 40
+ ! os.query is-fedora rawhide
+ os.query is-classic
+ ! os.query is-core
+ ;;
+ arch-linux-64)
+ os.query is-arch-linux
+ os.query is-classic
+ ! os.query is-core
+ ;;
+ amazon-linux-2-64)
+ os.query is-amazon-linux
+ os.query is-amazon-linux 2
+ ! os.query is-amazon-linux 2023
+ os.query is-classic
+ ! os.query is-core
+ ;;
+ amazon-linux-2023-64)
+ os.query is-amazon-linux
+ os.query is-amazon-linux 2023
+ ! os.query is-amazon-linux 2
+ os.query is-classic
+ ! os.query is-core
+ ;;
+ centos-9-64)
+ os.query is-centos 9
+ os.query is-centos
+ ! os.query is-core
+ ;;
+ opensuse-15.5-64)
+ os.query is-opensuse
+ os.query is-opensuse 15.5
+ os.query is-classic
+ ! os.query is-core
+ ;;
+ opensuse-tumbleweed-64)
+ os.query is-opensuse
+ os.query is-opensuse tumbleweed
+ os.query is-classic
+ ! os.query is-core
+ ;;
+ *)
+ echo "System $SPREAD_SYSTEM not supported"
+ exit 1
+ ;;
+ esac
diff --git a/tests/lib/external/snapd-testing-tools/tests/quiet/task.yaml b/tests/lib/external/snapd-testing-tools/tests/quiet/task.yaml
new file mode 100644
index 0000000..23c7f51
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/quiet/task.yaml
@@ -0,0 +1,12 @@
+summary: test for the quiet tool
+
+details: |
+ Check the quiet tool hides the command stdout and but shows the stderr
+
+backends: [google]
+
+execute: |
+ quiet echo test | NOMATCH test
+ quiet "cat nofile" 2>&1 | MATCH nofile
+ quiet "cat nofile" 2>&1 | MATCH "quiet: exit status 127"
+ quiet "cat nofile" 2>&1 | MATCH "quiet: end of output"
diff --git a/tests/lib/external/snapd-testing-tools/tests/remote.exec/task.yaml b/tests/lib/external/snapd-testing-tools/tests/remote.exec/task.yaml
new file mode 100644
index 0000000..249e871
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/remote.exec/task.yaml
@@ -0,0 +1,44 @@
+summary: smoke test for the remote.exec tool
+
+details: |
+ Check the remote.exec tool is able to run commands in the
+ remote instance through ssh.
+
+backends: [google]
+
+# Amazon linux is skipped because no sshpass available
+systems: [-amazon-linux-*]
+
+prepare: |
+ tests.pkgs install sshpass
+ remote.setup config --host localhost --port 22 --user tools-user-1 --pass tools-user-1
+
+restore: |
+ tests.pkgs remove sshpass
+ rm -rf remote.setup.cfg my-key my-key.pub /home/tools-user-1/* /home/tools-user-2/*
+
+execute: |
+ remote.exec --help | MATCH 'usage: remote.exec \[--user \] \[--pass \] '
+ remote.exec -h | MATCH 'usage: remote.exec \[--user \] \[--pass \] '
+
+ remote.exec "touch testfile"
+ test -f /home/tools-user-1/testfile
+
+ remote.exec --user tools-user-2 --pass tools-user-2 "touch testfile"
+ test -f /home/tools-user-2/testfile
+
+ remote.exec --user tools-user-2 --password tools-user-2 "touch testfile" 2>&1 | MATCH "remote.exec: unknown option --password"
+
+ # Check the remote execution using a certificate
+ ssh-keygen -t rsa -N "" -f my-key
+ chmod 0600 my-key my-key.pub
+
+ mkdir -p /home/tools-user-1/.ssh
+ cat my-key.pub > /home/tools-user-1/.ssh/authorized_keys
+
+ remote.setup config --host localhost --port 22 --user tools-user-1 --cert my-key
+ remote.exec "ls /home/tools-user-1/testfile" | MATCH testfile
+
+ # Check error when there is no configuration
+ rm -f remote.setup.cfg
+ remote.exec --user tools-user-2 --pass tools-user-2 "touch testfile" | MATCH "remote.exec: config file \"$(pwd)/remote.setup.cfg\" not found, please run remote.setup command first"
diff --git a/tests/lib/external/snapd-testing-tools/tests/remote.pull/task.yaml b/tests/lib/external/snapd-testing-tools/tests/remote.pull/task.yaml
new file mode 100644
index 0000000..658add6
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/remote.pull/task.yaml
@@ -0,0 +1,37 @@
+summary: smoke test for the remote.pull tool
+
+details: |
+ Check the remote.pull tool is able to retrieve files from the
+ remote instance through scp
+
+backends: [google]
+
+# Amazon linux is skipped because no sshpass available
+systems: [-amazon-linux-*]
+
+prepare: |
+ tests.pkgs install sshpass
+ remote.setup config --host localhost --port 22 --user tools-user-1 --pass tools-user-1
+
+restore: |
+ tests.pkgs remove sshpass
+ rm -rf testfile testfile2 /tmp/testfile2 remote.setup.cfg /home/tools-user-1/*
+
+execute: |
+ remote.pull --help | MATCH 'usage: remote.pull \[LOCAL_PATH\]'
+ remote.pull -h | MATCH 'usage: remote.pull \[LOCAL_PATH\]'
+
+ # check basic pull
+ touch /home/tools-user-1/testfile
+ remote.pull testfile .
+ test -f testfile
+
+ # check pull without local dir
+ touch /tmp/testfile2
+ remote.pull /tmp/testfile2
+ test -f ./testfile2
+
+ # Check no parameters
+ remote.pull | MATCH 'usage: remote.pull \[LOCAL_PATH\]'
+ rm -f remote.setup.cfg
+ remote.pull /home/tools-user-1/testfile . 2>&1 | MATCH "remote.pull: config file \"$(pwd)/remote.setup.cfg\" not found, please run remote.setup command first"
diff --git a/tests/lib/external/snapd-testing-tools/tests/remote.push/task.yaml b/tests/lib/external/snapd-testing-tools/tests/remote.push/task.yaml
new file mode 100644
index 0000000..047e5cc
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/remote.push/task.yaml
@@ -0,0 +1,37 @@
+summary: smoke test for the remote.push tool
+
+details: |
+ Check the remote.push tool is able to send files from
+ localhost to the remote instance through scp
+
+backends: [google]
+
+# Amazon linux is skipped because no sshpass available
+systems: [-amazon-linux-*]
+
+prepare: |
+ tests.pkgs install sshpass
+ remote.setup config --host localhost --port 22 --user tools-user-1 --pass tools-user-1
+
+restore: |
+ tests.pkgs remove sshpass
+ rm -rf testfile remote.setup.cfg /home/tools-user-1/*
+
+execute: |
+ remote.push --help | MATCH 'usage: remote.push \[REMOTE_PATH\]'
+ remote.push -h | MATCH 'usage: remote.push \[REMOTE_PATH\]'
+
+ # check basic push
+ touch testfile
+ remote.push testfile "/home/tools-user-1"
+ test -f /home/tools-user-1/testfile
+
+ # check push without target dir
+ touch testfile2
+ remote.push testfile2
+ test -f /home/tools-user-1/testfile2
+
+ # Check no parameters
+ remote.push | MATCH 'usage: remote.push \[REMOTE_PATH\]'
+ rm -f remote.setup.cfg
+ remote.push testfile "/home/tools-user-1" 2>&1 | MATCH "remote.push: config file \"$(pwd)/remote.setup.cfg\" not found, please run remote.setup command first"
diff --git a/tests/lib/external/snapd-testing-tools/tests/remote.refresh/task.yaml b/tests/lib/external/snapd-testing-tools/tests/remote.refresh/task.yaml
new file mode 100644
index 0000000..1b6a7cb
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/remote.refresh/task.yaml
@@ -0,0 +1,58 @@
+summary: smoke test for the remote.refresh tool
+
+details: |
+ Check the remote.refresh tool properly handles refreshes in
+ the remote instance including the refresh for several snaps
+ which trigger reboots
+
+backends: [google-nested]
+
+systems: [ubuntu-20.04-64]
+
+environment:
+ user: test
+ pass: ubuntu
+ host: localhost
+ port: 8022
+ unit: nested-vm
+ TARGET: xenial
+
+prepare: |
+ if [ "$TARGET" = xenial ]; then
+ wget -O pc.img.xz https://storage.googleapis.com/snapd-spread-tests/images/booted/pc-amd64-16-stable-core_beta_2.56.2.img.xz
+ fi
+ unxz pc.img.xz
+
+ service_line="kvm -nographic -snapshot -smp 2 -m 1500 -net nic,model=virtio -net user,hostfwd=tcp::$port-:22 -serial mon:stdio $PWD/pc.img"
+ tests.systemd create-and-start-unit "$unit" "$service_line"
+
+ remote.setup config --host "$host" --port "$port" --user "$user" --pass "$pass"
+
+restore: |
+ tests.systemd stop-unit --remove "$unit"
+ rm -f pc.img "$(remote.setup get-config-path)"
+
+execute: |
+ remote.refresh | MATCH 'usage: remote.refresh snap'
+ remote.refresh -h | MATCH 'usage: remote.refresh snap'
+ remote.refresh --help | MATCH 'usage: remote.refresh snap'
+
+ # check the vm already started
+ remote.wait-for ssh
+ remote.wait-for device-initialized
+ remote.wait-for snap-command
+
+ # Check waiting when reboot
+ remote.refresh full
+
+ if [ "$TARGET" = xenial ]; then
+ remote.refresh snap kernel --channel latest/beta
+ remote.wait-for refresh
+ remote.refresh snap core --channel stable
+ remote.wait-for refresh
+ elif [ "$TARGET" = bionic ]; then
+ remote.refresh snap kernel --channel 18/beta
+ remote.wait-for refresh
+ remote.refresh snap snapd --channel stable
+ remote.wait-for refresh
+ fi
diff --git a/tests/lib/external/snapd-testing-tools/tests/remote.retry/task.yaml b/tests/lib/external/snapd-testing-tools/tests/remote.retry/task.yaml
new file mode 100644
index 0000000..d2ed984
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/remote.retry/task.yaml
@@ -0,0 +1,48 @@
+summary: smoke test for the remote.retry tool
+
+details: |
+ This test checks the remote.retry tool properly handles retrying
+ a command in the remote instance. Also Verifies that on failure
+ the exit code from the final attempt is returned.
+
+backends: [google]
+
+# Amazon linux is skipped because no sshpass available
+systems: [-amazon-linux-*]
+
+prepare: |
+ tests.pkgs install sshpass
+ remote.setup config --host localhost --port 22 --user tools-user-1 --pass tools-user-1
+
+restore: |
+ tests.pkgs remove sshpass
+ rm -rf remote.setup.cfg /tmp/my-remote-retry-test
+
+execute: |
+ remote.retry --help | MATCH 'usage: remote.retry'
+ remote.retry -h | MATCH 'usage: remote.retry'
+
+ # check the positive scenario without parameters
+ remote.retry "true"
+
+ # check negative scenario
+ not remote.retry --wait 1 -attempts 2 "false"
+
+ # check the command is executed correctly
+ rm -f /tmp/my-remote-retry-test
+ remote.retry "touch /tmp/my-remote-retry-test"
+ test -f /tmp/my-remote-retry-test
+
+ # check the --attempts and --wait parameter
+ not remote.retry --wait 0.5 --attempts 5 "echo 1 >> /tmp/my-remote-retry-test && exit 1"
+ test "$(grep -c 1 /tmp/my-remote-retry-test)" = 5
+ rm -f /tmp/my-remote-retry-test
+
+ # check the -n parameter
+ not remote.retry --wait 0.5 -n 8 "echo 1 >> /tmp/my-remote-retry-test && exit 1"
+ test "$(grep -c 1 /tmp/my-remote-retry-test)" = 8
+ rm -f /tmp/my-remote-retry-test
+
+ # check errors
+ remote.retry --wait 0.5 -n 2 "false" 2>&1 | MATCH "remote.retry: timed out retrying command"
+ remote.retry 2>&1 | MATCH "remote.retry: command to retry not specified"
\ No newline at end of file
diff --git a/tests/lib/external/snapd-testing-tools/tests/remote.setup/task.yaml b/tests/lib/external/snapd-testing-tools/tests/remote.setup/task.yaml
new file mode 100644
index 0000000..9ee820b
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/remote.setup/task.yaml
@@ -0,0 +1,59 @@
+summary: smoke test for the remote.setup tool
+
+details: |
+ Check the remote.setup tool properly sets up up the
+ information required to allow connecting to the remote
+ instance through ssh.
+
+backends: [google]
+
+restore: |
+ rm -f remote.setup.cfg my-key my-key-2
+
+execute: |
+ remote.setup --help | MATCH 'usage: remote.setup config --host --port --user \[--pass \] \[--cert \]'
+ remote.setup -h | MATCH 'usage: remote.setup config --host --port --user \[--pass \] \[--cert \]'
+
+ # Check basic configuration
+ remote.setup config --host localhost --port 22 --user tools-user-1 --pass tools-pass-1
+ # shellcheck disable=SC1091
+ . remote.setup.cfg
+ test "$TESTS_REMOTE_HOST" = localhost
+ test "$TESTS_REMOTE_PORT" = 22
+ test "$TESTS_REMOTE_USER" = tools-user-1
+ test "$TESTS_REMOTE_PASS" = tools-pass-1
+ test -z "$TESTS_REMOTE_CERT"
+
+ # Check get-config-path subcommand
+ remote.setup get-config-path | MATCH "$(pwd)/remote.setup.cfg"
+
+ # check using another config file
+ REMOTE_CFG_FILE="$(pwd)/remote.newsetup.cfg"
+ export REMOTE_CFG_FILE
+ remote.setup config --host localhost --port 22 --user tools-user-1 --pass tools-pass-1
+ remote.setup get-config-path | MATCH "$(pwd)/remote.newsetup.cfg"
+
+ # check using the default config file again
+ unset REMOTE_CFG_FILE
+ remote.setup config --host localhost --port 22 --user tools-user-1 --pass tools-pass-1
+ remote.setup get-config-path | MATCH "$(pwd)/remote.setup.cfg"
+
+ # Check the certificate
+ touch my-key
+ remote.setup config --host 127.0.0.1 --port 22 --user tools-user-1 --cert my-key
+ # shellcheck disable=SC1091
+ . remote.setup.cfg
+ test "$TESTS_REMOTE_HOST" = 127.0.0.1
+ test "$TESTS_REMOTE_CERT" = my-key
+
+ # Check basic errors
+ remote.setup config --port 22 --user tools-user-1 --pass tools-pass-1 2>&1 | MATCH "remote.setup: host, port and user values are require"
+ remote.setup config --host 127.0.0.1 --user tools-user-1 --pass tools-pass-1 2>&1 | MATCH "remote.setup: host, port and user values are require"
+ remote.setup config --host 127.0.0.1 --port 22 --pass tools-pass-1 2>&1 | MATCH "remote.setup: host, port and user values are require"
+
+ # Check other errors
+ remote.setup config --host 127.0.0.1 --port 22 --user tools-user-1 --cert my-key-2 2>&1 | MATCH "remote.setup: certificate is set but file does not exist"
+ remote.setup config --host localhost --port 22 --user tools-user-1 --password tools-user-1 2>&1 | MATCH "tests.remote: unknown option --password"
+ if [ -z "$(command -v sshpass)" ]; then
+ remote.setup config --host localhost --port 22 --user tools-user-1 --pass tools-user-1 | MATCH "remote.setup: sshpass tool is required when password is configured"
+ fi
diff --git a/tests/lib/external/snapd-testing-tools/tests/remote.wait-for/task.yaml b/tests/lib/external/snapd-testing-tools/tests/remote.wait-for/task.yaml
new file mode 100644
index 0000000..db3a050
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/remote.wait-for/task.yaml
@@ -0,0 +1,85 @@
+summary: smoke test for the remote.wait-for tool
+
+details: |
+ Check the remote.wait-for tool waits properly for several
+ events in the remote instance. Supported events are:
+ device-initialized, reboot, refresh, ssh, no-ssh, etc
+
+backends: [google-nested]
+
+systems: [ubuntu-20.04-64]
+
+environment:
+ user: test
+ pass: ubuntu
+ host: localhost
+ port: 8022
+ unit: nested-vm
+ TARGET: xenial
+
+prepare: |
+ if [ "$TARGET" = xenial ]; then
+ wget -O pc.img.xz https://storage.googleapis.com/snapd-spread-tests/images/booted/pc-amd64-16-stable-core_beta_2.56.2.img.xz
+ fi
+ unxz pc.img.xz
+
+ service_line="kvm -nographic -snapshot -smp 2 -m 1500 -net nic,model=virtio -net user,hostfwd=tcp::$port-:22 -serial mon:stdio $PWD/pc.img"
+ tests.systemd create-and-start-unit "$unit" "$service_line"
+
+ remote.setup config --host "$host" --port "$port" --user "$user" --pass "$pass"
+
+restore: |
+ tests.systemd stop-unit --remove "$unit"
+ rm -f pc.img "$(remote.setup get-config-path)"
+
+execute: |
+ remote.wait-for | MATCH 'usage: remote.wait-for ssh'
+ remote.wait-for -h | MATCH 'usage: remote.wait-for ssh'
+ remote.wait-for --help | MATCH 'usage: remote.wait-for ssh'
+
+ # check the vm already started
+ remote.wait-for ssh
+
+ # Disable the refreshes for the vm and wait-for
+ remote.refresh disable-refreshes
+ remote.wait-for refresh
+
+ # Check if the device has been initialized
+ remote.wait-for device-initialized
+
+ # Check if the device has snap command
+ remote.wait-for snap-command
+
+ # Check waiting when reboot
+ remote.exec "sudo reboot" || true
+ remote.wait-for no-ssh --wait 1 -n 20
+ remote.wait-for ssh --wait 1 -n 120
+
+ # Check waiting for reboot
+ initial_boot_id="$(remote.exec "cat /proc/sys/kernel/random/boot_id")"
+ remote.exec "sudo reboot" || true
+ remote.wait-for reboot --wait 5 -n 20 "$initial_boot_id"
+
+ # Check waiting for reboot with not enough time
+ # shellcheck disable=SC2016
+ remote.retry --wait 1 -n 10 'test -n "$(cat /proc/sys/kernel/random/boot_id)"'
+ second_boot_id="$(remote.exec "cat /proc/sys/kernel/random/boot_id")"
+
+ remote.exec "sudo reboot" || true
+ # shellcheck disable=SC2251
+ ! remote.wait-for reboot --wait 0.5 -n 5 "$second_boot_id"
+
+ # Check again if the device has been initialized and the snap command
+ remote.wait-for device-initialized --wait 1 -n 60
+ remote.wait-for snap-command
+
+ # shellcheck disable=SC2016
+ remote.retry --wait 1 -n 10 'test -n "$(cat /proc/sys/kernel/random/boot_id)"'
+ third_boot_id="$(remote.exec "cat /proc/sys/kernel/random/boot_id")"
+
+ # Check boot ids are different
+ test "$initial_boot_id" != "$second_boot_id"
+ test "$second_boot_id" != "$third_boot_id"
+
+ # Check waiting for refresh in case no refresh in progress
+ remote.wait-for refresh
diff --git a/tests/lib/external/snapd-testing-tools/tests/repack-kernel/task.yaml b/tests/lib/external/snapd-testing-tools/tests/repack-kernel/task.yaml
new file mode 100644
index 0000000..86e2102
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/repack-kernel/task.yaml
@@ -0,0 +1,52 @@
+summary: tests for the repack-kernel tool
+
+details: |
+ Check the repack-kernel tools properly handles extraction of the
+ kernel snap to a workspace directory, and then repacks it back
+ to a snap
+
+backends: [google]
+
+# ubuntu-core-initramfs seems to be only available on ubuntu-20.04
+systems: [ubuntu-20.04-64]
+
+execute: |
+ repack-kernel | MATCH 'usage: repack-kernel '
+ repack-kernel -h | MATCH 'usage: repack-kernel '
+ repack-kernel --help | MATCH 'usage: repack-kernel '
+
+ # verify error when calling without an invalid commnnd
+ test "$(repack-kernel invalid-command 2>&1 | grep -c "no such command: invalid-command")" -eq 1
+
+ # verify that an error is thrown when calling commands without arguments
+ test "$(repack-kernel extract 2>&1 | grep -c "invalid arguments for extract")" -eq 1
+ test "$(repack-kernel extract "upstream-pc-kernel.snap" 2>&1 | grep -c "invalid arguments for extract")" -eq 1
+ test "$(repack-kernel prepare 2>&1 | grep -c "missing target for prepare")" -eq 1
+ test "$(repack-kernel cull-modules 2>&1 | grep -c "missing target for cull-modules")" -eq 1
+ test "$(repack-kernel cull-firmware 2>&1 | grep -c "missing target for cull-firmware")" -eq 1
+
+ # download the kernel snap
+ snap download pc-kernel --channel=20/stable --basename=upstream-pc-kernel
+
+ # setup dependencies
+ repack-kernel setup
+
+ # extract the kernel snap, including extracting the initrd from the kernel.efi
+ kerneldir=/tmp/kernel-workdir
+ repack-kernel extract "upstream-pc-kernel.snap" "$kerneldir"
+
+ # cull modules and firmware
+ repack-kernel cull-modules "$kerneldir"
+ repack-kernel cull-firmware "$kerneldir"
+
+ # repack the initrd into the kernel.efi
+ repack-kernel prepare "$kerneldir"
+
+ # repack the kernel snap itself
+ repack-kernel pack "$kerneldir" --filename=pc-kernel.snap
+ rm -rf "$kerneldir"
+
+ if ! [ -f "pc-kernel.snap" ]; then
+ echo "Repack failed, pc-kernel.snap was not created"
+ exit 1
+ fi
diff --git a/tests/lib/external/snapd-testing-tools/tests/retry/task.yaml b/tests/lib/external/snapd-testing-tools/tests/retry/task.yaml
new file mode 100644
index 0000000..d0b329c
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/retry/task.yaml
@@ -0,0 +1,30 @@
+summary: smoke test for the retry tool
+
+details: |
+ Check the retry tool allows retrying a command. Verify that on
+ failure the exit code from the final attempt is returned
+
+backends: [google]
+
+execute: |
+ # Retry runs the command that was passed as argument and returns the exit
+ # code of that command.
+ retry true
+ not retry -n 1 false
+
+ # If the command doesn't exist it is not re-tried multiple times.
+ ( not retry this-command-does-not-exist ) 2>&1 \
+ | MATCH 'retry: cannot execute command this-command-does-not-exist: \[Errno 2\] No such file or directory'
+
+ # On failure it tells us about it, showing progress.
+ retry -n 2 --wait 0.1 false 2>&1 | MATCH "retry: command false failed with code 1"
+ retry -n 2 --wait 0.1 false 2>&1 | MATCH "retry: next attempt in 0.1 second\(s\) \(attempt 1 of 2\)"
+ retry -n 2 --wait 0.1 false 2>&1 | MATCH "retry: command false keeps failing after 2 attempts"
+ # Though all output is removed with the --quiet switch.
+ test "$(retry -n 2 --wait 0.1 --quiet false 2>&1 | wc -l)" -eq 0
+
+ # Retry using environment variables
+ #shellcheck disable=SC2016
+ retry --env key1=1 --env key2=2 sh -c 'echo $key1$key2' | MATCH "12"
+ #shellcheck disable=SC2016
+ retry --env key1=1 --env key2,2 sh -c 'echo $key1$key2' 2>&1 | MATCH "retry: error: argument --env: environment variables expected format is 'KEY=VAL' got 'key2,2'"
diff --git a/tests/lib/external/snapd-testing-tools/tests/snaps.cleanup/task.yaml b/tests/lib/external/snapd-testing-tools/tests/snaps.cleanup/task.yaml
new file mode 100644
index 0000000..380c455
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/snaps.cleanup/task.yaml
@@ -0,0 +1,74 @@
+summary: smoke test for the snaps.cleanup tool
+
+details: |
+ Check the snaps.cleanup tool handles properly the
+ removal of the installed snaps in the current system.
+ The test also verifies the fundamental snaps are not removed
+
+backends: [google]
+
+systems: [ubuntu-16.04-64, ubuntu-18.04-64, ubuntu-20.04-64, ubuntu-22.04-64]
+
+execute: |
+ snaps.cleanup --help | MATCH 'usage: snaps.cleanup [--skip ]'
+ snaps.cleanup -h | MATCH 'usage: snaps.cleanup [--skip ]'
+
+ # Start with a clean list of snaps
+ snap wait system seed.loaded
+ for _ in $(seq 30); do
+ if snap changes | MATCH ".*Doing.*Auto-refresh.*"; then
+ sleep 1
+ else
+ break
+ fi
+ done
+ snaps.cleanup
+
+ # Check core is not cleaned
+ if ! snap list core; then
+ snap install core
+ fi
+ if ! snap list snapd; then
+ snap install snapd
+ fi
+ test "$(snap list | wc -l)" = "3"
+ snaps.cleanup
+ test "$(snap list | wc -l)" = "3"
+
+ # Check the snaps and the new base are cleaned
+ snap install test-snapd-tools test-snapd-tools-core18
+ test "$(snap list | wc -l)" = "6"
+ snaps.cleanup
+ test "$(snap list | wc -l)" = "3"
+ snap list core &>/dev/null
+ snap list snapd &>/dev/null
+
+ # Check we can skip a base
+ snap install test-snapd-tools test-snapd-tools-core18
+ snaps.cleanup --skip core18
+ test "$(snap list | wc -l)" = "4"
+ snap list core &>/dev/null
+ snap list core18 &>/dev/null
+ snaps.cleanup
+ test "$(snap list | wc -l)" = "3"
+
+ # Check we can skip a snap and its base
+ snap install test-snapd-tools test-snapd-tools-core18 test-snapd-tools-core20
+ test "$(snap list | wc -l)" = "8"
+ snaps.cleanup --skip test-snapd-tools-core18
+ test "$(snap list | wc -l)" = "5"
+ snap list core &>/dev/null
+ snap list core18 &>/dev/null
+ snap list test-snapd-tools-core18 &>/dev/null
+ snaps.cleanup
+ test "$(snap list | wc -l)" = "3"
+
+ # Check we can skip all the snaps
+ snap install test-snapd-tools test-snapd-tools-core18 test-snapd-tools-core20
+ test "$(snap list | wc -l)" = "8"
+ snaps.cleanup --skip test-snapd-tools --skip test-snapd-tools-core18 --skip test-snapd-tools-core20
+ test "$(snap list | wc -l)" = "8"
+ snaps.cleanup
+ test "$(snap list | wc -l)" = "3"
+
+ snaps.cleanup skip core 2>&1 | MATCH "snaps.cleanup: unknown subcommand skip"
diff --git a/tests/lib/external/snapd-testing-tools/tests/snaps.name/task.yaml b/tests/lib/external/snapd-testing-tools/tests/snaps.name/task.yaml
new file mode 100644
index 0000000..45e9dea
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/snaps.name/task.yaml
@@ -0,0 +1,30 @@
+summary: smoke test for the snaps.name tool
+
+details: |
+ Check the snaps.name tool properly retrieves the names of the
+ gadget and kernel snaps installed in the system. Also verifies
+ the snap-suffix command works properly.
+
+backends: [google]
+
+execute: |
+ snaps.name --help | MATCH 'usage: snaps.name gadget, kernel, core'
+ snaps.name -h | MATCH 'usage: snaps.name gadget, kernel, core'
+
+ test -z "$(snaps.name gadget)"
+ test -z "$(snaps.name kernel)"
+ test "$(snaps.name core)" = "core"
+
+ if os.query is-core18; then
+ test "$(snaps.name snap-suffix)" = "-core18"
+ elif os.query is-core20; then
+ test "$(snaps.name snap-suffix)" = "-core20"
+ elif os.query is-core22; then
+ test "$(snaps.name snap-suffix)" = "-core22"
+ elif os.query is-core24; then
+ test "$(snaps.name snap-suffix)" = "-core24"
+ else
+ test -z "$(snaps.name snap-suffix)"
+ fi
+
+ snaps.name my-snap 2>&1 | MATCH "snaps.name: unknown snap my-snap"
diff --git a/tests/lib/external/snapd-testing-tools/tests/spread-manager/checks/task1/task.yaml b/tests/lib/external/snapd-testing-tools/tests/spread-manager/checks/task1/task.yaml
new file mode 100644
index 0000000..a2a5929
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/spread-manager/checks/task1/task.yaml
@@ -0,0 +1,15 @@
+summary: this is the summary
+
+details: test
+
+prepare: |
+ echo "preparing"
+
+restore: |
+ echo "restoring"
+
+debug: |
+ echo "debugging"
+
+execute: |
+ echo "executing"
diff --git a/tests/lib/external/snapd-testing-tools/tests/spread-manager/checks/task2/task.yaml b/tests/lib/external/snapd-testing-tools/tests/spread-manager/checks/task2/task.yaml
new file mode 100644
index 0000000..7ae94af
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/spread-manager/checks/task2/task.yaml
@@ -0,0 +1,17 @@
+summary: this is the summary
+
+details: test
+
+manual: false
+
+prepare: |
+ echo "preparing"
+
+restore: |
+ echo "restoring"
+
+debug: |
+ echo "debugging"
+
+execute: |
+ echo "executing"
diff --git a/tests/lib/external/snapd-testing-tools/tests/spread-manager/checks/task3/task.yaml b/tests/lib/external/snapd-testing-tools/tests/spread-manager/checks/task3/task.yaml
new file mode 100644
index 0000000..02a2c23
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/spread-manager/checks/task3/task.yaml
@@ -0,0 +1,17 @@
+summary: this is the summary
+
+details: test
+
+manual: true
+
+prepare: |
+ echo "preparing"
+
+restore: |
+ echo "restoring"
+
+debug: |
+ echo "debugging"
+
+execute: |
+ echo "executing"
diff --git a/tests/lib/external/snapd-testing-tools/tests/spread-manager/checks/task4/task.yaml b/tests/lib/external/snapd-testing-tools/tests/spread-manager/checks/task4/task.yaml
new file mode 100644
index 0000000..8941e00
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/spread-manager/checks/task4/task.yaml
@@ -0,0 +1,17 @@
+summary: this is the summary
+
+details: test
+
+#manual: true
+
+prepare: |
+ echo "preparing"
+
+restore: |
+ echo "restoring"
+
+debug: |
+ echo "debugging"
+
+execute: |
+ echo "executing"
diff --git a/tests/lib/external/snapd-testing-tools/tests/spread-manager/checks/task5/.empty b/tests/lib/external/snapd-testing-tools/tests/spread-manager/checks/task5/.empty
new file mode 100644
index 0000000..e69de29
diff --git a/tests/lib/external/snapd-testing-tools/tests/spread-manager/spread.yaml b/tests/lib/external/snapd-testing-tools/tests/spread-manager/spread.yaml
new file mode 100644
index 0000000..e939737
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/spread-manager/spread.yaml
@@ -0,0 +1,12 @@
+project: spread-manager
+
+backends:
+ lxd:
+ systems:
+ - ubuntu-20.04:
+
+path: /home/test
+
+suites:
+ checks/:
+ summary: Verification tasks.
diff --git a/tests/lib/external/snapd-testing-tools/tests/spread-manager/task.yaml b/tests/lib/external/snapd-testing-tools/tests/spread-manager/task.yaml
new file mode 100644
index 0000000..817a08f
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/spread-manager/task.yaml
@@ -0,0 +1,96 @@
+summary: smoke test for the spread-manager tool
+
+details: |
+ Check the spread-manager tool allows setting a list of tests as manual.
+ Also checks manual tag can be removed for a list of tests.
+
+backends: [google]
+
+systems: [ ubuntu-20.04-64 ]
+
+prepare: |
+ wget -qO- wget https://storage.googleapis.com/snapd-spread-tests/spread/spread-amd64.tar.gz | tar xvz
+
+restore: |
+ rm -f spread
+
+execute: |
+ spread-manager -h | MATCH "spread-manager set-manual \[--project-path \] "
+ spread-manager --help | MATCH "spread-manager set-manual \[--project-path \] "
+
+ # Check task without manual tag
+ TASK=task1
+ test "$(./spread -list | wc -l)" == 3
+
+ spread-manager set-manual --project-path "$PWD" "checks/$TASK"
+ test -f "checks/$TASK/task.yaml.back"
+ MATCH "^manual: true" < "checks/$TASK/task.yaml"
+
+ test "$(./spread -list | wc -l)" == 2
+
+ spread-manager reset-manual --project-path "$PWD" "checks/$TASK"
+ test -f "checks/$TASK/task.yaml.back" && exit 1
+ NOMATCH "^manual: true" < "checks/$TASK/task.yaml"
+
+ test "$(./spread -list | wc -l)" == 3
+
+ # Check task without manual: false tag
+ TASK=task2
+ test "$(grep -c "manual:" checks/$TASK/task.yaml)" == 1
+ spread-manager set-manual --project-path "$PWD" "checks/$TASK"
+ test "$(grep -c "manual:" checks/$TASK/task.yaml)" == 1
+
+ test "$(./spread -list | wc -l)" == 2
+
+ spread-manager reset-manual --project-path "$PWD" "checks/$TASK"
+ test -f "checks/$TASK/task.yaml.back" && exit 1
+ MATCH "^manual: false" < "checks/$TASK/task.yaml"
+
+ test "$(./spread -list | wc -l)" == 3
+
+ # Check task without manual: true tag
+ TASK=task3
+ test "$(grep -c "manual:" checks/$TASK/task.yaml)" == 1
+ spread-manager unset-manual --project-path "$PWD" "checks/$TASK"
+ test "$(grep -c "manual:" checks/$TASK/task.yaml)" == 1
+
+ test "$(./spread -list | wc -l)" == 4
+ spread-manager reset-manual --project-path "$PWD" "checks/$TASK"
+ test "$(./spread -list | wc -l)" == 3
+
+ # Check task without #manual tag
+ TASK=task4
+ spread-manager set-manual --project-path "$PWD" "checks/$TASK"
+ test "$(./spread -list | wc -l)" == 2
+ spread-manager reset-manual --project-path "$PWD" "checks/$TASK"
+ test "$(./spread -list | wc -l)" == 3
+
+ # Check set and unset a list of tasks
+ spread-manager set-manual --project-path "$PWD" "checks/task1" "checks/task2" "checks/task3"
+ test "$(./spread -list | wc -l)" == 1
+ spread-manager reset-manual --project-path "$PWD" "checks/task1" "checks/task2" "checks/task3"
+ test "$(./spread -list | wc -l)" == 3
+ spread-manager unset-manual --project-path "$PWD" "checks/task1" "checks/task2" "checks/task3"
+ test "$(./spread -list | wc -l)" == 4
+ spread-manager reset-manual --project-path "$PWD" "checks/task1" "checks/task2" "checks/task3"
+ test "$(./spread -list | wc -l)" == 3
+ spread-manager set-manual --project-path "$PWD" "checks/task1,checks/task2,checks/task3"
+ test "$(./spread -list | wc -l)" == 1
+ spread-manager reset-manual --project-path "$PWD" "checks/task1,checks/task2,checks/task3"
+ test "$(./spread -list | wc -l)" == 3
+ spread-manager unset-manual --project-path "$PWD" "checks/task1 checks/task2 checks/task3"
+ test "$(./spread -list | wc -l)" == 4
+ spread-manager reset-manual --project-path "$PWD" "checks/task1 checks/task2 checks/task3"
+ test "$(./spread -list | wc -l)" == 3
+
+ # Check errors
+ spread-manager set-manual --project-path "" 2>&1 | MATCH "spread-manager: project path cannot be empty"
+ spread-manager set-manual --project-path "$PWD/noproject" "checks/task1" 2>&1 | MATCH "spread-manager: project path \"$PWD/noproject\" has to be a directory"
+ spread-manager set-manual --project-path "checks/task1" "checks/task1" 2>&1 | MATCH "spread-manager: project spread file \"checks/task1/spread.yaml\" does not exist"
+
+ spread-manager set-manual --project-path "$PWD" 2>&1 | MATCH "spread-manager: test path cannot be empty"
+ spread-manager set-manual --project-path "$PWD" "checks/task1/task.yaml" 2>&1 | MATCH "spread-manager: test path \"checks/task1/task.yaml\" has to be a directory"
+ spread-manager set-manual --project-path "$PWD" "checks/task5" 2>&1 | MATCH "spread-manager: test task \"checks/task5/task.yaml\" does not exist"
+
+ spread-manager reset-manual --project-path "$PWD" "checks/task1" 2>&1 | MATCH "spread-manager: test task backup does not exist \"$PWD/checks/task1/task.yaml.back\""
+ spread-manager set-manuals --project-path "$PWD" "checks/task1" 2>&1 | MATCH "spread-manager: no such command: set-manuals"
\ No newline at end of file
diff --git a/tests/lib/external/snapd-testing-tools/tests/spread-shellcheck/task.yaml b/tests/lib/external/snapd-testing-tools/tests/spread-shellcheck/task.yaml
new file mode 100644
index 0000000..bcbdb1d
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/spread-shellcheck/task.yaml
@@ -0,0 +1,50 @@
+summary: smoke test for the spread-shellcheck tool
+
+details: |
+ Check the spread-shellcheck properly runs the shellcheck
+ tool in all the sections of the spread tasks. Also check the
+ tool can run in a directory tree and skips checks when
+ it finds "shellcheck disable".
+
+backends: [google]
+
+systems: [ ubuntu-20.04-64 ]
+
+prepare: |
+ snap install shellcheck
+
+restore: |
+ snap remove shellcheck
+
+execute: |
+ spread-shellcheck -h | MATCH "usage: spread-shellcheck"
+
+ # Check the format of the spread tests in this project
+ spread-shellcheck "$PROJECT_PATH/tests"
+
+ # Check failing tasks
+ cp "$PWD/tasks/task1" "$PWD/tasks/task.yaml"
+ spread-shellcheck "$PWD/tasks" 2>&1 | MATCH "SC1035"
+ rm "$PWD/tasks/task.yaml"
+
+ cp "$PWD/tasks/task2" "$PWD/tasks/task.yaml"
+ spread-shellcheck "$PWD/tasks" 2>&1 | MATCH "SC1035"
+ rm "$PWD/tasks/task.yaml"
+
+ cp "$PWD/tasks/task3" "$PWD/tasks/task.yaml"
+ spread-shellcheck "$PWD/tasks" 2>&1 | MATCH "SC1035"
+ rm "$PWD/tasks/task.yaml"
+
+ cp "$PWD/tasks/task4" "$PWD/tasks/task.yaml"
+ spread-shellcheck "$PWD/tasks" 2>&1 | MATCH "SC1035"
+ rm "$PWD/tasks/task.yaml"
+
+ # Check that dirs can be excluded
+ cp "$PWD/tasks/task4" "$PWD/tasks/task.yaml"
+ spread-shellcheck "$PROJECT_PATH/tests" -e "$PWD/tasks"
+ rm "$PWD/tasks/task.yaml"
+
+ # Check that files can be excluded
+ cp "$PWD/tasks/task4" "$PWD/tasks/task.yaml"
+ spread-shellcheck "$PROJECT_PATH/tests" "$PWD/tasks/task.yaml" -e "$PWD/tasks"
+ rm "$PWD/tasks/task.yaml"
diff --git a/tests/lib/external/snapd-testing-tools/tests/spread-shellcheck/tasks/task1 b/tests/lib/external/snapd-testing-tools/tests/spread-shellcheck/tasks/task1
new file mode 100644
index 0000000..073ab02
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/spread-shellcheck/tasks/task1
@@ -0,0 +1,13 @@
+summary: this is the summary
+
+prepare: |
+ echo "preparing"
+
+restore: |
+ echo "restoring"
+
+debug: |
+ echo "debugging"
+
+execute: |
+ ![ -z "something" ]
diff --git a/tests/lib/external/snapd-testing-tools/tests/spread-shellcheck/tasks/task2 b/tests/lib/external/snapd-testing-tools/tests/spread-shellcheck/tasks/task2
new file mode 100644
index 0000000..d6f7b49
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/spread-shellcheck/tasks/task2
@@ -0,0 +1,13 @@
+summary: this is the summary
+
+prepare: |
+ ![ -z "something" ]
+
+restore: |
+ echo "restoring"
+
+debug: |
+ echo "debugging"
+
+execute: |
+ echo "executing"
diff --git a/tests/lib/external/snapd-testing-tools/tests/spread-shellcheck/tasks/task3 b/tests/lib/external/snapd-testing-tools/tests/spread-shellcheck/tasks/task3
new file mode 100644
index 0000000..eabb81e
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/spread-shellcheck/tasks/task3
@@ -0,0 +1,13 @@
+summary: this is the summary
+
+prepare: |
+ echo "preparing"
+
+restore: |
+ ![ -z "something" ]
+
+debug: |
+ echo "debugging"
+
+execute: |
+ echo "executing"
diff --git a/tests/lib/external/snapd-testing-tools/tests/spread-shellcheck/tasks/task4 b/tests/lib/external/snapd-testing-tools/tests/spread-shellcheck/tasks/task4
new file mode 100644
index 0000000..11f915b
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/spread-shellcheck/tasks/task4
@@ -0,0 +1,13 @@
+summary: this is the summary
+
+prepare: |
+ echo "preparing"
+
+restore: |
+ echo "restoring"
+
+debug: |
+ ![ -z "something" ]
+
+execute: |
+ echo "executing"
diff --git a/tests/lib/external/snapd-testing-tools/tests/tests.backup/task.yaml b/tests/lib/external/snapd-testing-tools/tests/tests.backup/task.yaml
new file mode 100644
index 0000000..140abeb
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/tests.backup/task.yaml
@@ -0,0 +1,63 @@
+summary: tests for the tests.backup tool
+
+details: |
+ Check the tests.backup tool properly backs up and
+ restores a specific directory.
+
+backends: [google]
+
+execute: |
+ # Without any arguments a help message is printed.
+ tests.backup | MATCH "usage: tests.backup prepare"
+ tests.backup | MATCH " tests.backup restore"
+
+ # Both -h and --help are recognized.
+ tests.backup --help | MATCH "usage: tests.backup"
+ tests.backup -h | MATCH "usage: tests.backup"
+
+ # Unknown commands and options are reported
+ tests.backup --foo 2>&1 | MATCH "tests.backup: unknown option --foo"
+ tests.backup foo 2>&1 | MATCH "tests.backup: unknown command foo"
+
+ # Create a file and a directory inside the current path
+ touch testfile-old
+ mkdir testdir-old
+
+ # Check prepare creates a backup for the current directory
+ test ! -f "${PWD}.tar"
+ tests.backup prepare
+ test -f "${PWD}.tar"
+
+ # Delete old data and create new data
+ rm "${PWD}/testfile-old"
+ rm -r testdir-old
+ touch testfile-new
+ mkdir testdir-new
+
+ # Restore the backup
+ tests.backup restore
+
+ # Check old files and directories are restored after restore
+ test -e testfile-old
+ test -e testdir-old
+
+ # Check new files and directories are gone after restore
+ test ! -e testfile-new
+ test ! -e testdir-new
+
+ # Check the backup file is gone
+ test ! -e "${PWD}.tar"
+
+ # Validate restore cannot be called if backup file does not exist
+ tests.backup restore 2>&1 | MATCH "tests.backup: cannot restore ${PWD}.tar, the file does not exist"
+
+ # Check the tool support a path to prepare and restore
+ TMP_DIR=$(mktemp -d)
+ tests.backup prepare "$TMP_DIR"
+ test -e "${TMP_DIR}.tar"
+ tests.backup restore "$TMP_DIR"
+ test ! -e "${TMP_DIR}.tar"
+
+ # Validate prepare cannot be called if backup dir does not exist
+ rm -rf "$TMP_DIR"
+ tests.backup prepare "$TMP_DIR" 2>&1 | MATCH "tests.backup: cannot backup $TMP_DIR, not a directory"
diff --git a/tests/lib/external/snapd-testing-tools/tests/tests.cleanup/task.yaml b/tests/lib/external/snapd-testing-tools/tests/tests.cleanup/task.yaml
new file mode 100644
index 0000000..d230365
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/tests.cleanup/task.yaml
@@ -0,0 +1,81 @@
+summary: tests for the tests.cleanup tool
+
+details: |
+ Check the tests.cleanup tool allows to defer, pop and
+ restore commands in tests.
+
+backends: [google]
+
+systems: [ubuntu-18.04-64]
+
+execute: |
+ # Without any arguments a help message is printed.
+ tests.cleanup | MATCH "usage: tests.cleanup defer \[args\]"
+ tests.cleanup | MATCH " tests.cleanup pop"
+ tests.cleanup | MATCH " tests.cleanup restore"
+
+ # Both -h and --help are also recognized.
+ tests.cleanup --help | MATCH "usage: tests.cleanup"
+ tests.cleanup -h | MATCH "usage: tests.cleanup"
+
+ # Unknown commands and options are reported
+ tests.cleanup --foo 2>&1 | MATCH "tests.cleanup: unknown option --foo"
+ tests.cleanup foo 2>&1 | MATCH "tests.cleanup: unknown command foo"
+
+ # Normal usage consists of a sequence of defer+, restore
+ # Note that restore runs the deferred commands in the opposite order.
+ tests.cleanup defer echo one
+ tests.cleanup defer echo two
+ tests.cleanup defer echo three
+ tests.cleanup restore > restore.log
+ diff -u restore.log - <&1 | MATCH 'tests.cleanup: deferred command "false" failed with exit code 1'
+ test -e defer.sh
+
+ # Clean the defer.sh
+ rm -f defer.sh
+
+ # Deferred commands can be popped and executed one by one. This is useful
+ # to ensure correctness in case of failure while still allowing precise
+ # resource management.
+ tests.cleanup defer echo popped
+ tests.cleanup pop | MATCH popped
+
+ # Popping removes the last command from the stack
+ tests.cleanup defer echo cmd-a
+ tests.cleanup defer echo cmd-b
+ tests.cleanup defer echo cmd-c
+ tests.cleanup pop | MATCH cmd-c
+ diff -u defer.sh - <&1 | MATCH 'tests.cleanup: cannot pop, cleanup stack is empty'
+ not tests.cleanup pop
diff --git a/tests/lib/external/snapd-testing-tools/tests/tests.pkgs/task.yaml b/tests/lib/external/snapd-testing-tools/tests/tests.pkgs/task.yaml
new file mode 100644
index 0000000..1b0ed94
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/tests.pkgs/task.yaml
@@ -0,0 +1,53 @@
+summary: Smoke test for tests.pkgs tool
+
+details: |
+ Check the tests.pkgs tool allows managing native packages independently
+ of the system being used. Verify that packages can be installed, queried
+ and removed among other operations.
+
+backends: [google]
+
+execute: |
+ if os.query is-core; then
+ tests.pkgs -h 2>&1 | MATCH 'tests.pkgs: Ubuntu Core is not supported'
+ exit
+ fi
+
+ # pkgs tool presents the usage screen when invoked without arguments
+ # or with the -h or --help options.
+ tests.pkgs | MATCH 'usage: tests.pkgs install \[--no-install-recommends\] \[PACKAGE...\]'
+ tests.pkgs -h | MATCH 'usage: tests.pkgs install \[--no-install-recommends\] \[PACKAGE...\]'
+ tests.pkgs --help | MATCH 'usage: tests.pkgs install \[--no-install-recommends\] \[PACKAGE...\]'
+
+ # Check the test pkg is not installed
+ not tests.pkgs is-installed test-snapd-pkg-1
+
+ # Install the test pkg and check it is installed and query it
+ tests.pkgs install test-snapd-pkg-1
+ tests.pkgs query test-snapd-pkg-1
+ tests.pkgs is-installed test-snapd-pkg-1
+
+ # Remove the test pkg and check it is not installed anymore and query it
+ tests.pkgs remove test-snapd-pkg-1
+ tests.pkgs query test-snapd-pkg-1
+ not tests.pkgs is-installed test-snapd-pkg-1
+
+ # Install a package with --no-install-recommends option
+ tests.pkgs install --no-install-recommends test-snapd-pkg-1
+ tests.pkgs query test-snapd-pkg-1
+ tests.pkgs remove test-snapd-pkg-1
+
+ # Install 2 test pkgs and check they are is installed
+ tests.pkgs install test-snapd-pkg-1 test-snapd-pkg-2
+ tests.pkgs is-installed test-snapd-pkg-1
+ tests.pkgs is-installed test-snapd-pkg-2
+
+ # Remove 2 test pkgs and check they are is not installed anymore
+ tests.pkgs remove test-snapd-pkg-1 test-snapd-pkg-2
+ not tests.pkgs is-installed test-snapd-pkg-1
+ not tests.pkgs is-installed test-snapd-pkg-2
+
+ # Check the message when a command is not supported
+ tests.pkgs noexist test-snapd-pkg-1 2>&1 | MATCH 'tests.pkgs: unknown command noexist'
+ tests.pkgs -install test-snapd-pkg-1 2>&1 | MATCH 'tests.pkgs: unknown option -install'
+
diff --git a/tests/lib/external/snapd-testing-tools/tests/tests.systemd/task.yaml b/tests/lib/external/snapd-testing-tools/tests/tests.systemd/task.yaml
new file mode 100644
index 0000000..b8d3d3f
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tests/tests.systemd/task.yaml
@@ -0,0 +1,69 @@
+summary: smoke test for the tests.systemd tool
+
+details: |
+ Check the tests.systemd properly manages systemd services.
+ Verify the tool is able to create units, wait for status and
+ stop units.
+
+backends: [google]
+
+systems: [-ubuntu-14.04-64]
+
+restore: |
+ tests.systemd stop-unit --remove my-test
+
+execute: |
+ tests.systemd | MATCH 'usage: tests.systemd create-and-start-unit'
+ tests.systemd -h | MATCH 'usage: tests.systemd create-and-start-unit'
+ tests.systemd --help | MATCH 'usage: tests.systemd create-and-start-unit'
+
+ # check a simple unit
+ tests.systemd create-and-start-unit my-test "/bin/sleep 500"
+ systemctl is-active my-test
+ tests.systemd stop-unit --remove my-test
+ not systemctl is-active my-test
+
+ # check the wait-for-service works when unit is inactive
+ tests.systemd create-and-start-unit my-test "/bin/sleep 5"
+ tests.systemd wait-for-service --state active my-test
+ tests.systemd wait-for-service --state inactive my-test
+ tests.systemd stop-unit --remove my-test
+ systemctl list-unit-files | NOMATCH my-test
+
+ # check the wait-for-service works when unit is inactive
+ tests.systemd create-and-start-unit my-test "/bin/sleep 10"
+ tests.systemd wait-for-service --state active my-test
+ not tests.systemd wait-for-service --state inactive --wait 1 -n 3 my-test
+ tests.systemd wait-for-service --state inactive --wait 2 --attempts 5 my-test
+ tests.systemd stop-unit --remove my-test
+ systemctl list-unit-files | NOMATCH my-test
+
+ # Check stop-unit
+ tests.systemd create-and-start-unit my-test-1 "/bin/sleep 10"
+ tests.systemd create-and-start-unit my-test-2 "/bin/sleep 10"
+ tests.systemd stop-unit my-test-1 my-test-2
+ not systemctl is-active my-test-1
+ not systemctl is-active my-test-2
+ systemctl is-enabled my-test-1
+ systemctl is-enabled my-test-2
+ tests.systemd stop-unit --remove my-test-1 my-test-2
+ not systemctl is-enabled my-test-1
+ not systemctl is-enabled my-test-2
+
+ # check missing parameters and start twice the same unit
+ tests.systemd create-and-start-unit 2>&1 | MATCH "tests.systemd: unit name cannot be empty"
+ tests.systemd create-and-start-unit my-test 2>&1 | MATCH "tests.systemd: unit command line cannot be empty"
+ tests.systemd create-and-start-unit my-test "/bin/sleep 5"
+ tests.systemd create-and-start-unit my-test "/bin/sleep 5" | MATCH "tests.systemd: unit service file already exist, it is going to be overwritten"
+ tests.systemd stop-unit 2>&1 | MATCH "tests.systemd: at least a unit name is required"
+ tests.systemd stop-unit --remove 2>&1 | MATCH "tests.systemd: at least a unit name is required"
+ tests.systemd stop-unit --remove my-test
+
+ # check unit override
+ tests.systemd create-and-start-unit my-test "/bin/sleep 100" "[Unit]\nAfter=foo"
+ systemctl cat my-test | MATCH "^ExecStart=/bin/sleep 100$"
+ systemctl cat my-test | MATCH "^After=foo$"
+ tests.systemd stop-unit --remove my-test
+
+ # check a unit can be removed when it already was removed
+ tests.systemd stop-unit --remove my-test
diff --git a/tests/lib/external/snapd-testing-tools/tools/not b/tests/lib/external/snapd-testing-tools/tools/not
new file mode 100755
index 0000000..ce2aade
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tools/not
@@ -0,0 +1,6 @@
+#!/bin/sh
+# This script executes whatever is given as argument and inverts the return
+# value. It is meant as a workaround for shellcheck discovery
+# https://github.com/koalaman/shellcheck/wiki/SC2251 where shell scripts using
+# "set -e" are not failing with a trivial code like "! true"
+! "$@"
diff --git a/tests/lib/external/snapd-testing-tools/tools/os.paths b/tests/lib/external/snapd-testing-tools/tools/os.paths
new file mode 100755
index 0000000..ff2425d
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tools/os.paths
@@ -0,0 +1,64 @@
+#!/bin/bash
+
+show_help() {
+ echo "usage: os.paths snap-mount-dir, media-dir, libexec-dir"
+ echo ""
+ echo "get paths information for the current system"
+}
+
+snap_mount_dir() {
+ if os.query is-fedora || os.query is-amazon-linux || os.query is-centos || os.query is-arch-linux; then
+ echo "/var/lib/snapd/snap"
+ else
+ echo "/snap"
+ fi
+}
+
+media_dir() {
+ if os.query is-fedora || os.query is-amazon-linux || os.query is-centos || os.query is-arch-linux || os.query is-opensuse; then
+ echo "/run/media"
+ else
+ echo "/media"
+ fi
+}
+
+libexec_dir() {
+ if os.query is-fedora || os.query is-amazon-linux || os.query is-centos || os.query is-opensuse tumbleweed; then
+ echo "/usr/libexec"
+ else
+ echo "/usr/lib"
+ fi
+}
+
+main() {
+ if [ $# -eq 0 ]; then
+ show_help
+ exit 0
+ fi
+
+ local subcommand="$1"
+ local action=
+ while [ $# -gt 0 ]; do
+ case "$1" in
+ -h|--help)
+ show_help
+ exit 0
+ ;;
+ *)
+ action=$(echo "$subcommand" | tr '-' '_')
+ shift
+ break
+ ;;
+ esac
+ done
+
+ if [ -z "$(declare -f "$action")" ]; then
+ echo "os.paths: unknown path $subcommand" >&2
+ show_help
+ exit 1
+ fi
+
+ "$action" "$@"
+}
+
+main "$@"
diff --git a/tests/lib/external/snapd-testing-tools/tools/os.query b/tests/lib/external/snapd-testing-tools/tools/os.query
new file mode 100755
index 0000000..0692560
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tools/os.query
@@ -0,0 +1,282 @@
+#!/bin/bash
+
+show_help() {
+ echo "usage: os.query is-core, is-classic"
+ echo " os.query is-core16, is-core18, is-core20, is-core22, is-core24"
+ echo " os.query is-core-gt, is-core-ge, is-core-lt, is-core-le"
+ echo " os.query is-trusty, is-xenial, is-bionic, is-focal, is-jammy, is-noble"
+ echo " os.query is-ubuntu [ID], is-debian [ID], is-fedora [ID], is-amazon-linux [ID], is-arch-linux, is-centos [ID], is-opensuse [ID]"
+ echo " os.query is-ubuntu-gt [ID], is-ubuntu-ge [ID], is-ubuntu-lt [ID], is-ubuntu-le [ID]"
+ echo " os.query is-pc-amd64, is-pc-i386, is-arm, is-armhf, is-arm64, is-s390x"
+ echo ""
+ echo "Get general information about the current system"
+}
+
+is_core() {
+ grep -qFx 'ID=ubuntu-core' /etc/os-release
+}
+
+is_core16() {
+ grep -qFx 'ID=ubuntu-core' /etc/os-release && grep -qFx 'VERSION_ID="16"' /etc/os-release
+}
+
+is_core18() {
+ grep -qFx 'ID=ubuntu-core' /etc/os-release && grep -qFx 'VERSION_ID="18"' /etc/os-release
+}
+
+is_core20() {
+ grep -qFx 'ID=ubuntu-core' /etc/os-release && grep -qFx 'VERSION_ID="20"' /etc/os-release
+}
+
+is_core22() {
+ grep -qFx 'ID=ubuntu-core' /etc/os-release && grep -qFx 'VERSION_ID="22"' /etc/os-release
+}
+
+is_core24() {
+ grep -qFx 'ID=ubuntu-core' /etc/os-release && grep -qFx 'VERSION_ID="24"' /etc/os-release
+}
+
+is_core_gt() {
+ local VERSION=$1
+ if [ -z "$VERSION" ]; then
+ echo "os.query: version id is expected"
+ exit 1
+ fi
+
+ is_core && compare_ubuntu "$VERSION" "-gt"
+}
+
+is_core_ge() {
+ local VERSION=$1
+ if [ -z "$VERSION" ]; then
+ echo "os.query: version id is expected"
+ exit 1
+ fi
+
+ is_core && compare_ubuntu "$VERSION" "-ge"
+}
+
+is_core_lt() {
+ local VERSION=$1
+ if [ -z "$VERSION" ]; then
+ echo "os.query: version id is expected"
+ exit 1
+ fi
+
+ is_core && compare_ubuntu "$VERSION" "-lt"
+}
+
+is_core_le() {
+ local VERSION=$1
+ if [ -z "$VERSION" ]; then
+ echo "os.query: version id is expected"
+ exit 1
+ fi
+
+ is_core && compare_ubuntu "$VERSION" "-le"
+}
+
+is_classic() {
+ ! is_core
+}
+
+is_trusty() {
+ grep -qFx 'ID=ubuntu' /etc/os-release && grep -qFx 'VERSION_ID="14.04"' /etc/os-release
+}
+
+is_xenial() {
+ grep -qFx 'UBUNTU_CODENAME=xenial' /etc/os-release
+}
+
+is_bionic() {
+ grep -qFx 'UBUNTU_CODENAME=bionic' /etc/os-release
+}
+
+is_focal() {
+ grep -qFx 'UBUNTU_CODENAME=focal' /etc/os-release
+}
+
+is_jammy() {
+ grep -qFx 'UBUNTU_CODENAME=jammy' /etc/os-release
+}
+
+is_noble() {
+ grep -qFx 'UBUNTU_CODENAME=noble' /etc/os-release
+}
+
+is_ubuntu() {
+ VERSION=$1
+ if [ -z "$VERSION" ]; then
+ grep -qFx 'ID=ubuntu' /etc/os-release || grep -qFx 'ID=ubuntu-core' /etc/os-release
+ else
+ grep -qFx 'ID=ubuntu' /etc/os-release && grep -qFx "VERSION_ID=\"$VERSION\"" /etc/os-release
+ fi
+}
+
+is_ubuntu_gt() {
+ is_classic && compare_ubuntu "${1:-}" "-gt"
+}
+
+is_ubuntu_ge() {
+ is_classic && compare_ubuntu "${1:-}" "-ge"
+}
+
+is_ubuntu_lt() {
+ is_classic && compare_ubuntu "${1:-}" "-lt"
+}
+
+is_ubuntu_le() {
+ is_classic && compare_ubuntu "${1:-}" "-le"
+}
+
+compare_ubuntu() {
+ VERSION=$1
+ OPERAND=$2
+
+ if [ -z "$VERSION" ]; then
+ echo "os.query: version id is expected"
+ exit 1
+ fi
+
+ if ! grep -q 'ID=ubuntu' /etc/os-release; then
+ echo "os.query: comparing non ubuntu system"
+ return 1
+ fi
+
+ NUM_RE='^[0-9]+$'
+ NUM_VERSION="$(echo "$VERSION" | tr -d '".')"
+ if ! [[ $NUM_VERSION =~ $NUM_RE ]] ; then
+ echo "os.query: invalid version format \"$VERSION\" provided"
+ exit 1
+ fi
+
+ SYS_VERSION="$(grep 'VERSION_ID' /etc/os-release)"
+ SYS_VERSION="$(echo "${SYS_VERSION#*=}" | tr -d '".')"
+ if ! [[ $SYS_VERSION =~ $NUM_RE ]] ; then
+ echo "os.query: invalid version format \"$SYS_VERSION\" retrieved from system"
+ exit 1
+ fi
+
+ test "$SYS_VERSION" "$OPERAND" "$NUM_VERSION"
+}
+
+is_debian() {
+ VERSION=$1
+ if [ -z "$VERSION" ]; then
+ grep -qFx 'ID=debian' /etc/os-release
+ elif [ "$VERSION" == "sid" ]; then
+ if [ -n "$SPREAD_SYSTEM" ]; then
+ [[ "$SPREAD_SYSTEM" == debian-sid-* ]]
+ else
+ grep -qFx 'ID=debian' /etc/os-release && grep -qE '^PRETTY_NAME=.*/sid"$' /etc/os-release
+ fi
+ else
+ grep -qFx 'ID=debian' /etc/os-release && grep -qFx "VERSION_ID=\"$VERSION\"" /etc/os-release
+ fi
+}
+
+is_fedora() {
+ VERSION=$1
+ if [ -z "$VERSION" ]; then
+ grep -qFx 'ID=fedora' /etc/os-release
+ elif [ "$VERSION" == "rawhide" ]; then
+ if [ -n "$SPREAD_SYSTEM" ]; then
+ [[ "$SPREAD_SYSTEM" == fedora-rawhide-* ]]
+ else
+ grep -qFx 'ID=fedora' /etc/os-release && grep -qFx "REDHAT_BUGZILLA_PRODUCT_VERSION=rawhide" /etc/os-release
+ fi
+ else
+ grep -qFx 'ID=fedora' /etc/os-release && grep -qFx "VERSION_ID=$VERSION" /etc/os-release
+ fi
+}
+
+is_amazon_linux() {
+ VERSION=$1
+ if [ -z "$VERSION" ]; then
+ grep -qFx 'ID="amzn"' /etc/os-release
+ else
+ grep -qFx 'ID="amzn"' /etc/os-release && grep -qFx "VERSION_ID=\"$VERSION\"" /etc/os-release
+ fi
+}
+
+is_centos() {
+ VERSION=$1
+ if [ -z "$VERSION" ]; then
+ grep -qFx 'ID="centos"' /etc/os-release
+ else
+ grep -qFx 'ID="centos"' /etc/os-release && grep -qFx "VERSION_ID=\"$VERSION\"" /etc/os-release
+ fi
+}
+
+is_arch_linux() {
+ grep -qFx 'ID=arch' /etc/os-release
+}
+
+is_opensuse() {
+ VERSION=$1
+ if [ -z "$VERSION" ]; then
+ grep -qFx 'ID="opensuse-leap"' /etc/os-release || grep -qFx 'ID="opensuse-tumbleweed"' /etc/os-release
+ elif [ "$VERSION" == "tumbleweed" ]; then
+ grep -qFx 'ID="opensuse-tumbleweed"' /etc/os-release
+ else
+ grep -qFx 'ID="opensuse-leap"' /etc/os-release && grep -qFx "VERSION_ID=\"$VERSION\"" /etc/os-release
+ fi
+}
+
+is_pc_amd64() {
+ uname -m | grep -qFx 'x86_64'
+}
+
+is_pc_i386() {
+ uname -m | grep -Eq '(i686|i386)'
+}
+
+is_arm() {
+ uname -m | grep -Eq '(^arm.*|^aarch*)'
+}
+
+is_armhf() {
+ uname -m | grep -qx 'armv7.*'
+}
+
+is_arm64() {
+ uname -m | grep -Eq '(aarch64.*|armv8.*)'
+}
+
+is_s390x() {
+ uname -m | grep -qFx 's390x'
+}
+
+
+main() {
+ if [ $# -eq 0 ]; then
+ show_help
+ exit 0
+ fi
+
+ local subcommand="$1"
+ local action=
+ while [ $# -gt 0 ]; do
+ case "$1" in
+ -h|--help)
+ show_help
+ exit 0
+ ;;
+ *)
+ action=$(echo "$subcommand" | tr '-' '_')
+ shift
+ break
+ ;;
+ esac
+ done
+
+ if [ -z "$(declare -f "$action")" ]; then
+ echo "os.query: no such command: $subcommand" >&2
+ show_help
+ exit 1
+ fi
+
+ "$action" "$@"
+}
+
+main "$@"
diff --git a/tests/lib/external/snapd-testing-tools/tools/quiet b/tests/lib/external/snapd-testing-tools/tools/quiet
new file mode 100755
index 0000000..48c1f2a
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tools/quiet
@@ -0,0 +1,32 @@
+#!/bin/bash
+
+# use "quiet foo" when you expect "foo" to produce a lot of output
+# that isn't useful unless foo itself fails.
+quiet() (
+ # note this is a subshell (parens instead of braces around the function)
+ # so this set only affects this function and not the caller
+ { set +x; } >&/dev/null
+
+ # not strictly needed because it's a subshell, but good practice
+ local tf retval
+
+ tf="$(mktemp)"
+
+ set +e
+ "$@" >& "$tf"
+ retval=$?
+ set -e
+
+ if [ "$retval" != "0" ]; then
+ echo "quiet: $*" >&2
+ echo "quiet: exit status $retval. Output follows:" >&2
+ cat "$tf" >&2
+ echo "quiet: end of output." >&2
+ fi
+
+ rm -f -- "$tf"
+
+ return $retval
+)
+
+quiet "$@"
diff --git a/tests/lib/external/snapd-testing-tools/tools/repack-kernel b/tests/lib/external/snapd-testing-tools/tools/repack-kernel
new file mode 100755
index 0000000..92b064d
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tools/repack-kernel
@@ -0,0 +1,230 @@
+#!/bin/bash
+set -e
+
+if [ -n "$D" ]; then
+ set -x
+fi
+
+show_help() {
+ echo "usage: repack-kernel "
+ echo ""
+ echo "handle extraction of the kernel snap to a workspace directory, and later"
+ echo "repacking it back to a snap."
+ echo ""
+ echo " repack-kernel setup - setup system dependencies"
+ echo " repack-kernel extract - extract under workspace tree"
+ echo " repack-kernel prepare - prepare initramfs & kernel for repacking"
+ echo " repack-kernel pack - pack the kernel"
+ echo " repack-kernel cull-firmware - remove unnecessary firmware"
+ echo " repack-kernel cull-modules - remove unnecessary modules"
+ echo ""
+}
+
+setup() {
+ if [ "$UID" != "0" ]; then
+ echo "run as root (only this command)"
+ exit 1
+ fi
+
+ # carries ubuntu-core-initframfs
+ add-apt-repository ppa:snappy-dev/image -y
+ apt update
+
+ if ! dpkg -s ubuntu-core-initramfs >/dev/null 2>&1; then
+ if ! apt install -y ubuntu-core-initramfs; then
+ echo "ubuntu-core-initramfs was not available in deb repository!"
+ exit 1
+ fi
+ fi
+}
+
+get_kver() {
+ local kerneldir="$1"
+ find "$kerneldir" -maxdepth 1 -name "config-*" | grep -Po 'config-\K.*'
+}
+
+extract() {
+ local snap_file="$1"
+ local target="$2"
+
+ if [ -z "$target" ] || [ -z "$snap_file" ]; then
+ echo "repack-kernel: invalid arguments for extract"
+ exit 1
+ fi
+
+ target=$(realpath "$target")
+
+ mkdir -p "$target" "$target/work" "$target/backup"
+
+ # kernel snap is huge, unpacking to current dir
+ unsquashfs -d "$target/kernel" "$snap_file"
+
+ kver="$(get_kver "$target/kernel")"
+
+ # repack initrd magic, beware
+ # assumptions: initrd is compressed with LZ4, cpio block size 512, microcode
+ # at the beginning of initrd image
+
+ # XXX: ideally we should unpack the initrd, replace snap-boostrap and
+ # repack it using ubuntu-core-initramfs --skeleton= this does not
+ # work and the rebuilt kernel.efi panics unable to start init, but we
+ # still need the unpacked initrd to get the right kernel modules
+ objcopy -j .initrd -O binary "$target"/kernel/kernel.efi "$target/work/initrd"
+
+ # copy out the kernel image for create-efi command
+ objcopy -j .linux -O binary "$target"/kernel/kernel.efi "$target/work/vmlinuz-$kver"
+
+ cp -a "$target"/kernel/kernel.efi "$target/backup/"
+
+ # this works on 20.04 but not on 18.04
+ unmkinitramfs "$target"/work/initrd "$target"/work/unpacked-initrd
+
+ # copy the unpacked initrd to use as the target skeleton
+ cp -ar "$target/work/unpacked-initrd" "$target/skeleton"
+
+ echo "prepared workspace at $target"
+ echo " kernel: $target/kernel ($kver)"
+ echo " kernel.efi backup: $target/backup/kernel.efi"
+ echo " temporary artifacts: $target/work"
+ echo " initramfs skeleton: $target/skeleton"
+
+}
+
+prepare() {
+ local target="$1"
+
+ if [ -z "$target" ]; then
+ echo "repack-kernel: missing target for prepare"
+ exit 1
+ fi
+
+ target=$(realpath "$target")
+
+ local kver
+ kver="$(get_kver "$target/kernel")"
+
+ (
+ # all the skeleton edits go to a local copy of distro directory
+ local skeletondir="$target/skeleton"
+
+ cd "$target/work"
+ # XXX: need to be careful to build an initrd using the right kernel
+ # modules from the unpacked initrd, rather than the host which may be
+ # running a different kernel
+ (
+ # accommodate assumptions about tree layout, use the unpacked initrd
+ # to pick up the right modules
+ cd unpacked-initrd/main
+ ubuntu-core-initramfs create-initrd \
+ --kernelver "$kver" \
+ --skeleton "$skeletondir" \
+ --feature main \
+ --kerneldir "lib/modules/$kver" \
+ --output "$target/work/repacked-initrd"
+ )
+
+ # assumes all files are named -$kver
+ ubuntu-core-initramfs create-efi \
+ --kernelver "$kver" \
+ --initrd repacked-initrd \
+ --kernel vmlinuz \
+ --output repacked-kernel.efi
+
+ cp "repacked-kernel.efi-$kver" "$target/kernel/kernel.efi"
+
+ # XXX: needed?
+ chmod +x "$target/kernel/kernel.efi"
+ )
+}
+
+cull_firmware() {
+ local target="$1"
+
+ if [ -z "$target" ]; then
+ echo "repack-kernel: missing target for cull-firmware"
+ exit 1
+ fi
+
+ # XXX: drop ~450MB+ of firmware which should not be needed in under qemu
+ # or the cloud system
+ rm -rf "$target"/kernel/firmware/*
+}
+
+cull_modules() {
+ local target="$1"
+
+ if [ -z "$target" ]; then
+ echo "repack-kernel: missing target for cull-modules"
+ exit 1
+ fi
+
+ target=$(realpath "$target")
+
+ local kver
+ kver="$(get_kver "$target/kernel")"
+
+ (
+ cd "$target/kernel"
+ # drop unnecessary modules
+ awk '{print $1}' < /proc/modules | sort > "$target/work/current-modules"
+ #shellcheck disable=SC2044
+ for m in $(find modules/ -name '*.ko'); do
+ noko=$(basename "$m"); noko="${noko%.ko}"
+ if echo "$noko" | grep -f "$target/work/current-modules" -q ; then
+ echo "keeping $m - $noko"
+ else
+ rm -f "$m"
+ fi
+ done
+
+ # depmod assumes that /lib/modules/$kver is under basepath
+ mkdir -p fake/lib
+ ln -s "$PWD/modules" fake/lib/modules
+ depmod -b "$PWD/fake" -A -v "$kver"
+ rm -rf fake
+ )
+}
+
+pack() {
+ local target="$1"
+
+ if [ -z "$target" ]; then
+ echo "repack-kernel: missing target for pack"
+ exit 1
+ fi
+ snap pack "${@:2}" "$target/kernel"
+}
+
+main() {
+ if [ $# -eq 0 ]; then
+ show_help
+ exit 0
+ fi
+
+ local subcommand="$1"
+ local action=
+ while [ $# -gt 0 ]; do
+ case "$1" in
+ -h|--help)
+ show_help
+ exit 0
+ ;;
+ *)
+ action=$(echo "$subcommand" | tr '-' '_')
+ shift
+ break
+ ;;
+ esac
+ done
+
+ if [ -z "$(declare -f "$action")" ]; then
+ echo "repack-kernel: no such command: $subcommand" >&2
+ show_help
+ exit 1
+ fi
+
+ "$action" "$@"
+}
+
+main "$@"
+
diff --git a/tests/lib/external/snapd-testing-tools/tools/retry b/tests/lib/external/snapd-testing-tools/tools/retry
new file mode 100755
index 0000000..89ed222
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tools/retry
@@ -0,0 +1,157 @@
+#!/usr/bin/env python3
+from __future__ import print_function, absolute_import, unicode_literals
+
+import argparse
+import itertools
+import os
+import subprocess
+import sys
+import time
+
+
+# Define MYPY as False and use it as a conditional for typing import. Despite
+# this declaration mypy will really treat MYPY as True when type-checking.
+# This is required so that we can import typing on Python 2.x without the
+# typing module installed. For more details see:
+# https://mypy.readthedocs.io/en/latest/common_issues.html#import-cycles
+MYPY = False
+if MYPY:
+ from typing import List, Text
+
+def envpair(s):
+ # type: (str) -> str
+ if not "=" in s:
+ raise argparse.ArgumentTypeError("environment variables expected format is 'KEY=VAL' got '{}'".format(s))
+ return s
+
+def _make_parser():
+ # type: () -> argparse.ArgumentParser
+ parser = argparse.ArgumentParser(
+ description="""
+Retry executes COMMAND at most N times, waiting for SECONDS between each
+attempt. On failure the exit code from the final attempt is returned.
+"""
+ )
+ parser.add_argument(
+ "-n",
+ "--attempts",
+ metavar="N",
+ type=int,
+ default=3,
+ help="number of attempts (default %(default)s)",
+ )
+ parser.add_argument(
+ "--wait",
+ metavar="SECONDS",
+ type=float,
+ default=1,
+ help="grace period between attempts (default %(default)ss)",
+ )
+ parser.add_argument(
+ "--env",
+ type=envpair,
+ metavar='KEY=VAL',
+ action='append',
+ default=[],
+ help="environment variable to use with format KEY=VALUE (no default)",
+ )
+ parser.add_argument(
+ "--maxmins",
+ metavar="MINUTES",
+ type=float,
+ default=0,
+ help="number of minutes after which to give up (no default, if set attempts is ignored)",
+ )
+ parser.add_argument(
+ "--quiet",
+ dest="verbose",
+ action="store_false",
+ default=True,
+ help="refrain from printing any output",
+ )
+ parser.add_argument(
+ "cmd", metavar="COMMAND", nargs="...", help="command to execute"
+ )
+ return parser
+
+
+def get_env(env):
+ # type: (List[str]) -> dict[str,str]
+ new_env = os.environ.copy()
+ maxsplit=1 # no keyword support for str.split() in py2
+ for key, val in [s.split("=", maxsplit) for s in env]:
+ new_env[key] = val
+ return new_env
+
+
+def run_cmd(cmd, n, wait, maxmins, verbose, env):
+ # type: (List[Text], int, float, float, bool, List[str]) -> int
+ if maxmins != 0:
+ attempts = itertools.count(1)
+ t0 = time.time()
+ after = "{} minutes".format(maxmins)
+ of_attempts_suffix = ""
+ else:
+ attempts = range(1, n + 1)
+ after = "{} attempts".format(n)
+ of_attempts_suffix = " of {}".format(n)
+ retcode = 0
+ i = 0
+ new_env = get_env(env)
+ for i in attempts:
+ retcode = subprocess.call(cmd, env=new_env)
+ if retcode == 0:
+ return 0
+ if verbose:
+ print(
+ "retry: command {} failed with code {}".format(" ".join(cmd), retcode),
+ file=sys.stderr,
+ )
+ if maxmins != 0:
+ elapsed = (time.time()-t0)/60
+ if elapsed > maxmins:
+ break
+ if i < n or maxmins != 0:
+ if verbose:
+ print(
+ "retry: next attempt in {} second(s) (attempt {}{})".format(
+ wait, i, of_attempts_suffix
+ ),
+ file=sys.stderr,
+ )
+ time.sleep(wait)
+
+ if verbose and i > 1:
+ print(
+ "retry: command {} keeps failing after {}".format(
+ " ".join(cmd), after
+ ),
+ file=sys.stderr,
+ )
+ return retcode
+
+
+def main():
+ # type: () -> None
+ parser = _make_parser()
+ ns = parser.parse_args()
+ # The command cannot be empty but it is difficult to express in argparse itself.
+ if len(ns.cmd) == 0:
+ parser.print_usage()
+ parser.exit(0)
+ # Return the last exit code as the exit code of this process.
+ try:
+ retcode = run_cmd(ns.cmd, ns.attempts, ns.wait, ns.maxmins, ns.verbose, ns.env)
+ except OSError as exc:
+ if ns.verbose:
+ print(
+ "retry: cannot execute command {}: {}".format(" ".join(ns.cmd), exc),
+ file=sys.stderr,
+ )
+ raise SystemExit(1)
+ else:
+ raise SystemExit(retcode)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/tests/lib/external/snapd-testing-tools/tools/snaps.cleanup b/tests/lib/external/snapd-testing-tools/tools/snaps.cleanup
new file mode 100755
index 0000000..5cdbb30
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tools/snaps.cleanup
@@ -0,0 +1,94 @@
+#!/bin/bash -e
+
+show_help() {
+ echo "usage: snaps.cleanup [--skip ]"
+ echo
+ echo "cleanup the snaps installed in the current system"
+}
+
+cleanup() {
+ snap_mount_dir="$(os.paths snap-mount-dir)"
+ remove_bases=""
+ skip_bases=""
+
+ gadget_name="$(snaps.name gadget)"
+ kernel_name="$(snaps.name kernel)"
+ core_name="$(snaps.name core)"
+
+ # remove all app snaps first
+ for snap in "$snap_mount_dir"/*; do
+ snap="${snap:6}"
+ case "$snap" in
+ "bin" | "$gadget_name" | "$kernel_name" | "$core_name" | "snapd" |README)
+ ;;
+ *)
+ # Check if a snap should be kept, there's a list of those in spread.yaml.
+ local keep=0
+ # shellcheck disable=SC2068
+ for skip_snap in $@; do
+ if [ "$snap" = "$skip_snap" ]; then
+ # Skip the snap base removal as well
+ snap_base=$(grep "base:" "$snap_mount_dir/$snap/current/meta/snap.yaml" | awk '{ print $2 }')
+ if [ -n "$snap_base" ]; then
+ skip_bases="$skip_bases $snap_base"
+ fi
+ keep=1
+ break
+ fi
+ done
+ if [ "$keep" -eq 0 ]; then
+ # remove regular snaps right away; collect base snaps for removal in the second step below?
+ if snap info --verbose "$snap" | grep -E '^type: +(base|core)'; then
+ if [ -z "$remove_bases" ]; then
+ remove_bases="$snap"
+ else
+ remove_bases="$remove_bases $snap"
+ fi
+ else
+ snap remove --purge "$snap"
+ fi
+ fi
+ ;;
+ esac
+ done
+ # remove all base/os snaps at the end
+ # skip the base snaps for the
+ if [ -n "$remove_bases" ]; then
+ for base in $remove_bases; do
+ if ! [[ $skip_bases =~ (^|[[:space:]])$base($|[[:space:]]) ]]; then
+ snap remove --purge "$base"
+ if [ -d "$snap_mount_dir/$base" ]; then
+ echo "Error: removing base $base has unexpected leftover dir $snap_mount_dir/$base"
+ ls -al "$snap_mount_dir"
+ ls -al "$snap_mount_dir/$base"
+ exit 1
+ fi
+ fi
+ done
+ fi
+}
+
+main() {
+ local skip
+ skip=""
+ while [ $# -gt 0 ]; do
+ case "$1" in
+ -h|--help)
+ show_help
+ exit 0
+ ;;
+ --skip)
+ skip="$skip $2"
+ shift 2
+ ;;
+ *)
+ echo "snaps.cleanup: unknown subcommand $1" >&2
+ show_help
+ exit 1
+ ;;
+ esac
+ done
+ cleanup "$skip"
+}
+
+main "$@"
diff --git a/tests/lib/external/snapd-testing-tools/tools/snaps.name b/tests/lib/external/snapd-testing-tools/tools/snaps.name
new file mode 100755
index 0000000..e562e92
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tools/snaps.name
@@ -0,0 +1,77 @@
+#!/bin/bash
+
+show_help() {
+ echo "usage: snaps.name gadget, kernel, core, snap-suffix"
+ echo ""
+ echo "get the name of a specific snap for the current system"
+}
+
+gadget_name() {
+ snap list | grep -E '(gadget$|gadget,)' | awk '{ print $1 }'
+}
+
+kernel_name(){
+ # TODO this may not always be true during remodel scenarios
+ # if the old kernel remains
+ snap list | grep -E '(kernel$|kernel,)' | awk '{ print $1 }'
+}
+
+core_name(){
+ local core_name
+ core_name="$(snap model --verbose | grep -Po "^base:\\s+\\K.*" || true)"
+ if [ -z "$core_name" ]; then
+ core_name="core"
+ fi
+ echo "$core_name"
+}
+
+snap_suffix(){
+ if os.query is-core18; then
+ echo "-core18"
+ elif os.query is-core20; then
+ echo "-core20"
+ elif os.query is-core22; then
+ echo "-core22"
+ elif os.query is-core24; then
+ echo "-core24"
+ fi
+}
+
+main() {
+ if [ $# -eq 0 ]; then
+ show_help
+ exit 0
+ fi
+
+ while [ $# -gt 0 ]; do
+ case "$1" in
+ -h|--help)
+ show_help
+ exit 0
+ ;;
+ gadget)
+ gadget_name
+ exit
+ ;;
+ kernel)
+ kernel_name
+ exit
+ ;;
+ core)
+ core_name
+ exit
+ ;;
+ snap-suffix)
+ snap_suffix
+ exit
+ ;;
+ *)
+ echo "snaps.name: unknown snap $1" >&2
+ exit 1
+ ;;
+ esac
+ done
+
+}
+
+main "$@"
diff --git a/tests/lib/external/snapd-testing-tools/tools/tests.backup b/tests/lib/external/snapd-testing-tools/tools/tests.backup
new file mode 100755
index 0000000..62d210c
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tools/tests.backup
@@ -0,0 +1,69 @@
+#!/bin/bash -e
+# Tool used to backup/restore a specific directory
+# It is used by the test tool to make sure each test
+# leaves the test directory as was initially
+
+show_help() {
+ echo "usage: tests.backup prepare [PATH]"
+ echo " tests.backup restore [PATH]"
+}
+
+cmd_prepare() {
+ local BACKUP_PATH=$1
+
+ if [ ! -d "$BACKUP_PATH" ]; then
+ echo "tests.backup: cannot backup $BACKUP_PATH, not a directory" >&2
+ exit 1
+ fi
+ tar cf "${BACKUP_PATH}.tar" "$BACKUP_PATH"
+}
+
+cmd_restore() {
+ local BACKUP_PATH=$1
+ if [ -f "${BACKUP_PATH}.tar" ]; then
+ # Find all the files in the path $BACKUP_PATH and delete them
+ # This command deletes also the hidden files
+ find "${BACKUP_PATH}" -maxdepth 1 -mindepth 1 -exec rm -rf {} \;
+ tar -C/ -xf "${BACKUP_PATH}.tar"
+ rm "${BACKUP_PATH}.tar"
+ else
+ echo "tests.backup: cannot restore ${BACKUP_PATH}.tar, the file does not exist" >&2
+ exit 1
+ fi
+}
+
+main() {
+ if [ $# -eq 0 ]; then
+ show_help
+ exit 0
+ fi
+
+ while [ $# -gt 0 ]; do
+ case "$1" in
+ -h|--help)
+ show_help
+ exit
+ ;;
+ prepare)
+ local BACKUP_PATH="${2:-$(pwd)}"
+ cmd_prepare "$BACKUP_PATH"
+ exit
+ ;;
+ restore)
+ local BACKUP_PATH="${2:-$(pwd)}"
+ cmd_restore "$BACKUP_PATH"
+ exit
+ ;;
+ -*)
+ echo "tests.backup: unknown option $1" >&2
+ exit 1
+ ;;
+ *)
+ echo "tests.backup: unknown command $1" >&2
+ exit 1
+ ;;
+ esac
+ done
+}
+
+main "$@"
diff --git a/tests/lib/external/snapd-testing-tools/tools/tests.cleanup b/tests/lib/external/snapd-testing-tools/tools/tests.cleanup
new file mode 100755
index 0000000..e8f6937
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tools/tests.cleanup
@@ -0,0 +1,97 @@
+#!/bin/bash -e
+
+show_help() {
+ echo "usage: tests.cleanup defer [args]"
+ echo " tests.cleanup pop"
+ echo " tests.cleanup restore"
+ echo
+ echo "COMMANDS:"
+ echo " restore: invokes all cleanup commands in reverse order"
+ echo " defer: pushes a command onto the cleanup stack"
+ echo " pop: invoke the most recently deferred command, discarding it"
+ echo
+ echo "The defer and pop commands can be to establish temporary"
+ echo "cleanup handler and remove it, in the case of success"
+}
+
+cmd_defer() {
+ if [ ! -e defer.sh ]; then
+ truncate --size=0 defer.sh
+ chmod +x defer.sh
+ fi
+ echo "$*" >> defer.sh
+}
+
+run_one_cmd() {
+ CMD="$1"
+ set +e
+ sh -ec "$CMD"
+ RET=$?
+ set -e
+ if [ $RET -ne 0 ]; then
+ echo "tests.cleanup: deferred command \"$CMD\" failed with exit code $RET"
+ exit $RET
+ fi
+}
+
+cmd_pop() {
+ if [ ! -s defer.sh ]; then
+ echo "tests.cleanup: cannot pop, cleanup stack is empty" >&2
+ exit 1
+ fi
+ head -n-1 defer.sh >defer.sh.pop
+ trap "mv defer.sh.pop defer.sh" EXIT
+ tail -n-1 defer.sh | while read -r CMD; do
+ run_one_cmd "$CMD"
+ done
+}
+
+cmd_restore() {
+ if [ -e defer.sh ]; then
+ tac defer.sh | while read -r CMD; do
+ run_one_cmd "$CMD"
+ done
+ rm -f defer.sh
+ fi
+}
+
+main() {
+ if [ $# -eq 0 ]; then
+ show_help
+ exit
+ fi
+
+ while [ $# -gt 0 ]; do
+ case "$1" in
+ -h|--help)
+ show_help
+ exit
+ ;;
+ defer)
+ shift
+ cmd_defer "$@"
+ exit
+ ;;
+ pop)
+ shift
+ cmd_pop "$@"
+ exit
+ ;;
+ restore)
+ shift
+ cmd_restore
+ exit
+ ;;
+ -*)
+ echo "tests.cleanup: unknown option $1" >&2
+ exit 1
+ ;;
+ *)
+ echo "tests.cleanup: unknown command $1" >&2
+ exit 1
+ ;;
+ esac
+ done
+}
+
+main "$@"
diff --git a/tests/lib/external/snapd-testing-tools/tools/tests.pkgs b/tests/lib/external/snapd-testing-tools/tools/tests.pkgs
new file mode 100755
index 0000000..7c122e9
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tools/tests.pkgs
@@ -0,0 +1,163 @@
+#!/bin/bash -e
+
+show_help() {
+ echo "usage: tests.pkgs install [--no-install-recommends] [PACKAGE...]"
+ echo " tests.pkgs remove [PACKAGE...]"
+ echo " tests.pkgs is-installed [PACKAGE]"
+ echo " tests.pkgs query [PACKAGE]"
+ echo " tests.pkgs list-installed"
+ echo
+ echo "Package names are standardized based on Debian package names"
+ echo "internally, package names are re-mapped to fit the convention"
+ echo "of the used system."
+}
+
+unsupported() {
+ echo "tests.pkgs: cannot manage packages on this system" >&2
+ exit 1
+}
+
+cmd_install() {
+ # This is re-defined by the backend file.
+ unsupported
+}
+
+cmd_install_local() {
+ # This is re-defined by the backend file.
+ unsupported
+}
+
+cmd_is_installed() {
+ # This is re-defined by the backend file.
+ unsupported
+}
+
+cmd_query() {
+ # This is re-defined by the backend file.
+ unsupported
+}
+
+cmd_list_installed() {
+ # This is re-defined by the backend file.
+ unsupported
+}
+
+cmd_remove() {
+ # This is re-defined by the backend file.
+ unsupported
+}
+
+remap_one() {
+ # This may be re-defined by the backend file.
+ echo "$1"
+}
+
+remap_many() {
+ local many
+ many=""
+ for pkg in "$@"; do
+ if [ -z "$many" ]; then
+ many="$(remap_one "$pkg")"
+ else
+ many="$many $(remap_one "$pkg")"
+ fi
+ done
+ echo "$many"
+}
+
+import_backend() {
+ local TOOLS_DIR
+ TOOLS_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
+ if [ -n "$TESTSTOOLS" ]; then
+ TOOLS_DIR="$TESTSTOOLS"
+ fi
+
+ if os.query is-core; then
+ echo "tests.pkgs: Ubuntu Core is not supported" >&2
+ return 1
+ elif os.query is-ubuntu || os.query is-debian; then
+ # Disabled because when the project is imported as a submodule the
+ # source is not found failing with SC1090 and SC1091
+ #shellcheck disable=SC1090,SC1091
+ . "$TOOLS_DIR/tests.pkgs.apt.sh"
+ elif os.query is-fedora || os.query is-centos || os.query is-amazon-linux; then
+ # Disabled because when the project is imported as a submodule the
+ # source is not found failing with SC1090 and SC1091
+ #shellcheck disable=SC1090,SC1091
+ . "$TOOLS_DIR/tests.pkgs.dnf-yum.sh"
+ elif os.query is-opensuse; then
+ # Disabled because when the project is imported as a submodule the
+ # source is not found failing with SC1090 and SC1091
+ #shellcheck disable=SC1090,SC1091
+ . "$TOOLS_DIR/tests.pkgs.zypper.sh"
+ elif os.query is-arch-linux; then
+ # Disabled because when the project is imported as a submodule the
+ # source is not found failing with SC1090 and SC1091
+ #shellcheck disable=SC1090,SC1091
+ . "$TOOLS_DIR/tests.pkgs.pacman.sh"
+ else
+ echo "tests.pkgs: cannot import packaging backend" >&2
+ return 1
+ fi
+}
+
+main() {
+ if [ $# -eq 0 ]; then
+ show_help
+ exit 1
+ fi
+
+ import_backend
+
+ action=
+ while [ $# -gt 0 ]; do
+ case "$1" in
+ -h|--help)
+ show_help
+ exit 0
+ ;;
+ --)
+ shift
+ break
+ ;;
+ install|remove|query|is-installed|list-installed)
+ action="$1"
+ shift
+ break # consume remaining arguments
+ ;;
+ -*)
+ echo "tests.pkgs: unknown option $1" >&2
+ exit 1
+ ;;
+ *)
+ echo "tests.pkgs: unknown command $1" >&2
+ exit 1
+ ;;
+ esac
+ done
+
+ case "$action" in
+ install)
+ # shellcheck disable=SC2046
+ cmd_install $(remap_many "$@")
+ ;;
+ is-installed)
+ cmd_is_installed "$(remap_one "$@")"
+ ;;
+ query)
+ cmd_query "$(remap_one "$@")"
+ ;;
+ list-installed)
+ cmd_list_installed
+ ;;
+ remove)
+ cmd_remove "$(remap_many "$@")"
+ ;;
+ *)
+ echo "tests.pkgs: unknown action $action" >&2
+ exit 1
+ ;;
+ esac
+}
+
+main "$@"
diff --git a/tests/lib/external/snapd-testing-tools/tools/tests.pkgs.apt.sh b/tests/lib/external/snapd-testing-tools/tools/tests.pkgs.apt.sh
new file mode 100644
index 0000000..a2ec54a
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tools/tests.pkgs.apt.sh
@@ -0,0 +1,65 @@
+#!/bin/bash
+
+remap_one() {
+ case "$1" in
+ man)
+ if os.query is-debian; then
+ echo "man-db"
+ else
+ echo "$1"
+ fi
+ ;;
+ printer-driver-cups-pdf)
+ if os.query is-debian || os.query is-trusty; then
+ echo "cups-pdf"
+ else
+ echo "$1"
+ fi
+ ;;
+ test-snapd-pkg-1)
+ echo "curseofwar"
+ ;;
+ test-snapd-pkg-2)
+ echo "robotfindskitten"
+ ;;
+ *)
+ echo "$1"
+ ;;
+ esac
+}
+
+cmd_install() {
+ apt-get update
+
+ local APT_FLAGS="--yes"
+ while [ -n "$1" ]; do
+ case "$1" in
+ --no-install-recommends)
+ APT_FLAGS="$APT_FLAGS --no-install-recommends"
+ shift
+ ;;
+ *)
+ break
+ ;;
+ esac
+ done
+ # shellcheck disable=SC2068,SC2086
+ apt-get install $APT_FLAGS $@
+}
+
+cmd_is_installed() {
+ dpkg -S "$1" >/dev/null 2>&1
+}
+
+cmd_query() {
+ apt-cache policy "$1"
+}
+
+cmd_list_installed() {
+ apt list --installed | cut -d/ -f1 | sort
+}
+
+cmd_remove() {
+ # shellcheck disable=SC2068
+ apt-get remove --yes $@
+}
diff --git a/tests/lib/external/snapd-testing-tools/tools/tests.pkgs.dnf-yum.sh b/tests/lib/external/snapd-testing-tools/tools/tests.pkgs.dnf-yum.sh
new file mode 100644
index 0000000..16a9e02
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tools/tests.pkgs.dnf-yum.sh
@@ -0,0 +1,75 @@
+#!/bin/bash
+
+remap_one() {
+ case "$1" in
+ xdelta3)
+ echo "xdelta"
+ ;;
+ openvswitch-switch)
+ echo "openvswitch"
+ ;;
+ printer-driver-cups-pdf)
+ echo "cups-pdf"
+ ;;
+ python3-gi)
+ echo "python3-gobject"
+ ;;
+ test-snapd-pkg-1)
+ echo "freeglut"
+ ;;
+ test-snapd-pkg-2)
+ echo "texlive-base"
+ ;;
+ *)
+ echo "$1"
+ ;;
+ esac
+}
+
+cmd_install() {
+ local CMD="dnf"
+ if [ -z "$(command -v dnf)" ]; then
+ CMD="yum"
+ fi
+ local DNF_YUM_FLAGS="-y"
+
+ while [ -n "$1" ]; do
+ case "$1" in
+ --no-install-recommends)
+ DNF_YUM_FLAGS="$DNF_YUM_FLAGS --setopt=install_weak_deps=False"
+ shift
+ ;;
+ *)
+ break
+ ;;
+ esac
+ done
+
+ # shellcheck disable=SC2068,SC2086
+ $CMD install $DNF_YUM_FLAGS $@
+}
+
+cmd_is_installed() {
+ rpm -qi "$1" >/dev/null 2>&1
+}
+
+cmd_query() {
+ if [ "$(command -v dnf)" != "" ]; then
+ dnf info "$1"
+ else
+ yum info "$1"
+ fi
+}
+
+cmd_list_installed() {
+ rpm -qa | sort
+}
+
+cmd_remove() {
+ # shellcheck disable=SC2068
+ if [ "$(command -v dnf)" != "" ]; then
+ dnf remove -y $@
+ else
+ yum remove -y $@
+ fi
+}
diff --git a/tests/lib/external/snapd-testing-tools/tools/tests.pkgs.pacman.sh b/tests/lib/external/snapd-testing-tools/tools/tests.pkgs.pacman.sh
new file mode 100644
index 0000000..c3e8d3b
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tools/tests.pkgs.pacman.sh
@@ -0,0 +1,71 @@
+#!/bin/bash
+
+remap_one() {
+ case "$1" in
+ python3-yaml)
+ echo "python-yaml"
+ ;;
+ dbus-x11)
+ # no separate dbus-x11 package in arch
+ echo "dbus"
+ ;;
+ printer-driver-cups-pdf)
+ echo "cups-pdf"
+ ;;
+ openvswitch-switch)
+ echo "openvswitch"
+ ;;
+ man)
+ echo "man-db"
+ ;;
+ python3-dbus)
+ echo "python-dbus"
+ ;;
+ python3-gi)
+ echo "python-gobject"
+ ;;
+ test-snapd-pkg-1)
+ echo "cmus"
+ ;;
+ test-snapd-pkg-2)
+ echo "robotfindskitten"
+ ;;
+ *)
+ echo "$1"
+ ;;
+ esac
+}
+
+cmd_install() {
+ local PACMAN_FLAGS="--noconfirm"
+ while [ -n "$1" ]; do
+ case "$1" in
+ --no-install-recommends)
+ # Pacman only ever installs the required dependencies
+ shift
+ ;;
+ *)
+ break
+ ;;
+ esac
+ done
+ # shellcheck disable=SC2068,SC2086
+ pacman -S $PACMAN_FLAGS $@
+}
+
+cmd_is_installed() {
+ pacman -Qi "$1" >/dev/null 2>&1
+}
+
+cmd_query() {
+ pacman -Si "$1"
+}
+
+cmd_list_installed() {
+ pacman -Qe | awk '{ print $1 }' | sort
+}
+
+cmd_remove() {
+ # shellcheck disable=SC2068
+ pacman -Rnsc --noconfirm $@
+}
diff --git a/tests/lib/external/snapd-testing-tools/tools/tests.pkgs.zypper.sh b/tests/lib/external/snapd-testing-tools/tools/tests.pkgs.zypper.sh
new file mode 100644
index 0000000..044ec25
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tools/tests.pkgs.zypper.sh
@@ -0,0 +1,66 @@
+#!/bin/bash
+
+remap_one() {
+ case "$1" in
+ python3-yaml)
+ echo "python3-PyYAML"
+ ;;
+ dbus-x11)
+ echo "dbus-1-x11"
+ ;;
+ printer-driver-cups-pdf)
+ echo "cups-pdf"
+ ;;
+ python3-dbus)
+ # In OpenSUSE Leap 15, this is renamed to python3-dbus-python
+ echo "dbus-1-python3"
+ ;;
+ python3-gi)
+ echo "python3-gobject"
+ ;;
+ test-snapd-pkg-1)
+ echo "nudoku"
+ ;;
+ test-snapd-pkg-2)
+ echo "system-user-games"
+ ;;
+ *)
+ echo "$1"
+ ;;
+ esac
+}
+
+cmd_install() {
+ local ZYPPER_FLAGS="-y"
+ while [ -n "$1" ]; do
+ case "$1" in
+ --no-install-recommends)
+ ZYPPER_FLAGS="$ZYPPER_FLAGS --no-recommends"
+ shift
+ ;;
+ *)
+ break
+ ;;
+ esac
+ done
+
+ # shellcheck disable=SC2068,SC2086
+ zypper install $ZYPPER_FLAGS $@
+}
+
+cmd_is_installed() {
+ rpm -qi "$1" >/dev/null 2>&1
+}
+
+cmd_query() {
+ zypper info "$1"
+}
+
+cmd_list_installed() {
+ rpm -qa | sort
+}
+
+cmd_remove() {
+ # shellcheck disable=SC2068
+ zypper remove -y $@
+}
diff --git a/tests/lib/external/snapd-testing-tools/tools/tests.systemd b/tests/lib/external/snapd-testing-tools/tools/tests.systemd
new file mode 100755
index 0000000..fc63338
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/tools/tests.systemd
@@ -0,0 +1,177 @@
+#!/bin/bash -e
+
+show_help() {
+ echo "usage: tests.systemd create-and-start-unit "
+ echo " tests.systemd stop-unit [--remove] ..."
+ echo " tests.systemd wait-for-service [-n|--attempts retries] [--wait seconds] [--state STATE] "
+}
+
+# Create and start a persistent systemd unit that survives reboots. Use as:
+# systemd_create_and_start_unit "name" "my-service --args"
+# The third arg supports "overrides" which allow to customize the service
+# as needed, e.g.:
+# systemd_create_and_start_unit "name" "start" "[Unit]\nAfter=foo"
+create_and_start_unit() {
+ local name=$1
+ local start_line=$2
+ local override=$3
+
+ if [ -z "$name" ]; then
+ echo "tests.systemd: unit name cannot be empty"
+ return 1
+ fi
+ if [ -z "$start_line" ]; then
+ echo "tests.systemd: unit command line cannot be empty"
+ return 1
+ fi
+ if [ -f "/etc/systemd/system/$name.service" ]; then
+ echo "tests.systemd: unit service file already exist, it is going to be overwritten"
+ fi
+
+ printf '[Unit]\nDescription=Support for test %s\n[Service]\nType=simple\nExecStart=%s\n[Install]\nWantedBy=multi-user.target\n' "$name" "$start_line" > "/etc/systemd/system/$name.service"
+ if [ -n "$override" ]; then
+ mkdir -p "/etc/systemd/system/$name.service.d"
+ # shellcheck disable=SC2059
+ printf "$override" > "/etc/systemd/system/$name.service.d/override.conf"
+ fi
+
+ systemctl daemon-reload
+ systemctl enable "$name"
+ systemctl start "$name"
+ wait_for_service "$name"
+}
+
+_stop_unit() {
+ local unit=$1
+
+ if systemctl is-active "$unit"; then
+ retries=20
+ while systemctl status "$unit" | grep -q "Active: activating"; do
+ if [ $retries -eq 0 ]; then
+ echo "tests.systemd: unit $unit could not be stopped"
+ systemctl status "$unit"
+ exit 1
+ fi
+ retries=$(( retries - 1 ))
+ sleep 1
+ done
+
+ systemctl stop "$unit"
+ fi
+}
+
+stop_unit() {
+ local remove=false
+ while [ $# -gt 0 ]; do
+ case "$1" in
+ --remove)
+ remove=true
+ shift
+ ;;
+ *)
+ break
+ ;;
+ esac
+ done
+
+ if [ $# -eq 0 ]; then
+ echo "tests.systemd: at least a unit name is required"
+ return 1
+ fi
+
+ for unit in "$@"; do
+ _stop_unit "$unit"
+ if [ "$remove" = true ]; then
+ if systemctl is-enabled "$unit"; then
+ systemctl disable "$unit"
+ fi
+ rm -f "/etc/systemd/system/$unit.service"
+ rm -rf "/etc/systemd/system/$unit.service.d"
+ fi
+ done
+
+ systemctl daemon-reload
+}
+
+wait_for_service() {
+ if [ $# -eq 0 ]; then
+ show_help
+ exit 0
+ fi
+
+ local attempts=300
+ local wait=1
+ local state="active"
+ local service_name
+
+ while [ $# -gt 0 ]; do
+ case "$1" in
+ --wait)
+ wait="$2"
+ shift 2
+ ;;
+ -n|--attempts)
+ attempts="$2"
+ shift 2
+ ;;
+ --state)
+ state="$2"
+ shift 2
+ ;;
+ *)
+ service_name=$1
+ break
+ ;;
+ esac
+ done
+
+ if [ -z "$service_name" ]; then
+ echo "tests.systemd: unit name cannot be empty"
+ return 1
+ fi
+
+ for i in $(seq "$attempts"); do
+ if systemctl show -p ActiveState "$service_name" | grep -q "ActiveState=$state"; then
+ return
+ fi
+ # show debug output every 1min
+ if [ "$i" -gt 0 ] && [ $(( i % 60 )) = 0 ]; then
+ systemctl status "$service_name" || true
+ fi
+ sleep "$wait"
+ done
+
+ echo "tests.systemd: service $service_name did not become $state"
+ return 1
+}
+
+main() {
+ if [ $# -eq 0 ]; then
+ show_help
+ exit 0
+ fi
+
+ while [ $# -gt 0 ]; do
+ case "$1" in
+ -h|--help)
+ show_help
+ exit
+ ;;
+ *)
+ action=$(echo "$1" | tr '-' '_')
+ shift
+ break
+ ;;
+ esac
+ done
+
+ if [ -z "$(declare -f "$action")" ]; then
+ echo "tests.systemd: no such command: $action" >&2
+ show_help
+ exit 1
+ fi
+
+ "$action" "$@"
+}
+
+main "$@"
diff --git a/tests/lib/external/snapd-testing-tools/utils/check-test-format b/tests/lib/external/snapd-testing-tools/utils/check-test-format
new file mode 100755
index 0000000..25c8521
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/utils/check-test-format
@@ -0,0 +1,150 @@
+#!/usr/bin/python3
+
+"""
+This tool is used to verify a correct format of spread tests
+The input is a directory which is scanned recursively and all
+the task.yaml files are check
+"""
+
+import argparse
+import glob
+import os
+import sys
+import yaml
+import yamlordereddictloader
+
+SUPPORTED_KEYS = [
+ "summary",
+ "details",
+ "backends",
+ "systems",
+ "manual",
+ "priority",
+ "warn-timeout",
+ "kill-timeout",
+ "environment",
+ "artifacts",
+ "prepare",
+ "restore",
+ "debug",
+ "execute",
+]
+MANDATORY_KEYS = ["summary", "details", "execute"]
+
+
+def check_mandatory_keys(task_keys):
+ findings = []
+ for key in MANDATORY_KEYS:
+ if key not in task_keys:
+ findings.append("Key '{}' is mandatory".format(key))
+
+ return findings
+
+
+def check_keys_order(task_keys):
+ last_index = -1
+ last_key = ""
+ findings = []
+
+ for curr_key in task_keys:
+ try:
+ curr_index = SUPPORTED_KEYS.index(curr_key)
+ if curr_index <= last_index:
+ findings.append(
+ "Keys '{}' and '{}' do not follow the desired order: {}".format(
+ last_key, curr_key, SUPPORTED_KEYS
+ )
+ )
+
+ last_index = curr_index
+ last_key = curr_key
+
+ except ValueError:
+ findings.append(
+ "key '{}' is not among the supported keys: {}".format(
+ curr_key, SUPPORTED_KEYS
+ )
+ )
+
+ return findings
+
+
+def check_task_format(filepath):
+ if not os.path.isfile(filepath):
+ print("Format checks failed for task {}".format(filepath))
+ print(" - The path is not a file")
+ return False
+
+ filemap = dict()
+ try:
+ with open(filepath, "r") as task:
+ filemap = yaml.load(task, Loader=yamlordereddictloader.Loader)
+ except yaml.scanner.ScannerError:
+ print("Invalid task format, checks failed for task {}".format(filepath))
+ return False
+
+ findings = check_keys_order(filemap.keys())
+ findings.extend(check_mandatory_keys(filemap.keys()))
+ if findings:
+ print("Format checks failed for task {}".format(filepath))
+ for finding in findings:
+ print(" - " + finding)
+ return False
+
+ return True
+
+def check_dir(directory):
+ if not os.path.isdir(directory):
+ print("Format checks failed for directory {}".format(directory))
+ print(" - The path is not a directory")
+ return False
+
+ status = True
+ for file in glob.glob(os.path.join(directory, "**/task.yaml"), recursive=True):
+ if not check_task_format(file):
+ status = False
+
+ return status
+
+def check_tests(tests):
+ status = True
+ for test in tests:
+ if not os.path.isfile(test):
+ print("Format checks failed for test {}".format(test))
+ print(" - The path is not a file")
+ status = False
+ continue
+
+ if not check_task_format(test):
+ status = False
+
+ return status
+
+def _make_parser():
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ "--dir", help="path to the directory to check recursively"
+ )
+ parser.add_argument(
+ "--tests", help="list of tests path to check", nargs='+'
+ )
+ return parser
+
+
+def main():
+ parser = _make_parser()
+ args = parser.parse_args()
+
+ status = 0
+ if args.tests:
+ if not check_tests(args.tests):
+ status = 1
+
+ if args.dir:
+ if not check_dir(args.dir):
+ status = 1
+
+ sys.exit(status)
+
+if __name__ == "__main__":
+ main()
diff --git a/tests/lib/external/snapd-testing-tools/utils/log-analyzer b/tests/lib/external/snapd-testing-tools/utils/log-analyzer
new file mode 100755
index 0000000..79ef43a
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/utils/log-analyzer
@@ -0,0 +1,330 @@
+#!/bin/bash
+
+show_help() {
+ echo "usage: log-analyzer list-failed-tasks "
+ echo " log-analyzer list-executed-tasks "
+ echo " log-analyzer list-successful-tasks "
+ echo " log-analyzer list-aborted-tasks "
+ echo " log-analyzer list-all-tasks "
+ echo " log-analyzer list-reexecute-tasks "
+ echo ""
+ echo "The log analyzer is an utility wchi provides useful information about a spread"
+ echo "execution. The main functionality of the analyzer utility is to determine which tests"
+ echo "have to be re-executed, being able to include the tests that have been aborted, even"
+ echo "when those tests are not included in the test results."
+ echo "The log analyzer uses as input the spread expression that was used to run the tests,"
+ echo "this expression determines which are all the tests to be considered. The second input"
+ echo "is the output of the log-parser utility, which generates a json file including all the"
+ echo "information extracted from the raw spread log"
+ echo ""
+ echo "Available options:"
+ echo " -h --help show this help message."
+ echo ""
+ echo "COMMANDS:"
+ echo " list-failed-tasks list the tasks that failed during execute"
+ echo " list-executed-tasks list the tasks that were executed"
+ echo " list-successful-tasks list the successful tasks"
+ echo " list-aborted-tasks list the aborted tasks (needs spread to be installed)"
+ echo " list-all-tasks list all the tasks"
+ echo " list-reexecute-tasks list the tasks to re-execute to complete (includes aborted and failed tasks)"
+ echo ""
+ echo "PARSED-LOG: This is the output generated by the log-parser tool"
+ echo "EXEC-PARAM: this is the parameter used to run spread (something like this BACKEND:SYSTEM:SUITE)"
+ echo ""
+}
+
+_check_log() {
+ local log="$1"
+
+ if [ -z "$log" ]; then
+ echo "log.analyzer: the log file cannot be empty"
+ exit 1
+ elif [ ! -f "$log" ]; then
+ echo "log.analyzer: the log file $log does not exist"
+ exit 1
+ fi
+}
+
+_list_failed() {
+ local level="$1"
+ local stage="$2"
+ local log="$3"
+
+ if [ -z "$level" ]; then
+ echo "log.analyzer: the first parameter cannot be empty"
+ exit 1
+ elif [ ! "$level" = 'task' ] && [ ! "$level" = 'suite' ] && [ ! "$level" = 'project' ]; then
+ echo "log.analyzer: the first parameter has to be: task, suite or project"
+ exit 1
+ fi
+
+ if [ -z "$stage" ]; then
+ echo "log.analyzer: the second parameter cannot be empty"
+ exit 1
+ elif [ ! "$stage" = 'prepare' ] && [ ! "$stage" = 'restore' ]; then
+ echo "log.analyzer: the second parameter has to be: prepare or restore"
+ exit 1
+ fi
+ _check_log "$log"
+
+ jq -r ".[] | select( .type == \"result\") | select( .result_type == \"Failed\") | select(.level == \"$level\") | select(.stage == \"$stage\") | .detail.lines[]" "$log" | cut -d '-' -f2- | xargs
+}
+
+_merge_tasks_lists() {
+ # Returns the list1 + the tasks in list2 which are not included in list1
+ local list1="$1"
+ local list2="$2"
+ local merged_list="$1"
+ local list1_file
+
+ list1_file=$(mktemp list1.XXXXXX)
+ for elem in $list1; do
+ echo "$elem" >> "$list1_file"
+ done
+
+ # shellcheck disable=SC2086
+ for elem2 in $list2; do
+ if ! grep -Fxq "$elem2" "$list1_file"; then
+ merged_list="$merged_list $elem2"
+ fi
+ done
+ rm "$list1_file"
+ echo "$merged_list"
+}
+
+_diff_tasks_lists() {
+ # Returns the list1 - the tasks in list2
+ local list1="$1"
+ local list2="$2"
+ local diff_list list2_file
+
+ diff_list=""
+ list2_file=$(mktemp list2.XXXXXX)
+ for elem in $list2; do
+ echo "$elem" >> "$list2_file"
+ done
+
+ # shellcheck disable=SC2086
+ for elem1 in $list1; do
+ if ! grep -Fxq "$elem1" "$list2_file"; then
+ diff_list="$diff_list $elem1"
+ fi
+ done
+ rm "$list2_file"
+ echo "$diff_list"
+}
+
+_intersection_tasks_lists() {
+ # Returns the tasks in list1 which are also in the list2
+ local list1="$1"
+ local list2="$2"
+ local both_list list2_file
+
+ both_list=""
+ list2_file=$(mktemp list2.XXXXXX)
+ for elem in $list2; do
+ echo "$elem" >> "$list2_file"
+ done
+ for elem in $list1; do
+ # -F tells grep to look for fixed strings, not regexps
+ if grep -Fxq "$elem" "$list2_file"; then
+ both_list="$both_list $elem"
+ fi
+ done
+ rm "$list2_file"
+ echo "$both_list"
+}
+
+list_all_tasks() {
+ local exec_exp="$1"
+ exec_exp="$(echo "$exec_exp" | tr ',' ' ')"
+ if ! command -v spread >/dev/null; then
+ echo "log.analyzer: spread tool is not installed, exiting..."
+ exit 1
+ fi
+
+ # shellcheck disable=SC2086
+ spread -list $exec_exp
+}
+
+_list_executed_and_failed_tasks() {
+ local exec_exp="$1"
+ local log="$2"
+
+ if ! command -v spread >/dev/null; then
+ echo "log.analyzer: spread tool is not installed, exiting..."
+ exit 1
+ fi
+ _check_log "$log"
+
+ local failed_tasks failed_tasks_restore failed_tasks_prepare exec_and_failed_tasks
+ failed_tasks="$(list_failed_tasks "$exec_exp" "$log")"
+ failed_tasks_prepare="$(_list_failed task prepare "$log")"
+ failed_tasks_restore="$(_list_failed task restore "$log")"
+
+ exec_and_failed_tasks="$(_merge_tasks_lists "$failed_tasks" "$failed_tasks_restore")"
+ _diff_tasks_lists "$exec_and_failed_tasks" "$failed_tasks_prepare"
+}
+
+list_failed_tasks() {
+ local exec_exp="$1"
+ local log="$2"
+
+ if [ -z "$exec_exp" ]; then
+ echo "log.analyzer: execution expression for spread cannot be empty"
+ exit 1
+ fi
+ exec_exp="$(echo "$exec_exp" | tr ',' ' ')"
+ _check_log "$log"
+
+ local all_tasks failed_tasks
+ all_tasks="$(list_all_tasks "$exec_exp")"
+ failed_tasks="$(jq -r '.[] | select( .type == "result") | select( .result_type == "Failed") | select(.level == "tasks") | .detail.lines[]' "$log" | cut -d '-' -f2- | xargs)"
+ _intersection_tasks_lists "$failed_tasks" "$all_tasks"
+}
+
+list_reexecute_tasks() {
+ local exec_exp="$1"
+ local log="$2"
+
+ if [ -z "$exec_exp" ]; then
+ echo "log.analyzer: execution expression for spread cannot be empty"
+ exit 1
+ fi
+ exec_exp="$(echo "$exec_exp" | tr ',' ' ')"
+ _check_log "$log"
+
+ local aborted_tasks exec_and_failed_tasks all_tasks reexec_tasks
+ aborted_tasks="$(list_aborted_tasks "$exec_exp" "$log")"
+ all_tasks="$(list_all_tasks "$exec_exp")"
+ exec_and_failed_tasks="$(_list_executed_and_failed_tasks "$exec_exp" "$log")"
+
+ # Remove the tasks which are not in the filter from the executed and failed
+ exec_and_failed_tasks="$(_intersection_tasks_lists "$exec_and_failed_tasks" "$all_tasks")"
+ reexec_tasks="$(_merge_tasks_lists "$aborted_tasks" "$exec_and_failed_tasks")"
+
+ # In case all the tests are failed or aborted, then the execution expression is used to reexecute
+ if [ "$(echo "$reexec_tasks" | wc -w)" = "$(echo "$all_tasks" | wc -w)" ]; then
+ echo "$exec_exp"
+ return
+ fi
+
+ # When all the tests were successful, then no tests need to be reexecuted
+ if [ "$(echo "$reexec_tasks" | wc -w)" = 0 ]; then
+ return
+ fi
+ echo "$reexec_tasks"
+}
+
+list_successful_tasks() {
+ local exec_exp="$1"
+ local log="$2"
+
+ if [ -z "$exec_exp" ]; then
+ echo "log.analyzer: execution expression for spread cannot be empty"
+ exit 1
+ fi
+ exec_exp="$(echo "$exec_exp" | tr ',' ' ')"
+ _check_log "$log"
+
+ local all_tasks executed_tasks failed_tasks_restore failed_tasks
+ all_tasks="$(list_all_tasks "$exec_exp")"
+ executed_tasks="$(list_executed_tasks "$exec_exp" "$log")"
+ executed_tasks="$(_intersection_tasks_lists "$executed_tasks" "$all_tasks")"
+ failed_tasks="$(list_failed_tasks "$exec_exp" "$log")"
+ failed_tasks_restore="$(_list_failed task restore "$log")"
+
+ if [ -n "$failed_tasks_restore" ]; then
+ failed_tasks="$(_merge_tasks_lists "$failed_tasks" "$failed_tasks_restore")"
+ fi
+
+ if [ -n "$failed_tasks" ]; then
+ executed_tasks="$(_diff_tasks_lists "$executed_tasks" "$failed_tasks")"
+ fi
+
+ echo "$executed_tasks"
+}
+
+list_executed_tasks() {
+ local exec_exp="$1"
+ local log="$2"
+
+ if [ -z "$exec_exp" ]; then
+ echo "log.analyzer: execution expression for spread cannot be empty"
+ exit 1
+ fi
+ exec_exp="$(echo "$exec_exp" | tr ',' ' ')"
+ _check_log "$log"
+
+ local all_tasks executed_tasks
+ all_tasks="$(list_all_tasks "$exec_exp")"
+ executed_tasks="$(jq -r '.[] | select( .type == "phase") | select( .verb == "Executing") | .task' "$log")"
+ _intersection_tasks_lists "$executed_tasks" "$all_tasks"
+}
+
+list_aborted_tasks() {
+ local exec_exp="$1"
+ local log="$2"
+
+ if [ -z "$exec_exp" ]; then
+ echo "log.analyzer: execution expression for spread cannot be empty"
+ exit 1
+ fi
+ exec_exp="$(echo "$exec_exp" | tr ',' ' ')"
+ _check_log "$log"
+
+ local all_tasks executed_tasks failed_tasks_prepare failed_tasks_restore failed_tasks
+ all_tasks="$(list_all_tasks "$exec_exp")"
+ executed_tasks="$(list_executed_tasks "$exec_exp" "$log")"
+ failed_tasks="$(list_failed_tasks "$exec_exp" "$log")"
+ failed_tasks_prepare="$(_list_failed task prepare "$log")"
+ failed_tasks_restore="$(_list_failed task restore "$log")"
+
+ # In case no tasks for the expression, the aborted list is empty
+ if [ -z "$all_tasks" ]; then
+ return
+ fi
+
+ # In case no tasks are successfully executed, all the tasks - the failed ones are the aborted
+ if [ -z "$executed_tasks" ]; then
+ exec_and_failed_tasks="$(_list_executed_and_failed_tasks "$exec_exp" "$log")"
+ _diff_tasks_lists "$all_tasks" "$exec_and_failed_tasks"
+ return
+ fi
+
+ # In other cases the aborted tasks are all the tasks - the executed - the that failed
+ _diff_tasks_lists "$all_tasks" "$executed_tasks"
+}
+
+main() {
+ if [ $# -eq 0 ]; then
+ show_help
+ exit 0
+ fi
+
+ local subcommand="$1"
+ local action=
+ while [ $# -gt 0 ]; do
+ case "$1" in
+ -h|--help)
+ show_help
+ exit 0
+ ;;
+ *)
+ action=$(echo "$subcommand" | tr '-' '_')
+ shift
+ break
+ ;;
+ esac
+ done
+
+ if [ -z "$(declare -f "$action")" ]; then
+ echo "log.analyzer: no such command: $subcommand" >&2
+ show_help
+ exit 1
+ fi
+
+ "$action" "$@"
+}
+
+main "$@"
diff --git a/tests/lib/external/snapd-testing-tools/utils/log-filter b/tests/lib/external/snapd-testing-tools/utils/log-filter
new file mode 100755
index 0000000..91fafca
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/utils/log-filter
@@ -0,0 +1,208 @@
+#!/usr/bin/env python3
+
+import argparse
+import io
+import re
+import sys
+import typing
+
+import log_helper as helper
+
+# Rules are applied to filter content in the following operation details:
+# ERROR: The error shoes the bash output when a task fails
+# DEBUG: The debug includes the output of the debug script for task/suite errors
+# WARNING: The last lines from the task execution output
+# FAILED: Include a list of failed tests in the results section
+SUPPORTED_RULES = [helper.Result.FAILED.value] + helper.ExecutionInfo.list()
+
+
+def print_line(line: str) -> None:
+ if not line:
+ print()
+ else:
+ print(line.strip())
+
+
+def write_line(line: str, to_stream: typing.TextIO) -> None:
+ if not line:
+ print("\n", file=to_stream)
+ else:
+ print(line.strip(), file=to_stream, end="\n")
+
+
+def compile_rules(rules: list[str], operation: str) -> list[re.Pattern[str]]:
+ patterns = []
+ regex_list = []
+
+ for rule in rules:
+ parts = rule.split("=", 1)
+ if len(parts) != 2:
+ raise ValueError(
+ "Error: Rule '{}' does not follow the OPERATION=PATTERN format".format(
+ rule
+ )
+ )
+
+ rule_operation = parts[0]
+ rule_pattern = parts[1]
+
+ if rule_operation not in SUPPORTED_RULES:
+ raise ValueError(
+ "Error: Rule operation '{}' not in supported list: {}".format(
+ rule_operation, SUPPORTED_RULES
+ )
+ )
+
+ if operation == rule_operation:
+ patterns.append(rule_pattern)
+
+ for pattern in patterns:
+ regex_list.append(re.compile(pattern))
+ return regex_list
+
+
+def process_detail_line(
+ line: str, regex_list: list[re.Pattern[str]], to_stream: typing.TextIO
+) -> None:
+ if not regex_list:
+ write_line(line, to_stream)
+ else:
+ for regex in regex_list:
+ matches = regex.findall(line)
+ for match in matches:
+ write_line(match, to_stream)
+
+
+def process_detail(
+ from_stream: typing.TextIO,
+ start_line: str,
+ regex_list: list[re.Pattern[str]],
+ to_stream: typing.TextIO,
+) -> str:
+ """Process lines from the start of the details section until the last line,
+ returns the first line right after the details section
+ """
+ write_line(start_line, to_stream)
+ for line in sys.stdin:
+ print_line(line)
+
+ if not line:
+ continue
+
+ # Check if the detail is finished
+ if helper.is_detail_finished(line):
+ return line
+
+ # Print all the lines
+ process_detail_line(line, regex_list, to_stream)
+
+ return ""
+
+
+def skip_detail(from_stream: typing.TextIO) -> str:
+ """Skip lines from the start of the details section until the last line,
+ returns the first line right after the details section
+ """
+ for line in sys.stdin:
+ print_line(line)
+ if not line:
+ continue
+
+ # Check if the detail is finished
+ if helper.is_detail_finished(line):
+ return line
+
+ return ""
+
+
+def process_spread_output(
+ output_file: str, exclude_lines: set[str], filter_rules: list[str]
+) -> None:
+ error_regex = compile_rules(filter_rules, helper.ExecutionInfo.ERROR.value)
+ debug_regex = compile_rules(filter_rules, helper.ExecutionInfo.DEBUG.value)
+ failed_regex = compile_rules(filter_rules, helper.Result.FAILED.value)
+ warning_regex = compile_rules(filter_rules, helper.ExecutionInfo.WARNING.value)
+
+ with open(output_file, "w") as myfile:
+ for line in sys.stdin:
+ print_line(line)
+
+ while helper.is_detail_start(line):
+ regex_list = []
+ if helper.is_operation(line, helper.ExecutionInfo.DEBUG):
+ if helper.ExecutionInfo.DEBUG.value in exclude_lines:
+ line = skip_detail(sys.stdin)
+ continue
+ else:
+ regex_list = debug_regex
+
+ if helper.is_operation(line, helper.ExecutionInfo.ERROR):
+ if helper.ExecutionInfo.ERROR.value in exclude_lines:
+ line = skip_detail(sys.stdin)
+ continue
+ else:
+ regex_list = error_regex
+
+ if helper.is_operation(line, helper.ExecutionInfo.WARNING):
+ if helper.ExecutionInfo.WARNING.value in exclude_lines:
+ line = skip_detail(sys.stdin)
+ continue
+ else:
+ regex_list = warning_regex
+
+ if helper.is_operation(line, helper.Result.FAILED):
+ if helper.Result.FAILED.value in exclude_lines:
+ line = skip_detail(sys.stdin)
+ continue
+ else:
+ regex_list = failed_regex
+
+ line = process_detail(sys.stdin, line, regex_list, myfile)
+
+ if (
+ not exclude_lines
+ or not helper.is_any_operation(line)
+ or not helper.get_operation(line) in exclude_lines
+ ):
+ write_line(line, myfile)
+
+
+def _make_parser() -> argparse.ArgumentParser:
+ parser = argparse.ArgumentParser(
+ description="""
+This tool is used to save a filtered version of the spread output. It parses the spread output and filters
+sections and debug/error details to the file passed as parameter (all the lines are printed).
+When a output file is not provided the debug output is sent to spread_filtered.log
+"""
+ )
+ parser.add_argument(
+ "-o",
+ "--output-file",
+ metavar="PATH",
+ default="spread.filtered.log",
+ help="path to the filtered output file",
+ )
+ parser.add_argument(
+ "-e",
+ "--exclude",
+ action="append",
+ default=[],
+ choices=helper.OPERATIONS,
+ help="A line section to exclude from the output",
+ )
+ parser.add_argument(
+ "-f",
+ "--filter",
+ action="append",
+ metavar="OPERATION=PATTERN",
+ default=[],
+ help="It is used to extract specific data from errors. Allowed operations are Error, Debug, Failed and WARNING:",
+ )
+
+ return parser
+
+
+if __name__ == "__main__":
+ parser = _make_parser()
+ args = parser.parse_args()
+ process_spread_output(args.output_file, args.exclude, args.filter)
diff --git a/tests/lib/external/snapd-testing-tools/utils/log-parser b/tests/lib/external/snapd-testing-tools/utils/log-parser
new file mode 100755
index 0000000..c4494f5
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/utils/log-parser
@@ -0,0 +1,525 @@
+#!/usr/bin/env python3
+
+"""
+This tool reads a spread log and creates a file with all the data
+The output file includes the more important information extracted
+from the log to be analyzed
+"""
+
+import argparse
+import json
+import os
+import re
+import sys
+
+import log_helper as helper
+
+
+class Phase:
+ """
+ Phase represents the task/suite/project action in terms of:
+ Preparing, Executing and Restoring
+ """
+
+ def __init__(self, verb, task, date, time, source_line):
+ self.type = "phase"
+ self.verb = verb
+ self.time = time
+ self.date = date
+ self.task = task
+ self.source_line = source_line
+
+ def __repr__(self):
+ return self.source_line
+
+ def to_dict(self):
+ return {
+ "type": self.type,
+ "date": self.date,
+ "time": self.time,
+ "verb": self.verb,
+ "task": self.task,
+ }
+
+
+class Result:
+ """
+ Result represents the results for a spread run
+ The results can be: Successful, failed and aborted
+ """
+
+ def __init__(
+ self, result_type, level, stage, number, date, time, detail, source_line
+ ):
+ self.type = "result"
+ self.result_type = result_type
+ self.level = level
+ self.stage = stage
+ self.number = number
+ self.time = time
+ self.date = date
+ self.detail = detail
+ self.source_line = source_line
+
+ def __repr__(self):
+ if self.detail:
+ return "{}{}".format(self.source_line, str(self.detail))
+ return self.source_line
+
+ def to_dict(self):
+ prepared_detail = None
+ if self.detail:
+ prepared_detail = self.detail.to_dict()
+ return {
+ "type": self.type,
+ "date": self.date,
+ "time": self.time,
+ "result_type": self.result_type,
+ "level": self.level,
+ "stage": self.stage,
+ "number": self.number,
+ "detail": prepared_detail,
+ }
+
+
+class Info:
+ """
+ Info represents the tasks status information which is included
+ in the spread log. The info can be: Error, Debug and Warning.
+ """
+
+ def __init__(self, info_type, verb, task, extra, date, time, detail, source_line):
+ self.type = "info"
+ self.info_type = info_type
+ self.verb = verb
+ self.time = time
+ self.date = date
+ self.task = task
+ self.extra = extra
+ self.detail = detail
+ self.source_line = source_line
+
+ def __repr__(self):
+ if self.detail:
+ return "{}{}".format(self.source_line, self.detail)
+ return self.source_line
+
+ def to_dict(self):
+ prepared_detail = None
+ if self.detail:
+ prepared_detail = self.detail.to_dict()
+ return {
+ "type": self.type,
+ "date": self.date,
+ "time": self.time,
+ "info_type": self.info_type,
+ "verb": self.verb,
+ "task": self.task,
+ "extra": self.extra,
+ "detail": prepared_detail,
+ }
+
+
+class Rule:
+ """
+ Rule represent the KEY=PATTERN used to extract information from a set of lines
+ """
+
+ def __init__(self, rule):
+ parts = rule.split("=", 1)
+ if len(parts) != 2:
+ raise ValueError(
+ "Error: Rule '{}' does not follow the KEY=PATTERN format".format(rule)
+ )
+
+ self.key = parts[0]
+ self.pattern = parts[1]
+
+ try:
+ re.compile(self.pattern)
+ except re.error as err:
+ raise ValueError(
+ "Error: pattern '{}' cannot be compiled: {}".format(self.pattern, err)
+ )
+
+ def filter(self, lines) -> list[str]:
+ regex = re.compile(self.pattern)
+ all_matches = []
+ for line in lines:
+ matches = regex.findall(line)
+ for match in matches:
+ if match:
+ all_matches.append(match)
+
+ return all_matches
+
+
+class Detail:
+ """
+ Detail represents the extra lines which are displayed after the info
+ """
+
+ def __init__(self, lines_limit: int, lines: list[str], rules: list[str]) -> None:
+ self.lines_limit = lines_limit
+ self.lines = lines
+ self.data = dict[str, list[str]]()
+ self._process_rules(rules)
+
+ def _get_lines(self) -> list[str]:
+ if self.lines_limit < 0 or self.lines_limit > len(self.lines):
+ return self.lines
+
+ # Use self.lines_limit-1 because the last line is a '.' and we don't
+ # want to count it as a line in the log details
+ return self.lines[-self.lines_limit - 1 :]
+
+ def _process_rules(self, rules) -> None:
+ for rule in rules:
+ key = rule.key
+ matches = rule.filter(self.lines)
+ self.data[key] = matches
+
+ def __repr__(self):
+ return "".join(self._get_lines())
+
+ def to_dict(self) -> dict[str, list[str]]:
+ details_dict = {"lines": self.lines[-self.lines_limit - 1 :]}
+ for key in self.data.keys():
+ details_dict[key] = self.data[key]
+
+ return details_dict
+
+
+class Action:
+ """
+ The actions are general operations that the spread can do while
+ executing tests like: Rebooting, Discarding, Allocating, Waiting,
+ Allocated, Connecting, Connected, Sending
+ """
+
+ def __init__(
+ self, verb: str, task: str, extra: str, date: str, time: str, source_line: str
+ ) -> None:
+ self.type = "action"
+ self.verb = verb
+ self.time = time
+ self.extra = extra
+ self.date = date
+ self.task = task
+ self.source_line = source_line
+
+ def __repr__(self):
+ return self.source_line
+
+ def to_dict(self) -> dict[str, str]:
+ return {
+ "type": self.type,
+ "date": self.date,
+ "time": self.time,
+ "verb": self.verb,
+ "task": self.task,
+ "extra": self.extra,
+ }
+
+
+class LogReader:
+ """
+ LogReader manages the spread log, it allows to read, export and print
+ """
+
+ def __init__(
+ self,
+ filepath: str,
+ output_type: str,
+ lines_limit: str,
+ error_rules: str,
+ debug_rules: str,
+ ) -> None:
+ self.filepath = filepath
+ self.output_type = output_type
+ self.lines_limit = lines_limit
+ self.lines = list[str]()
+ self.iter = 0
+ self.full_log = list[str]()
+ self.error_rules = [Rule(rule) for rule in error_rules]
+ self.debug_rules = [Rule(rule) for rule in debug_rules]
+
+ def __repr__(self):
+ return str(self.to_dict())
+
+ def to_dict(self) -> dict:
+ return {"full_log": self.full_log}
+
+ def print_log(self) -> None:
+ if not self.full_log:
+ return
+
+ print("".join(str(x) for x in self.full_log))
+
+ def export_log(self, filepath: str) -> None:
+ prepared_log = []
+ for item in self.full_log:
+ if isinstance(item, str):
+ prepared_log.append(item)
+ else:
+ prepared_log.append(item.to_dict())
+ with open(filepath, "w") as json_file:
+ json.dump(prepared_log, json_file, indent=4)
+
+ def _next_line(self) -> str:
+ self.iter = self.iter + 1
+ return self.lines[self.iter - 1]
+
+ def check_log_exists(self) -> bool:
+ return os.path.exists(self.filepath)
+
+ def read_spread_log(self) -> None:
+ try:
+ with open(self.filepath, "r", encoding="utf-8") as filepath:
+ self.lines = filepath.readlines()
+ except UnicodeDecodeError:
+ with open(self.filepath, "r", encoding="latin-1") as filepath:
+ self.lines = filepath.readlines()
+
+ self.iter = 0
+
+ # Then iterate line by line analyzing the log
+ while self.iter < len(self.lines):
+ line = self._next_line()
+
+ if not helper.is_any_operation(line):
+ continue
+
+ # The line is a task execution; preparing, executing, restoring
+ if self._match_phase(line):
+ phase = self._get_phase(line)
+ if phase:
+ self.full_log.append(phase)
+ continue
+
+ # The line shows info: error, debug, warning
+ if self._match_info(line):
+ info = self._get_info(line)
+ if info:
+ self.full_log.append(info)
+ continue
+
+ # The line is another operation: Rebooting, Discarding, Allocating
+ # Waiting, Allocated, Connecting, Connected, Sending'
+ if self._match_action(line):
+ action = self._get_action(line)
+ if action:
+ self.full_log.append(action)
+ continue
+
+ # The line is a result: Successful, Aborted, Failed
+ if self._match_result(line):
+ result = self._get_result(line)
+ if result:
+ self.full_log.append(result)
+ continue
+
+ def _match_info(self, line: str) -> bool:
+ return helper.get_operation(line) in helper.ExecutionInfo.list()
+
+ def _match_phase(self, line: str) -> bool:
+ return helper.get_operation(line) in helper.ExecutionPhase.list()
+
+ def _match_action(self, line: str) -> bool:
+ return (
+ helper.get_operation(line)
+ in helper.GeneralAction.list() + helper.GeneralActionStatus.list()
+ )
+
+ def _match_result(self, line: str) -> bool:
+ return helper.get_operation(line) in helper.Result.list()
+
+ def _get_detail(self, rules, results=False, other_limit=None):
+ """
+ This function is used to get the piece of log which is after the
+ info lines (error, debug, warning). The detail could also include
+ a limit of lines to tail the log and show the last lines.
+ It returns a Detail object included all the lines.
+ """
+ detail = []
+ while self.iter < len(self.lines):
+ previous_line = self.lines[self.iter - 1]
+ line = self._next_line()
+ if helper.is_detail_finished(line):
+ # This is needed because it could happen that in the details there is a spread log
+ # because sometimes we run nested spread
+ # The is not valid when the details are for failed tests in results section
+ if results or previous_line.strip() == ".":
+ break
+
+ detail.append(line)
+
+ # We leave the iter in the last line in case the log has finished
+ if not self.iter == len(self.lines):
+ self.iter = self.iter - 1
+ if not other_limit:
+ other_limit = self.lines_limit
+
+ return Detail(other_limit, detail, rules)
+
+ def _get_info(self, line):
+ """
+ Get the Info object for the error, debug and warning lines including
+ the details for this
+ """
+ date = helper.get_date(line)
+ time = helper.get_time(line)
+ info_type = helper.get_operation(line)
+ info_extra = helper.get_operation_info(line)
+
+ verb = None
+ task = None
+ if info_type == helper.ExecutionInfo.WARNING.value:
+ # Removing the : from WARNING:
+ info_type = info_type.split(":")[0]
+ verb = None
+ task = None
+ extra = info_extra
+ elif info_type == helper.ExecutionInfo.ERROR.value:
+ verb = info_extra.split(" ")[0]
+ task = info_extra.split(" ")[1]
+ extra = None
+ elif info_type == helper.ExecutionInfo.DEBUG.value:
+ verb = None
+ task = info_extra.split(" ")[2]
+ extra = None
+ else:
+ print("log-parser: detail type not recognized: {}".format(info_type))
+ return
+
+ # Pass the rules according to the info type
+ rules = self.debug_rules
+ if info_type == helper.ExecutionInfo.ERROR.value:
+ rules = self.error_rules
+
+ detail = None
+ if helper.is_detail(line):
+ detail = self._get_detail(rules)
+
+ return Info(info_type, verb, task, extra, date, time, detail, line)
+
+ def _get_result(self, line):
+ """Get the Result object including the details for the result"""
+ date = helper.get_date(line)
+ time = helper.get_time(line)
+ result_type = helper.get_operation(line)
+ result_extra = helper.get_operation_info(line)
+ level = result_extra.split(" ")[0].split(":")[0]
+ number = result_extra.split(" ")[-1]
+
+ stage = None
+ detail = None
+ if result_type == helper.Result.FAILED.value:
+ if level in helper.ExecutionLevel.list():
+ stage = result_extra.split(" ")[1].split(":")[0]
+ detail = self._get_detail([], results=True, other_limit=-1)
+
+ return Result(result_type, level, stage, number, date, time, detail, line)
+
+ def _get_phase(self, line):
+ """
+ Get the phase object for lines preparing, executing and restoring
+ """
+ date = helper.get_date(line)
+ time = helper.get_time(line)
+ verb = helper.get_operation(line)
+ task = helper.get_operation_info(line).split(" ")[0]
+ return Phase(verb, task.split("...")[0], date, time, line)
+
+ def _get_action(self, line):
+ """Get the general actions object for lines rebooting, allocating, etc"""
+
+ date = helper.get_date(line)
+ time = helper.get_time(line)
+ verb = helper.get_operation(line)
+ task = None
+ extra = helper.get_operation_info(line)
+ return Action(verb, task, extra, date, time, line)
+
+
+def _make_parser():
+ # type: () -> argparse.ArgumentParser
+ parser = argparse.ArgumentParser(
+ description="""
+Parse the spread log and generates a file with a standardized output. It also
+allows to filter the output by type and define the number of lines to show
+for the error/debug/warning output.
+"""
+ )
+ parser.add_argument(
+ "-c",
+ "--cut",
+ type=int,
+ default=1000,
+ help="maximum number of lines for logs on errors and debug sections",
+ )
+ parser.add_argument(
+ "-f",
+ "--format",
+ type=str,
+ default="json",
+ choices=["json"],
+ help="format for the output",
+ )
+ parser.add_argument(
+ "-o",
+ "--output",
+ default="spread-results.json",
+ type=str,
+ help="output file to save the result",
+ )
+ parser.add_argument(
+ "-er",
+ "--error-rule",
+ action="append",
+ default=[],
+ help="A KEY=PATTERN used to extract and store specific data from errors",
+ )
+ parser.add_argument(
+ "-dr",
+ "--debug-rule",
+ action="append",
+ default=[],
+ help="A KEY=PATTERN used to extract and store specific data from debug output",
+ )
+ parser.add_argument(
+ "log_path", metavar="PATH", help="path to the log to be analyzed"
+ )
+ return parser
+
+
+def main():
+ # type: () -> None
+ parser = _make_parser()
+ args = parser.parse_args()
+
+ if len(args.log_path) == 0:
+ parser.print_usage()
+ parser.exit(0)
+
+ reader = LogReader(
+ args.log_path,
+ args.format,
+ args.cut,
+ args.error_rule,
+ args.debug_rule,
+ )
+ if not reader.check_log_exists():
+ print("log-parser: log not found")
+ sys.exit(1)
+
+ reader.read_spread_log()
+
+ if args.output:
+ reader.export_log(args.output)
+
+ reader.print_log()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/tests/lib/external/snapd-testing-tools/utils/log_helper.py b/tests/lib/external/snapd-testing-tools/utils/log_helper.py
new file mode 100644
index 0000000..18de503
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/utils/log_helper.py
@@ -0,0 +1,193 @@
+"""
+This library provides a set of functions that can be used to
+process the spread output.
+"""
+
+import re
+
+from enum import Enum
+
+
+class ListedEnum(Enum):
+ @classmethod
+ def list(cls) -> list[str]:
+ return list(map(lambda c: c.value, cls))
+
+class ExecutionPhase(ListedEnum):
+ PREPARING = "Preparing"
+ EXECUTING = "Executing"
+ RESTORING = "Restoring"
+
+
+class ExecutionInfo(ListedEnum):
+ DEBUG = "Debug"
+ ERROR = "Error"
+ WARNING = "WARNING:"
+
+
+class ExecutionLevel(ListedEnum):
+ TASK = "task"
+ SUITE = "suite"
+ PROJECT = "project"
+
+
+class GeneralAction(ListedEnum):
+ REBOOTING = "Rebooting"
+ DISCARDING = "Discarding"
+ ALLOCATING = "Allocating"
+ WAITING = "Waiting"
+ CONNECTING = "Connecting"
+ SENDING = "Sending"
+
+
+class GeneralActionStatus(ListedEnum):
+ ALLOCATED = "Allocated"
+ CONNECTED = "Connected"
+
+
+class Result(ListedEnum):
+ FAILED = "Failed"
+ SUCCESSFUL = "Successful"
+ ABORTED = "Aborted"
+
+
+OPERATIONS = (
+ ExecutionPhase.list()
+ + GeneralAction.list()
+ + GeneralActionStatus.list()
+ + ExecutionInfo.list()
+ + Result.list()
+)
+
+
+# Start line
+START_LINE = ".*Project content is packed for delivery.*"
+
+
+def _match_date(date: str) -> bool:
+ return re.match(r"\d{4}-\d{2}-\d{2}", date) is not None
+
+
+def _match_time(time: str) -> bool:
+ return re.match(r"\d{2}:\d{2}:\d{2}", time) is not None
+
+
+def is_initial_line(line: str) -> bool:
+ if not line:
+ return False
+
+ pattern = re.compile(START_LINE)
+ parts = line.strip().split(" ")
+ return (
+ len(parts) > 2
+ and _match_date(parts[0])
+ and _match_time(parts[1])
+ and re.match(pattern, line) is not None
+ )
+
+
+# Debug line starts with the operation
+def is_operation(line: str, operation: Enum) -> bool:
+ if not line:
+ return False
+
+ parts = line.strip().split(" ")
+ return (
+ len(parts) > 2
+ and _match_date(parts[0])
+ and _match_time(parts[1])
+ and parts[2] == operation.value
+ )
+
+
+# Check if the line contains any operation
+def is_any_operation(line: str) -> bool:
+ if not line:
+ return False
+
+ parts = line.strip().split(" ")
+ return (
+ len(parts) > 2
+ and _match_date(parts[0])
+ and _match_time(parts[1])
+ and parts[2] in OPERATIONS
+ )
+
+
+# Return the date in the line
+def get_date(line: str) -> str:
+ if not line:
+ raise RuntimeError("Empty line provided")
+
+ parts = line.strip().split(" ")
+ if len(parts) <= 2 or not _match_date(parts[0]):
+ raise ValueError("Incorrect format for line provided: {}".format(line))
+
+ return parts[0]
+
+
+# Return the time in the line
+def get_time(line: str) -> str:
+ if not line:
+ raise RuntimeError("Empty line provided")
+
+ parts = line.strip().split(" ")
+ if len(parts) <= 2 or not _match_date(parts[0]) or not _match_time(parts[1]):
+ raise ValueError("Incorrect format for line provided: {}".format(line))
+
+ return parts[1]
+
+
+# Return the operation in the line
+def get_operation(line: str) -> str:
+ if not line:
+ raise RuntimeError("Empty line provided")
+
+ parts = line.strip().split(" ")
+ if len(parts) <= 2 or not _match_date(parts[0]) or not _match_time(parts[1]):
+ raise ValueError("Incorrect format for line provided: {}".format(line))
+
+ return parts[2]
+
+
+# Return the information that comes after the operation
+def get_operation_info(line: str) -> str:
+ if not line:
+ raise RuntimeError("Empty line provided")
+
+ parts = line.strip().split(" ")
+ if len(parts) <= 3 or not _match_date(parts[0]) or not _match_time(parts[1]):
+ raise ValueError("Incorrect format for line provided: {}".format(line))
+
+ return " ".join(parts[3:])
+
+
+# Details are displayed after Error/Debug/Failed/Warning operations
+def is_detail_start(line: str) -> bool:
+ return (
+ is_operation(line, ExecutionInfo.DEBUG)
+ or is_operation(line, ExecutionInfo.ERROR)
+ or is_operation(line, ExecutionInfo.WARNING)
+ or is_operation(line, Result.FAILED)
+ )
+
+
+# Error/Debug/Failed output sometimes finish with either EOF error or a log file
+# and no detail displayed
+def is_detail(line: str) -> bool:
+ return (
+ is_operation(line, ExecutionInfo.DEBUG)
+ or is_operation(line, ExecutionInfo.ERROR)
+ or is_operation(line, ExecutionInfo.WARNING)
+ ) and line.strip()[-1:] == ":"
+
+
+# Error/Debug/Failed output finishes when a new other line starts
+def is_detail_finished(line: str) -> bool:
+ parts = line.strip().split(" ")
+ return (
+ len(parts) > 3
+ and _match_date(parts[0])
+ and _match_time(parts[1])
+ and parts[2] in OPERATIONS
+ )
diff --git a/tests/lib/external/snapd-testing-tools/utils/spread-manager b/tests/lib/external/snapd-testing-tools/utils/spread-manager
new file mode 100755
index 0000000..be411f6
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/utils/spread-manager
@@ -0,0 +1,167 @@
+#!/bin/bash
+
+show_help() {
+ echo "usage: spread-manager set-manual [--project-path ] "
+ echo " spread-manager unset-manual [--project-path ] "
+ echo " spread-manager reset-manual [--project-path ] "
+ echo ""
+ echo "Tool used to help with functions that are not already implemented in spread"
+}
+
+_check_project_path() {
+ local project_path="$1"
+ if [ -z "$project_path" ]; then
+ echo "spread-manager: project path cannot be empty"
+ exit 1
+ fi
+ if [ ! -d "$project_path" ]; then
+ echo "spread-manager: project path \"$project_path\" has to be a directory"
+ exit 1
+ fi
+ if [ ! -f "$project_path/spread.yaml" ]; then
+ echo "spread-manager: project spread file \"$project_path/spread.yaml\" does not exist"
+ exit 1
+ fi
+}
+
+_check_test_paths() {
+ # shellcheck disable=SC2124
+ local test_paths="$@"
+ if [ -z "$test_paths" ]; then
+ echo "spread-manager: test path cannot be empty"
+ exit 1
+ fi
+ for task_path in $test_paths; do
+ _check_test_path "$task_path"
+ done
+}
+
+_check_test_path() {
+ local test_path="$1"
+ if [ -z "$test_path" ]; then
+ echo "spread-manager: test path cannot be empty"
+ exit 1
+ fi
+ if [ ! -d "$test_path" ]; then
+ echo "spread-manager: test path \"$test_path\" has to be a directory"
+ exit 1
+ fi
+ if [ ! -f "$test_path/task.yaml" ]; then
+ echo "spread-manager: test task \"$test_path/task.yaml\" does not exist"
+ exit 1
+ fi
+}
+
+_set_manual_value() {
+ local task_path=$1
+ local manual_value=$2
+
+ # Update the manual property
+ if grep -E "^manual:" "$task_path"; then
+ sed -i -e "s/^manual:.*/manual: $manual_value/g" "$task_path"
+ else
+ echo "manual: $manual_value" >> "$task_path"
+ fi
+}
+
+set_manual() {
+ local project_path="$1"
+ shift
+ # shellcheck disable=SC2124
+ local test_paths="$@"
+
+ _check_project_path "$project_path"
+ test_paths="$(echo "$test_paths" | tr ',' ' ')"
+ _check_test_paths "$test_paths"
+
+ for test_path in $test_paths; do
+ local task_path
+ task_path="$project_path/$test_path/task.yaml"
+
+ # Backup the task
+ cp "$task_path" "$task_path.back"
+ _set_manual_value "$task_path" true
+ done
+}
+
+unset_manual() {
+ local project_path="$1"
+ shift
+ # shellcheck disable=SC2124
+ local test_paths="$@"
+
+ _check_project_path "$project_path"
+ test_paths="$(echo "$test_paths" | tr ',' ' ')"
+ _check_test_paths "$test_paths"
+
+ for test_path in $test_paths; do
+ local task_path
+ task_path="$project_path/$test_path/task.yaml"
+
+ # Backup the task
+ cp "$task_path" "$task_path.back"
+ _set_manual_value "$task_path" false
+ done
+}
+
+reset_manual() {
+ local project_path="$1"
+ shift
+ # shellcheck disable=SC2124
+ local test_paths="$@"
+
+ _check_project_path "$project_path"
+ test_paths="$(echo "$test_paths" | tr ',' ' ')"
+ _check_test_paths "$test_paths"
+
+ for test_path in $test_paths; do
+ local task_path
+ task_path="$project_path/$test_path/task.yaml"
+
+ if [ -f "$task_path.back" ]; then
+ mv "$task_path.back" "$task_path"
+ else
+ echo "spread-manager: test task backup does not exist \"$task_path.back\""
+ exit 1
+ fi
+ done
+}
+
+main() {
+ if [ $# -eq 0 ]; then
+ show_help
+ exit 0
+ fi
+
+ local subcommand="$1"
+ local action=
+ if [ $# -eq 0 ] || [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
+ show_help
+ exit 0
+ else
+ action=$(echo "$subcommand" | tr '-' '_')
+ shift
+ fi
+
+ if [ -z "$(declare -f "$action")" ]; then
+ echo "spread-manager: no such command: $subcommand" >&2
+ show_help
+ exit 1
+ fi
+
+ local project_path
+ if [ $# -gt 0 ]; then
+ if [[ "$action" =~ .*_manual ]]; then
+ project_path="$(pwd)"
+ if [ "$1" == "--project-path" ]; then
+ project_path="$2"
+ shift 2
+ fi
+ "$action" "$project_path" "$@"
+ else
+ "$action" "$@"
+ fi
+ fi
+}
+
+main "$@"
diff --git a/tests/lib/external/snapd-testing-tools/utils/spread-shellcheck b/tests/lib/external/snapd-testing-tools/utils/spread-shellcheck
new file mode 100755
index 0000000..4769a7d
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/utils/spread-shellcheck
@@ -0,0 +1,413 @@
+#!/usr/bin/env python3
+
+# Copyright (C) 2022 Canonical Ltd
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 3 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+import argparse
+import binascii
+import hashlib
+import itertools
+import logging
+import os
+import re
+import subprocess
+import yaml
+
+from collections import namedtuple
+from concurrent.futures import ThreadPoolExecutor
+from multiprocessing import cpu_count
+from pathlib import Path
+from threading import Lock
+from typing import Dict
+
+
+# default shell for shellcheck
+SHELLCHECK_SHELL = os.getenv('SHELLCHECK_SHELL', 'bash')
+# set to non-empty to ignore all errors
+NO_FAIL = os.getenv('NO_FAIL')
+# set to non empty to enable 'set -x'
+D = os.getenv('D')
+# set to non-empty to enable verbose logging
+V = os.getenv('V')
+# set to a number to use these many threads
+N = int(os.getenv('N') or cpu_count())
+# file with list of files that can fail validation
+CAN_FAIL = os.getenv('CAN_FAIL')
+
+# names of sections
+SECTIONS = ['prepare', 'prepare-each', 'restore', 'restore-each',
+ 'debug', 'debug-each', 'execute', 'repack']
+
+
+def parse_arguments():
+ parser = argparse.ArgumentParser(description='spread shellcheck helper')
+ parser.add_argument('-s', '--shell', default='bash',
+ help='shell')
+ parser.add_argument('-n', '--no-errors', action='store_true',
+ default=False, help='ignore all errors ')
+ parser.add_argument('-v', '--verbose', action='store_true',
+ default=False, help='verbose logging')
+ parser.add_argument('--can-fail', default=None,
+ help=('file with list of files that are can fail '
+ 'validation'))
+ parser.add_argument('-P', '--max-procs', default=N, type=int, metavar='N',
+ help='run these many shellchecks in parallel (default: %(default)s)')
+ parser.add_argument('-e', '--exclude', default=[], action="append",
+ help='path to exclude of the shell check')
+ parser.add_argument('--no-cache', help='disable caching', action='store_true')
+ parser.add_argument('paths', nargs='+', help='paths to check')
+ return parser.parse_args()
+
+
+class ShellcheckRunError(Exception):
+ def __init__(self, stderr):
+ super().__init__()
+ self.stderr = stderr
+
+
+class ShellcheckError(Exception):
+ def __init__(self, path):
+ super().__init__()
+ self.sectionerrors = {}
+ self.path = path
+
+ def addfailure(self, section, error):
+ self.sectionerrors[section] = error
+
+ def __len__(self):
+ return len(self.sectionerrors)
+
+
+class ShellcheckFailures(Exception):
+ def __init__(self, failures=None):
+ super().__init__()
+ self.failures = set()
+ if failures:
+ self.failures = set(failures)
+
+ def merge(self, otherfailures):
+ self.failures = self.failures.union(otherfailures.failures)
+
+ def __len__(self):
+ return len(self.failures)
+
+ def intersection(self, other):
+ return self.failures.intersection(other)
+
+ def difference(self, other):
+ return self.failures.difference(other)
+
+ def __iter__(self):
+ return iter(self.failures)
+
+
+def checksection(data, env: Dict[str, str]):
+ # spread shell snippets are executed under 'set -e' shell, make sure
+ # shellcheck knows about that
+ script_data = []
+ script_data.append('set -e')
+
+ for key, value in env.items():
+ value = str(value)
+ disabled_warnings = set()
+ export_disabled_warnings = set()
+ def replacement(match):
+ if match.group(0) == '"':
+ # SC2089 and SC2090 are about quotes vs arrays
+ # We cannot have arrays in environment variables of spread
+ # So we do have to use quotes
+ disabled_warnings.add('SC2089')
+ export_disabled_warnings.add('SC2090')
+ return r'\"'
+ else:
+ assert(match.group('command') is not None)
+ # "Useless" echo. This is what we get.
+ # We cannot just evaluate to please shellcheck.
+ disabled_warnings.add('SC2116')
+ return '$({})'.format(match.group('command'))
+ value = re.sub(r'[$][(]HOST:(?P.*)[)]|"', replacement, value)
+ # converts
+ # FOO: "$(HOST: echo $foo)" -> FOO="$(echo $foo)"
+ # FOO: "$(HOST: echo \"$foo\")" -> FOO="$(echo "$foo")"
+ # FOO: "foo" -> FOO="foo"
+ # FOO: "\"foo\"" -> FOO="\"foo\""
+ if disabled_warnings:
+ script_data.append("# shellcheck disable={}".format(','.join(disabled_warnings)))
+ script_data.append("{}=\"{}\"".format(key, value))
+ if export_disabled_warnings:
+ script_data.append("# shellcheck disable={}".format(','.join(export_disabled_warnings)))
+ script_data.append("export {}".format(key, value))
+ script_data.append(data)
+ proc = subprocess.Popen("shellcheck -s {} -x -".format(SHELLCHECK_SHELL),
+ stdout=subprocess.PIPE,
+ stdin=subprocess.PIPE,
+ shell=True)
+ stdout, _ = proc.communicate(input='\n'.join(script_data).encode('utf-8'), timeout=60)
+ if proc.returncode != 0:
+ raise ShellcheckRunError(stdout)
+
+
+class Cacher:
+ _instance = None
+
+ def __init__(self):
+ self._enabled = True
+ self._lock = Lock()
+ self._hit =0
+ self._miss = 0
+ self._shellcheck_version = None
+ self._probe_shellcheck_version()
+
+ @classmethod
+ def init(cls):
+ cls._instance = Cacher()
+
+ @classmethod
+ def get(cls):
+ return cls._instance
+
+ def disable(self):
+ logging.debug("caching is disabled")
+ self._enabled = False
+
+ @staticmethod
+ def _cache_path_for(digest):
+ prefix = digest[:2]
+ return Path.home().joinpath(".cache", "spread-shellcheck", prefix, digest)
+
+ def is_cached(self, data, path):
+ if not self._enabled:
+ return False, ""
+ # the digest uses script content and shellcheck versions as inputs, but
+ # consider other possible inputs: path to the *.yaml file (so moving
+ # the script around would cause a miss) or even the contents of this
+ # script
+ h = hashlib.sha256()
+ h.update(self._shellcheck_version)
+ h.update(data)
+ hdg = binascii.b2a_hex(h.digest()).decode()
+ cachepath = Cacher._cache_path_for(hdg)
+ logging.debug("cache stamp %s, exists? %s", cachepath.as_posix(), cachepath.exists())
+ hit = cachepath.exists()
+ self._record_cache_event(hit)
+ return hit, hdg
+
+ def cache_success(self, digest, path):
+ if not self._enabled:
+ return
+ cachepath = Cacher._cache_path_for(digest)
+ logging.debug("cache success, path %s", cachepath.as_posix())
+ cachepath.parent.mkdir(parents=True, exist_ok=True)
+ cachepath.touch()
+
+ def _record_cache_event(self, hit):
+ with self._lock:
+ if hit:
+ self._hit += 1
+ else:
+ self._miss += 1
+
+ def _probe_shellcheck_version(self):
+ logging.debug("probing shellcheck version")
+ out = subprocess.check_output("shellcheck --version", shell=True)
+ self._shellcheck_version = out
+
+ @property
+ def stats(self):
+ return namedtuple('Stats', ['hit', 'miss'])(self._hit, self._miss)
+
+
+def checkfile(path, executor):
+ logging.debug("checking file %s", path)
+ with open(path, mode='rb') as inf:
+ rawdata = inf.read()
+ cached, digest = Cacher.get().is_cached(rawdata, path)
+ if cached:
+ logging.debug("entry %s already cached", digest)
+ return
+ data = yaml.safe_load(rawdata)
+
+ errors = ShellcheckError(path)
+ # TODO: handle stacking of environment from other places that influence it:
+ # spread.yaml -> global env + backend env + suite env -> task.yaml (task
+ # env + variant env).
+ env = {}
+ for key, value in data.get("environment", {}).items():
+ if "/" in key:
+ # TODO: re-check with each variant's value set.
+ key = key.split('/', 1)[0]
+ env[key] = value
+ for section in SECTIONS:
+ if section not in data:
+ continue
+ try:
+ logging.debug("%s: checking section %s", path, section)
+ checksection(data[section], env)
+ except ShellcheckRunError as serr:
+ errors.addfailure(section, serr.stderr.decode('utf-8'))
+
+ if path.endswith('spread.yaml') and 'suites' in data:
+ # check suites
+ suites_sections_and_futures = []
+ for suite in data['suites'].keys():
+ for section in SECTIONS:
+ if section not in data['suites'][suite]:
+ continue
+ logging.debug("%s (suite %s): checking section %s", path, suite, section)
+ future = executor.submit(checksection, data['suites'][suite][section], env)
+ suites_sections_and_futures.append((suite, section, future))
+ for item in suites_sections_and_futures:
+ suite, section, future = item
+ try:
+ future.result()
+ except ShellcheckRunError as serr:
+ errors.addfailure('suites/' + suite + '/' + section,
+ serr.stderr.decode('utf-8'))
+
+ if errors:
+ raise errors
+ # only stamp the cache when the script was found to be valid
+ Cacher.get().cache_success(digest, path)
+
+
+def is_file_in_dirs(file, dirs):
+ for dir in dirs:
+ if os.path.abspath(file).startswith('{}/'.format(os.path.abspath(dir))):
+ print('Skipping {}'.format(file))
+ return True
+
+ return False
+
+
+def findfiles(locations, exclude):
+ for loc in locations:
+ if os.path.isdir(loc):
+ for root, _, files in os.walk(loc, topdown=True):
+ for name in files:
+ if name in ['spread.yaml', 'task.yaml']:
+ full_path = os.path.join(root, name)
+ if not is_file_in_dirs(full_path, exclude):
+ yield full_path
+ else:
+ full_path = os.path.abspath(loc)
+ if not is_file_in_dirs(full_path, exclude):
+ yield full_path
+
+
+def check1path(path, executor):
+ try:
+ checkfile(path, executor)
+ except ShellcheckError as err:
+ return err
+ return None
+
+
+def checkpaths(locs, exclude, executor):
+ # setup iterator
+ locations = findfiles(locs, exclude)
+ failed = []
+ for serr in executor.map(check1path, locations, itertools.repeat(executor)):
+ if serr is None:
+ continue
+ logging.error(('shellcheck failed for file %s in sections: '
+ '%s; error log follows'),
+ serr.path, ', '.join(serr.sectionerrors.keys()))
+ for section, error in serr.sectionerrors.items():
+ logging.error("%s: section '%s':\n%s", serr.path, section, error)
+ failed.append(serr.path)
+
+ if failed:
+ raise ShellcheckFailures(failures=failed)
+
+
+def loadfilelist(flistpath):
+ flist = set()
+ with open(flistpath) as inf:
+ for line in inf:
+ if not line.startswith('#'):
+ flist.add(line.strip())
+ return flist
+
+
+def main(opts):
+ paths = opts.paths or ['.']
+ exclude = opts.exclude
+ failures = ShellcheckFailures()
+ with ThreadPoolExecutor(max_workers=opts.max_procs) as executor:
+ try:
+ checkpaths(paths, exclude, executor)
+ except ShellcheckFailures as sf:
+ failures.merge(sf)
+
+ if not opts.no_cache:
+ stats = Cacher.get().stats
+ logging.info("cache stats: hit %d miss %d", stats.hit, stats.miss)
+
+ if failures:
+ if opts.can_fail:
+ can_fail = loadfilelist(opts.can_fail)
+
+ unexpected = failures.difference(can_fail)
+ if unexpected:
+ logging.error(('validation failed for the following '
+ 'non-whitelisted files:\n%s'),
+ '\n'.join([' - ' + f for f in
+ sorted(unexpected)]))
+ raise SystemExit(1)
+
+ did_not_fail = can_fail - failures.intersection(can_fail)
+ if did_not_fail:
+ logging.error(('the following files are whitelisted '
+ 'but validated successfully:\n%s'),
+ '\n'.join([' - ' + f for f in
+ sorted(did_not_fail)]))
+ raise SystemExit(1)
+
+ # no unexpected failures
+ return
+
+ logging.error('validation failed for the following files:\n%s',
+ '\n'.join([' - ' + f for f in sorted(failures)]))
+
+ if NO_FAIL or opts.no_errors:
+ logging.warning("ignoring errors")
+ else:
+ raise SystemExit(1)
+
+
+if __name__ == '__main__':
+ opts = parse_arguments()
+ if opts.verbose or D or V:
+ lvl = logging.DEBUG
+ else:
+ lvl = logging.INFO
+ logging.basicConfig(level=lvl)
+
+ if CAN_FAIL:
+ opts.can_fail = CAN_FAIL
+
+ if NO_FAIL:
+ opts.no_errors = True
+
+ if opts.max_procs == 1:
+ # TODO: temporary workaround for a deadlock when running with a single
+ # worker
+ opts.max_procs += 1
+ logging.warning('workers count bumped to 2 to workaround a deadlock')
+
+ Cacher.init()
+ if opts.no_cache:
+ Cacher.get().disable()
+
+ main(opts)
diff --git a/tests/lib/external/snapd-testing-tools/utils/spreadJ b/tests/lib/external/snapd-testing-tools/utils/spreadJ
new file mode 100755
index 0000000..26f6b4a
--- /dev/null
+++ b/tests/lib/external/snapd-testing-tools/utils/spreadJ
@@ -0,0 +1,119 @@
+#!/bin/bash
+
+show_help() {
+ echo "usage: spreadJ rerun [--suite SUITE] "
+ echo " spreadJ show [--suite SUITE] "
+ echo " spreadJ stats [--suite SUITE] "
+ echo " spreadJ list [--suite SUITE] "
+ echo ""
+ echo "Available options:"
+ echo " -h --help show this help message."
+ echo ""
+ echo "TARGET:"
+ echo " all,failed,passed,aborted"
+ echo ""
+ echo "Tool used to help with functions that are not already implemented in spread"
+}
+
+_filter_suite() {
+ local suite="$1"
+ if [ -z "$suite" ]; then
+ echo ".testsuites[]"
+ else
+ echo ".testsuites[] | select(.name == \"$suite\")"
+ fi
+}
+
+rerun() {
+ local suite=""
+ if [ "$1" == "--suite" ]; then
+ suite="$2"
+ shift 2
+ fi
+ local res_path="$1"
+ if [ ! -e "$res_path" ]; then
+ echo "spreadJ: results path not found: $res_path"
+ exit 1
+ fi
+
+ local query
+ query="$(_filter_suite $suite).tests[] | select((.result == \"failed\") or (.result == \"aborted\")).name"
+ jq -r "$query" "$res_path"
+}
+
+stats() {
+ local suite=""
+ if [ "$1" == "--suite" ]; then
+ suite="$2"
+ shift 2
+ fi
+ local res_path="$1"
+
+ if [ ! -e "$res_path" ]; then
+ echo "spreadJ: results path not found: $res_path"
+ exit 1
+ fi
+
+ local query
+ if [ -z "$suite" ]; then
+ query="del(.testsuites)"
+ else
+ query="$(_filter_suite $suite) | del(.tests) | del(.name)"
+ fi
+ jq -r "$query" "$res_path"
+}
+
+list() {
+ local suite=""
+ if [ "$1" == "--suite" ]; then
+ suite="$2"
+ shift 2
+ fi
+ local target="$1"
+ local res_path="$2"
+
+ if [ ! -e "$res_path" ]; then
+ echo "spreadJ: results path not found: $res_path"
+ exit 1
+ fi
+
+ if [ -z "$target" ]; then
+ echo "spreadJ: result target cannot be empty"
+ exit 1
+ fi
+
+ local query
+ if [ "$target" == "all" ]; then
+ query="$(_filter_suite $suite).tests[]).name"
+ else
+ query="$(_filter_suite $suite).tests[] | select((.result == \"$target\")).name"
+ fi
+ jq -r "$query" "$res_path"
+}
+
+main() {
+ if [ $# -eq 0 ]; then
+ show_help
+ exit 0
+ fi
+
+ local subcommand="$1"
+ local action=
+ if [ $# -eq 0 ] || [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
+ show_help
+ exit 0
+ else
+ action=$(echo "$subcommand" | tr '-' '_')
+ shift
+ fi
+
+ if [ -z "$(declare -f "$action")" ]; then
+ echo "spreadJ: no such command: $subcommand" >&2
+ show_help
+ exit 1
+ fi
+
+ "$action" "$@"
+}
+
+main "$@"