From 30cd6d4ac93246efa05cf7ba29a00c047544850b Mon Sep 17 00:00:00 2001 From: Andrew Psaltis Date: Mon, 8 Mar 2021 16:20:17 -0500 Subject: [PATCH] Provide "official" pkg_filegroup-using RPM rule and tests (#303) This change replaces the experimental `pkg_rpm` rule in `pkg/experimental/rpm.bzl` rule with one that is aware of the new `pkg_filegroup` semantics in `pkg/mappings.bzl`, with the following additional changes: - The `%files` metadata build-out is made more clear. Additionally, the `rpm_filetag` mapping rule attribute replaces the `section` value previously passed to `pkg_filegroup`. - Inputs to `pkg_rpm` are now specified with the `srcs` attribute instead of `data`, to be consistent with regards to the nameing of runtime requirements and build-time sources. This also matches the implementation of `pkg_tar` and `pkg_zip`; `pkg_deb` stilluses `data`. These should all be made consistent at some point. - Conflicting package contents detection and error logging is now streamlined. - Test coverage has improved, including new analysis tests for conflict detection. Also, bugs in the RPM python tests were fixed opportunistically. The old `pkg_filegroup` code in this same directory will be removed in a separate change. Fixes to other known issues with this implementation of `pkg_rpm` were not attempted, and are currently tracked in #300. --- pkg/experimental/BUILD | 26 +- pkg/experimental/rpm.bzl | 261 +++++++++--------- pkg/experimental/tests/rpm/BUILD | 137 ++++++--- pkg/experimental/tests/rpm/analysis_tests.bzl | 197 +++++++++++++ .../tests/rpm/pkg_rpm_basic_test.py | 7 +- 5 files changed, 445 insertions(+), 183 deletions(-) create mode 100644 pkg/experimental/tests/rpm/analysis_tests.bzl diff --git a/pkg/experimental/BUILD b/pkg/experimental/BUILD index 57783d24..b2c5357d 100644 --- a/pkg/experimental/BUILD +++ b/pkg/experimental/BUILD @@ -12,13 +12,15 @@ # See the License for the specific language governing permissions and # limitations under the License. -licenses(["notice"]) +load("//:mappings.bzl", "pkg_filegroup", "pkg_files") +load(":rpm.bzl", "pkg_rpm") exports_files( glob([ "*.bzl", + ]) + [ "template.spec.in", - ]), + ], visibility = ["//visibility:public"], ) @@ -32,3 +34,23 @@ filegroup( ], visibility = ["//:__pkg__"], ) + +pkg_files( + name = "artifacts", + srcs = [":template.spec.in"], +) + +pkg_filegroup( + name = "usr_share_artifacts", + srcs = [":artifacts"], +) + +pkg_rpm( + name = "example", + srcs = [":usr_share_artifacts"], + description = "Other example!", + license = "N/A", + release = "1", + summary = "Example!", + version = "1.0", +) diff --git a/pkg/experimental/rpm.bzl b/pkg/experimental/rpm.bzl index 0f7d3374..99671189 100644 --- a/pkg/experimental/rpm.bzl +++ b/pkg/experimental/rpm.bzl @@ -1,4 +1,4 @@ -# Copyright 2019-2020 The Bazel Authors. All rights reserved. +# Copyright 2019 The Bazel Authors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -12,17 +12,82 @@ # See the License for the specific language governing permissions and # limitations under the License. -# NOTE: This is different from make_rpm.py in pkg/, and is specific to the -# `pkg_rpm` rule in this directory. - """Provides rules for creating RPM packages via pkg_filegroup and friends.""" -load("//experimental:pkg_filegroup.bzl", "PackageDirInfo", "PackageFileInfo", "PackageSymlinkInfo") +load("//:providers.bzl", "PackageFilegroupInfo") rpm_filetype = [".rpm"] spec_filetype = [".spec", ".spec.in"] +# TODO(nacl): __install, __cp +# {0} is the source, {1} is the dest +# +# TODO(nacl, #292): cp -r does not do the right thing with TreeArtifacts +_INSTALL_FILE_STANZA_FMT = """ +install -d %{{buildroot}}/$(dirname {1}) +cp -r {0} %{{buildroot}}/{1} +""" + +# TODO(nacl): __install +# {0} is the directory name +# +# This may not be strictly necessary, given that they'll be created in the +# CPIO when rpmbuild processes the `%files` list. +_INSTALL_DIR_STANZA_FMT = """ +install -d %{{buildroot}}/{0} +""" + +# {0} is the name of the link, {1} is the target +_INSTALL_SYMLINK_STANZA_FMT = """ +%{{__install}} -d %{{buildroot}}/$(dirname {0}) +%{{__ln_s}} {1} %{{buildroot}}/{0} +""" + +def _package_contents_metadata(origin_label, grouping_label): + """Named construct for helping to identify conflicting packaged contents""" + return struct( + origin = origin_label if origin_label else "", + group = grouping_label, + ) + +def _conflicting_contents_error(destination, from1, from2, attr_name = "srcs"): + message = """Destination {destination} is provided by both (1) {from1_origin} and (2) {from2_origin}; please sensure that each destination is provided by exactly one input. + + (1) {from1_origin} is provided from group {from1_group} + (2) {from2_origin} is provided from group {from2_group} + """.format( + destination = destination, + from1_origin = from1.origin, + from1_group = from1.group, + from2_origin = from2.origin, + from2_group = from2.group, + ) + + fail(message, attr_name) + +def _make_filetags(attributes, default_filetag = None): + """Helper function for rendering RPM spec file tags, like + + ``` + %attr(0755, root, root) %dir + ``` + """ + template = "%attr({mode}, {user}, {group}) {supplied_filetag}" + + mode = attributes.get("mode", "-") + user = attributes.get("user", "-") + group = attributes.get("group", "-") + + supplied_filetag = attributes.get("rpm_filetag", default_filetag) + + return template.format( + mode = mode, + user = user, + group = group, + supplied_filetag = supplied_filetag or "", + ) + def _pkg_rpm_impl(ctx): """Implements the pkg_rpm rule.""" @@ -203,7 +268,7 @@ def _pkg_rpm_impl(ctx): files.append(ctx.file.changelog) args.append(ctx.file.changelog.path) - files += ctx.files.data + files += ctx.files.srcs #### Sanity checking @@ -211,15 +276,10 @@ def _pkg_rpm_impl(ctx): # sane, but the output may also create hard-to-debug issues. Better to err # on the side of correctness here. dest_check_map = {} - for d in ctx.attr.data: + for dep in ctx.attr.srcs: # TODO(nacl, #191): This loop should be consolidated with the install # script-generating loop below, as they're iterating on the same data. - # d is a Target - - # FIXME: if/when we start to consider other providers here, we may want - # to create a subroutine to consolidate these loops. - # NOTE: This does not detect cases where directories are not named # consistently. For example, all of these may collide in reality, but # won't be detected by the below: @@ -232,52 +292,33 @@ def _pkg_rpm_impl(ctx): # to be consistent with path naming conventions. # # There is also an unsolved question of determining how to handle - # subdirectories of "PackageFileInfo" targets that are actually + # subdirectories of "PackageFilesInfo" targets that are actually # directories. - if PackageFileInfo in d: - pfi = d[PackageFileInfo] - for dest in pfi.dests: - if dest in dest_check_map: - fail( - "Destination '{0}' is provided by both {1} and {2}; please ensure each destination is provided by exactly one incoming rule".format( - dest, - dest_check_map[dest], - d.label, - ), - "data", - ) - else: - dest_check_map[dest] = d.label - if PackageDirInfo in d: - pdi = d[PackageDirInfo] - for dest in pdi.dirs: + # d is a Target + pfg_info = dep[PackageFilegroupInfo] + for entry, origin in pfg_info.pkg_files: + for dest, src in entry.dest_src_map.items(): + metadata = _package_contents_metadata(origin, dep.label) if dest in dest_check_map: - fail( - "Destination '{0}' is provided by both {1} and {2}; please ensure each destination is provided by exactly one incoming rule".format( - dest, - dest_check_map[dest], - d.label, - ), - "data", - ) + _conflicting_contents_error(dest, metadata, dest_check_map[dest]) else: - dest_check_map[dest] = d.label + dest_check_map[dest] = metadata - if PackageSymlinkInfo in d: - psi = d[PackageSymlinkInfo] - for dest in psi.link_map.keys(): + for entry, origin in pfg_info.pkg_dirs: + for dest in entry.dirs: + metadata = _package_contents_metadata(origin, dep.label) if dest in dest_check_map: - fail( - "Destination '{0}' is provided by both {1} and {2}; please ensure each destination is provided by exactly one incoming rule".format( - dest, - dest_check_map[dest], - d.label, - ), - "data", - ) + _conflicting_contents_error(dest, metadata, dest_check_map[dest]) else: - dest_check_map[dest] = d.label + dest_check_map[dest] = metadata + + for entry, origin in pfg_info.pkg_symlinks: + metadata = _package_contents_metadata(origin, dep.label) + if entry.destination in dest_check_map: + _conflicting_contents_error(entry.destination, metadata, dest_check_map[entry.destination]) + else: + dest_check_map[entry.destination] = metadata #### Procedurally-generated scripts/lists (%install, %files) @@ -286,28 +327,6 @@ def _pkg_rpm_impl(ctx): if ctx.attr.debug: install_script_pieces.append("set -x") - # TODO(nacl): __install, __cp - # {0} is the source, {1} is the dest - install_file_stanza_fmt = """ -install -d %{{buildroot}}/$(dirname {1}) -cp -r {0} %{{buildroot}}/{1} - """ - - # TODO(nacl): __install - # {0} is the directory name - # - # This may not be strictly necessary, given that they'll be created in the - # CPIO when rpmbuild processes the `%files` list. - install_dir_stanza_fmt = """ -install -d %{{buildroot}}/{0} - """ - - # {0} is the name of the link, {1} is the target - install_symlink_stanza_fmt = """ -%{{__install}} -d %{{buildroot}}/$(dirname {0}) -%{{__ln_s}} {1} %{{buildroot}}/{0} -""" - # Build up the RPM files list (%files -f) rpm_files_list = [] @@ -318,45 +337,33 @@ install -d %{{buildroot}}/{0} # produce an installation script that is longer than necessary. A better # implementation would track directories that are created and ensure that # they aren't unnecessarily recreated. - for elem in ctx.attr.data: - if PackageFileInfo in elem: - pfi = elem[PackageFileInfo] - file_base = "%attr({}) {}".format( - ", ".join(pfi.attrs["unix"]), - "%" + pfi.section if pfi.section else "", - ) - for (source, dest) in zip(pfi.srcs, pfi.dests): + for dep in ctx.attr.srcs: + pfg_info = dep[PackageFilegroupInfo] + for entry, _ in pfg_info.pkg_files: + file_base = _make_filetags(entry.attributes) + + for dest, src in entry.dest_src_map.items(): rpm_files_list.append(file_base + " /" + dest) - install_script_pieces.append(install_file_stanza_fmt.format( - source.path, + install_script_pieces.append(_INSTALL_FILE_STANZA_FMT.format( + src.path, dest, )) - if PackageDirInfo in elem: - pdi = elem[PackageDirInfo] - file_base = "%attr({}) {}".format( - ", ".join(pdi.attrs["unix"]), - "%" + pdi.section if pdi.section else "", - ) - for d in pdi.dirs: + for entry, _ in pfg_info.pkg_dirs: + file_base = _make_filetags(entry.attributes, "%dir") + for d in entry.dirs: rpm_files_list.append(file_base + " /" + d) - install_script_pieces.append(install_dir_stanza_fmt.format( + install_script_pieces.append(_INSTALL_DIR_STANZA_FMT.format( d, )) - if PackageSymlinkInfo in elem: - psi = elem[PackageSymlinkInfo] - file_base = "%attr({}) {}".format( - ", ".join(psi.attrs["unix"]), - "%" + psi.section if psi.section else "", - ) - for link_name, target in psi.link_map.items(): - rpm_files_list.append(file_base + " /" + link_name) - - install_script_pieces.append(install_symlink_stanza_fmt.format( - link_name, - target, - )) + for entry, _ in pfg_info.pkg_symlinks: + file_base = _make_filetags(entry.attributes) + rpm_files_list.append(file_base + " /" + entry.destination) + install_script_pieces.append(_INSTALL_SYMLINK_STANZA_FMT.format( + entry.destination, + entry.source, + )) install_script = ctx.actions.declare_file("{}.spec.install".format(rpm_name)) ctx.actions.write( @@ -385,7 +392,7 @@ install -d %{{buildroot}}/{0} args.extend(["--rpmbuild_arg=" + a for a in additional_rpmbuild_args]) - for f in ctx.files.data: + for f in ctx.files.srcs: args.append(f.path) #### Call the generator script. @@ -441,28 +448,10 @@ def _pkg_rpm_outputs(name, rpm_name, version, release): pkg_rpm = rule( doc = """Creates an RPM format package via `pkg_filegroup` and friends. - The uses the outputs of the rules in `pkg_filegroup.bzl` to construct arbitrary RPM - packages. Attributes of this rule provide preamble information and + The uses the outputs of the rules in `mappings.bzl` to construct arbitrary + RPM packages. Attributes of this rule provide preamble information and scriptlets, which are then used to compose a valid RPM spec file. - The meat is in the `data` attribute, which is handled like so: - - - `pkg_filegroup`s provide mappings of targets to output files: - - - They are copied to their destination after their destination directory - is created. - - - No directory ownership is implied; they will typically be owned by - `root.root` and given permissions associated with `root`'s `umask`, - typically 0755, unless otherwise overidden. - - - File permissions are set in the `%files` manifest. `%config` or other - `%files` properties are propagated from the `section` attribute. - - - `pkg_mkdirs` provide directories and directory ownership. They are - created in the package tree directly. They are owned as specified by the - `section` attribute, which typically is the same as `%dir`. - This rule will fail at analysis time if: - Any `data` input may create the same destination, regardless of other @@ -483,6 +472,16 @@ pkg_rpm = rule( - GNU coreutils. BSD coreutils may work, but are not tested. + To set RPM file attributes (like `%config` and friends), set the + `rpm_filetag` in corresponding packaging rule (`pkg_files`, etc). The value + is prepended with "%" and added to the `%files` list, for example: + + ``` + attrs = {"rpm_filetag": ("config(missingok, noreplace)",)}, + ``` + + Is the equivalent to `%config(missingok, noreplace)` in the `%files` list. + """, # @unsorted-dict-items attrs = { @@ -582,17 +581,13 @@ pkg_rpm = rule( "changelog": attr.label( allow_single_file = True, ), - "data": attr.label_list( - doc = """Mappings to include in this RPM. + "srcs": attr.label_list( + doc = """Mapping groups to include in this RPM. These are typically brought into life as `pkg_filegroup`s. """, mandatory = True, - providers = [ - [PackageFileInfo], - [PackageDirInfo], - [PackageSymlinkInfo], - ], + providers = [PackageFilegroupInfo], ), "debug": attr.bool( doc = """Debug the RPM helper script and RPM generation""", @@ -733,7 +728,7 @@ pkg_rpm = rule( If not provided, the compression mode will be computed using normal RPM spec file processing. Defaults may vary per distribution: - consult your distribution's documentation for more details. + consult its documentation for more details. WARNING: Bazel is currently not aware of action threading requirements for non-test actions. Using threaded compression may result in diff --git a/pkg/experimental/tests/rpm/BUILD b/pkg/experimental/tests/rpm/BUILD index de392a83..d7dbc14c 100644 --- a/pkg/experimental/tests/rpm/BUILD +++ b/pkg/experimental/tests/rpm/BUILD @@ -13,52 +13,92 @@ # limitations under the License. load("@bazel_skylib//rules:build_test.bzl", "build_test") -load("@rules_pkg//experimental:pkg_filegroup.bzl", "pkg_filegroup", "pkg_mkdirs", "pkg_mklinks") +load("@rules_pkg//:mappings.bzl", "pkg_attributes", "pkg_filegroup", "pkg_files", "pkg_mkdirs", "pkg_mklink") load("@rules_pkg//experimental:rpm.bzl", "pkg_rpm") load("@rules_python//python:defs.bzl", "py_test") +load(":analysis_tests.bzl", "analysis_tests") -licenses(["notice"]) - -filegroup( - name = "ars", - srcs = glob(["testdata/*.ar"]), -) +############################################################################ +# analysis tests +############################################################################ +analysis_tests(name = "analysis_tests") ############################################################################ # pkg_filegroups for testing ############################################################################ -pkg_filegroup( - name = "ars_pfg", +filegroup( + name = "ars", + srcs = [ + "//tests:testdata/a.ar", + "//tests:testdata/a_ab.ar", + "//tests:testdata/a_b.ar", + "//tests:testdata/a_b_ab.ar", + "//tests:testdata/ab.ar", + "//tests:testdata/b.ar", + "//tests:testdata/empty.ar", + ], +) + +pkg_files( + name = "ars_pf", srcs = [ ":ars", ], - attrs = {"unix": [ - "0755", - "root", - "root", - ]}, + attributes = pkg_attributes( + group = "root", + mode = "0755", + user = "root", + ), prefix = "/test", ) +genrule( + name = "config_empty", + outs = ["config.txt"], + cmd = "touch $@", +) + +pkg_files( + name = "config_file", + srcs = [":config_empty"], + attributes = pkg_attributes( + group = "root", + mode = "0644", + rpm_filetag = "%config(missingok, noreplace)", + user = "root", + ), +) + pkg_mkdirs( name = "var_log_foo", - attrs = {"unix": [ - "0755", - "root", - "root", - ]}, + attributes = pkg_attributes( + group = "root", + mode = "0755", + user = "root", + ), dirs = ["/var/log/foo"], ) -pkg_mklinks( +pkg_mklink( name = "test_links", - attrs = {"unix": [ - "0777", - "root", - "root", - ]}, - links = {"/usr/bin/link-name": "/usr/bin/link-target"}, + src = "/usr/bin/link-target", + attributes = pkg_attributes( + group = "root", + mode = "0777", + user = "root", + ), + dest = "/usr/bin/link-name", +) + +pkg_filegroup( + name = "test_pfg", + srcs = [ + ":ars_pf", + ":config_file", + ":test_links", + ":var_log_foo", + ], ) ############################################################################ @@ -67,13 +107,11 @@ pkg_mklinks( pkg_rpm( name = "test_rpm", + srcs = [ + ":test_pfg", + ], architecture = "noarch", conflicts = ["not-a-test"], - data = [ - ":ars_pfg", - ":test_links", - ":var_log_foo", - ], description = """pkg_rpm test rpm description""", license = "Apache 2.0", post_scriptlet = """echo post""", @@ -92,14 +130,12 @@ pkg_rpm( # Just like the above one, except the compression is changed. pkg_rpm( name = "test_rpm-bzip2", + srcs = [ + ":test_pfg", + ], architecture = "noarch", binary_payload_compression = "w2.bzdio", conflicts = ["not-a-test"], - data = [ - ":ars_pfg", - ":test_links", - ":var_log_foo", - ], description = """pkg_rpm test rpm description""", license = "Apache 2.0", post_scriptlet = """echo post""", @@ -122,7 +158,10 @@ pkg_rpm( # Emit a CSV file providing a manifest providing the expected RPM contents genrule( name = "test_rpm_manifest", - srcs = [":ars"], + srcs = [ + ":ars", + ":config_file", + ], outs = ["manifest.csv"], # Keep the header (the first line echo'd below) in sync with # rpm_queryformat_fieldnames in pkg_rpm_basic_test.py @@ -139,7 +178,17 @@ genrule( # Symlink destination (not provided) echo , ) >> $@ - + done + # Config file + for f in $(location :config_file); do + ( + echo -n /$$(basename $$f), + md5sum $$f | cut -d' ' -f1 | tr '\\n' , + # User,Group,Mode,Fflags (fflags "cmn" = config + missingok + noreplace) + echo -n 'root,root,100644,cmn' + # Symlink destination (not provided) + echo , + ) >> $@ done # Directory (has no hash) ( @@ -180,6 +229,7 @@ genrule( echo 'capability:sense' echo 'test:manual' + echo 'config(test_rpm) = 1.1.1-2222:config' ) > $(RULEDIR)/provides.csv ( # NOTE: excludes 'rpmlib' requires that may be version-dependent @@ -193,6 +243,7 @@ genrule( echo 'bash:preun' # Hand-specified echo 'test-lib > 1.0:manual' + echo 'config(test_rpm) = 1.1.1-2222:config' ) > $(RULEDIR)/requires.csv """, ) @@ -202,7 +253,7 @@ genrule( sh_library( name = "pkg_rpm_basic_test_data", testonly = True, - data = [ + srcs = [ ":test_rpm", ":test_rpm-bzip2", ":test_rpm_manifest", @@ -230,12 +281,10 @@ py_test( pkg_rpm( name = "test_rpm_default_template", testonly = True, - architecture = "noarch", - data = [ - ":ars_pfg", - ":test_links", - ":var_log_foo", + srcs = [ + ":test_pfg", ], + architecture = "noarch", description = """pkg_rpm test rpm description""", license = "Apache 2.0", release = "2222", diff --git a/pkg/experimental/tests/rpm/analysis_tests.bzl b/pkg/experimental/tests/rpm/analysis_tests.bzl new file mode 100644 index 00000000..8775d098 --- /dev/null +++ b/pkg/experimental/tests/rpm/analysis_tests.bzl @@ -0,0 +1,197 @@ +# Copyright 2021 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tests for RPM generation analysis""" + +load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts") +load("//experimental:rpm.bzl", "pkg_rpm") +load( + "//:mappings.bzl", + "pkg_filegroup", + "pkg_files", + "pkg_mkdirs", + "pkg_mklink", +) + +# Generic negative test boilerplate +# +# TODO: create an internal test library containing this function, and maybe the second one too +def _generic_neg_test_impl(ctx): + env = analysistest.begin(ctx) + + asserts.expect_failure(env, ctx.attr.reason) + + return analysistest.end(env) + +generic_neg_test = analysistest.make( + _generic_neg_test_impl, + attrs = { + "reason": attr.string( + default = "", + ), + }, + expect_failure = True, +) + +def _generic_base_case_test_impl(ctx): + env = analysistest.begin(ctx) + + # Nothing here intentionally, this is simply an attempt to verify successful + # analysis. + + return analysistest.end(env) + +generic_base_case_test = analysistest.make( + _generic_base_case_test_impl, + attrs = {}, +) + +def _declare_pkg_rpm(name, srcs_ungrouped, tags = None, **kwargs): + pfg_name = "{}_pfg".format(name) + _tags = tags or ["manual"] + + pkg_filegroup( + name = pfg_name, + srcs = srcs_ungrouped, + tags = _tags, + ) + + pkg_rpm( + name = name, + srcs = [":" + pfg_name], + version = "1.0", + release = "1", + license = "N/A", + summary = "A test", + description = "very much a test", + tags = _tags, + **kwargs + ) + +def _declare_conflicts_test(name, srcs, **kwargs): + rpm_name = name + "_rpm" + _declare_pkg_rpm( + name = rpm_name, + srcs_ungrouped = srcs, + tags = ["manual"], + ) + + generic_neg_test( + name = name, + target_under_test = ":" + rpm_name, + ) + +def _test_conflicting_inputs(name): + # The test here is to confirm if pkg_rpm rejects conflicting inputs + # + # The structure of the code is such that it's only necessary to test any one + # packaged item conflicts with all others; order is irrelevant. + # + # So, we test how everything would conflict with a "file" entry + pkg_files( + name = "{}_file_base".format(name), + srcs = ["foo"], + tags = ["manual"], + ) + + _declare_pkg_rpm( + name = name + "_base", + srcs_ungrouped = [":{}_file_base".format(name)], + ) + + generic_base_case_test( + name = name + "_base_case_passes_analysis", + target_under_test = ":" + name + "_base", + ) + + ################################################## + # file vs conflicting file + ################################################## + + pkg_files( + name = "{}_file_conflict".format(name), + srcs = ["foo"], + tags = ["manual"], + ) + + _declare_conflicts_test( + name = name + "_conflict_with_file", + srcs = [ + ":{}_file_base".format(name), + ":{}_file_conflict".format(name), + ], + ) + + ################################################## + # file vs conflicting dir + ################################################## + + pkg_mkdirs( + name = "{}_dir_conflict".format(name), + dirs = ["foo"], + tags = ["manual"], + ) + + _declare_conflicts_test( + name = name + "_conflict_with_dir", + srcs = [ + ":{}_file_base".format(name), + ":{}_dir_conflict".format(name), + ], + ) + + ################################################## + # file vs conflicting symbolic link + ################################################## + + pkg_mklink( + name = "{}_symlink_conflict".format(name), + dest = "foo", + src = "bar", + tags = ["manual"], + ) + + _declare_conflicts_test( + name = name + "_conflict_with_symlink", + srcs = [ + ":{}_file_base".format(name), + ":{}_symlink_conflict".format(name), + ], + ) + + native.test_suite( + name = name, + tests = [ + ":{}_{}".format(name, test_name) + for test_name in [ + "base_case_passes_analysis", + "conflict_with_file", + "conflict_with_dir", + "conflict_with_symlink", + ] + ], + ) + +def analysis_tests(name, **kwargs): + # Need to test: + # + # - Mutual exclusivity of certain options (low) + # + _test_conflicting_inputs(name = name + "_conflicting_inputs") + native.test_suite( + name = name, + tests = [ + name + "_conflicting_inputs", + ], + ) diff --git a/pkg/experimental/tests/rpm/pkg_rpm_basic_test.py b/pkg/experimental/tests/rpm/pkg_rpm_basic_test.py index 82c1b473..638e19f8 100644 --- a/pkg/experimental/tests/rpm/pkg_rpm_basic_test.py +++ b/pkg/experimental/tests/rpm/pkg_rpm_basic_test.py @@ -125,11 +125,10 @@ def test_contents(self): sio = io.StringIO(rpm_output.decode('utf-8')) rpm_output_reader = csv.DictReader( sio, fieldnames = rpm_queryformat_fieldnames) - for rpm_file_info in rpm_output_reader: - my_path = rpm_file_info['path'] - self.assertIn(my_path, manifest_specs) - self.assertDictEqual(manifest_specs[my_path], rpm_file_info) + rpm_output_specs = {r['path'] : r for r in rpm_output_reader} + + self.assertDictEqual(manifest_specs, rpm_output_specs) def test_preamble_metadata(self): metadata_prefix = "rules_pkg/experimental/tests/rpm"