http://cr.opensolaris.org/~bpytlik/ips-10515-v1/
10515 search should support -o for output like pkg contents
10608 search should support -H option for output
In addition to the normal code review, I'd appreciate if a couple of
people could apply the patch I've attached, run the test suite and
report how it compares to the time the current gate takes to run? I've
been seeing some timing results that leave me questioning my sanity
(like adding 216 commented lines doubling the time of the test suite) so
before I spend more cycles on something that's specific only on my
machine/environment/configuration/astrological sign, I'd like some
independent confirmation....
Thanks,
Brock
# HG changeset patch
# User Brock Pytlik <[email protected]>
# Date 1250565946 25200
# Node ID ba1529fbd2057c1067ebc98bf99b4ca31fa68eb5
# Parent 1adb555e1fefa4cdb8a16e0cf56a37ca1e8fb47c
10515 search should support -o for output like pkg contents
10608 search should support -H option for output
diff -r 1adb555e1fef -r ba1529fbd205 src/client.py
--- a/src/client.py Fri Aug 14 22:12:12 2009 -0700
+++ b/src/client.py Mon Aug 17 20:25:46 2009 -0700
@@ -80,6 +80,16 @@
CLIENT_API_VERSION = 19
PKG_CLIENT_NAME = "pkg"
+JUST_UNKN = 0
+JUST_LEFT = -1
+JUST_RIGHT = 1
+
+valid_special_attrs = [ "action.name", "action.key", "action.raw",
+ "pkg.name", "pkg.fmri", "pkg.shortfmri", "pkg.publisher",
+ "pkg.size", "pkg.csize" ]
+
+valid_special_prefixes = ["action.", "pkg."]
+
def error(text, cmd=None):
"""Emit an error message prefixed by the command name """
@@ -959,13 +969,33 @@
return a.attrs.get(a.key_attr), a.name, match
return match, a.name, a.attrs.get(a.key_attr)
-def process_v_1_search(tup, first, return_type, pub):
- """Transforms the tuples returned by search v1 into the four column
- output format.
+def produce_matching_token(action, match):
+ """Given an action and a match value (see convert_output for more
+ details on this parameter), return the token which matched the
query."""
- The "first" parameter is a boolean stating whether this is the first
- time this function has been called. This controls the printing of the
- header information.
+ if isinstance(action, actions.attribute.AttributeAction):
+ return match
+ if match == "basename":
+ return action.attrs.get("path")
+ r = action.attrs.get(match)
+ if r:
+ return r
+ return action.attrs.get(action.key_attr)
+
+def produce_matching_type(action, match):
+ """Given an action and a match value (see convert_output for more
+ details on this parameter), return the kind of match this was. For
+ example, if the query matched a portion of a path of an action, this
+ will return 'path'. If the action is an attribute action, it returns
+ the name set in the action. """
+
+ if not isinstance(action, actions.attribute.AttributeAction):
+ return match
+ return action.attrs.get("name")
+
+def v1_extract_info(tup, return_type, pub):
+ """Given a result from search, massages the information into a form
+ useful for produce_lines.
The "return_type" parameter is an enumeration that describes the type
of the information that will be converted.
@@ -974,7 +1004,14 @@
If "return_type" is action information, "tup" is a three-tuple of the
fmri name, the match, and a string representation of the action. In
the case where "return_type" is package information, "tup" is a one-
- tuple containing the fmri name."""
+ tuple containing the fmri name.
+
+ The "pub" parameter contains information about the publisher from which
+ the result was obtained."""
+
+ action = None
+ match = None
+ match_type = None
if return_type == api.Query.RETURN_ACTIONS:
try:
@@ -983,47 +1020,56 @@
error(_("The server returned a malformed result.\n"
"The problematic structure:%r") % (tup,))
return False
- if first:
- msg("%-10s %-9s %-25s %s" %
- ("INDEX", "ACTION", "VALUE", "PACKAGE"))
try:
- out1, out2, out3 = __convert_output(action, match)
+ action = actions.fromstr(action.rstrip())
except actions.ActionError, e:
error(_("The server returned an invalid action.\n%s") %
e)
return False
- msg("%-10s %-9s %-25s %s" %
- (out1, out2, out3,
- fmri.PkgFmri(str(pfmri)).get_short_fmri()))
+ match_type = produce_matching_type(action, match)
+ match = produce_matching_token(action, match)
else:
pfmri = tup
- if first:
- msg("%s" % ("PACKAGE"))
- pub_name = ''
- # If pub is not None, it's either a RepositoryURI or a
Publisher
- # object. If it's a Publisher, it has a prefix. Otherwise,
- # use the uri.
- if pub is not None and hasattr(pub, "prefix"):
- pub_name = " (%s)" % pub.prefix
- elif pub is not None and hasattr(pub, "uri"):
- pub_name = " (%s)" % pub.uri
- msg("%s%s" %
- (fmri.PkgFmri(str(pfmri)).get_short_fmri(), pub_name))
- return True
+ pfmri = fmri.PkgFmri(str(pfmri))
+ return pfmri, action, pub, match, match_type
def search(img_dir, args):
"""Search for the given query."""
- opts, pargs = getopt.getopt(args, "alprs:I")
+ # Constants which control the paging behavior for search output.
+ page_timeout = .5
+ max_timeout = 5
+ min_page_size = 5
+
+ search_attrs = valid_special_attrs[:]
+ search_attrs.extend(["search.match", "search.match_type"])
+
+ search_prefixes = valid_special_prefixes[:]
+ search_prefixes.extend(["search."])
+
+ opts, pargs = getopt.getopt(args, "Halo:prs:I")
+
+ default_attrs_action = ["search.match_type", "action.name",
+ "search.match", "pkg.shortfmri"]
+
+ default_attrs_package = ["pkg.shortfmri", "pkg.publisher"]
local = remote = case_sensitive = False
servers = []
+ attrs = []
return_actions = True
+ display_headers = True
+ use_default_attrs = True
for opt, arg in opts:
- if opt == "-a":
+ if opt == "-H":
+ display_headers = False
+ elif opt == "-a":
return_actions = True
elif opt == "-l":
local = True
+ elif opt == "-o":
+ attrs.extend(arg.split(","))
+ use_default_attrs = False
elif opt == "-p":
return_actions = False
elif opt == "-r":
@@ -1048,6 +1094,19 @@
usage(_("at least one search term must be provided"),
cmd="search")
+ check_attrs(attrs, "search", reference=search_attrs,
+ prefixes=search_prefixes)
+
+ action_attr = False
+ for a in attrs:
+ if a.startswith("action.") or a.startswith("search.match"):
+ action_attr = True
+ if not return_actions:
+ usage(_("action level options ('%s') to -o "
+ "cannot be used with the -p option") % a,
+ cmd="search")
+ break
+
searches = []
try:
@@ -1063,7 +1122,6 @@
error(e)
return 1
- first = True
good_res = False
bad_res = False
@@ -1077,20 +1135,92 @@
# By default assume we don't find anything.
retcode = 1
- for raw_value in itertools.chain(*searches):
+ # get initial set of results
+ justs = calc_justs(attrs)
+ page_again = True
+ widths = []
+ st = None
+ ssu = None
+ header_attrs = attrs
+ while page_again:
+ unprocessed_res = []
+ page_again = False
+ # Indexless search raises a slow search exception. In
+ # that case, catch the exception, finish processing the
+ # results, then propogate the error.
try:
- query_num, pub, (v, return_type, tmp) = \
- raw_value
- except ValueError, e:
- error(_("The server returned a malformed "
- "result:%r") % (raw_value,))
- bad_res = True
- continue
- ret = process_v_1_search(tmp, first,
- return_type, pub)
- good_res |= ret
- bad_res |= not ret
- first = False
+ for raw_value in itertools.chain(*searches):
+ if not st:
+ st = time.time()
+ try:
+ query_num, pub, \
+ (v, return_type, tmp) = \
+ raw_value
+ except ValueError, e:
+ error(_("The server returned a
"
+ "malformed result:%r") %
+ (raw_value,))
+ bad_res = True
+ continue
+ # This check is necessary since a
+ # a pacakge search can be specified
+ # using the <> operator.
+ if action_attr and \
+ return_type != \
+ api.Query.RETURN_ACTIONS:
+ usage(_("action level options "
+ "to -o cannot be used with
"
+ "the queries that return "
+ "packages"), cmd="search")
+ if use_default_attrs and not justs:
+ if return_type == \
+ api.Query.RETURN_ACTIONS:
+ attrs = \
+
default_attrs_action
+ header_attrs = \
+ ["index", "action",
+ "value", "package"]
+ else:
+ attrs =
default_attrs_package
+ header_attrs = \
+ ["package",
+ "publisher"]
+ justs = calc_justs(attrs)
+ ret = v1_extract_info(
+ tmp, return_type, pub)
+ bad_res |= isinstance(ret, bool)
+ if ret:
+ good_res = True
+ unprocessed_res.append(ret)
+ # Check whether the paging timeout
+ # should be increased.
+ if time.time() - st > page_timeout:
+ if len(unprocessed_res) > \
+ min_page_size:
+ page_again = True
+ break
+ else:
+ page_timeout = min(
+ page_timeout * 2,
+ max_timeout)
+ except api_errors.SlowSearchUsed, e:
+ ssu = e
+ lines = produce_lines(unprocessed_res, attrs,
+ show_all=True)
+ old_widths = widths[:]
+ widths = calc_widths(lines, attrs, widths)
+ # If headers are being displayed and the layout of the
+ # columns have changed, print the headers again using
+ # the new widths.
+ if display_headers and old_widths[:-1] != widths[:-1]:
+ print_headers(header_attrs, widths, justs)
+ for line in lines:
+ msg((create_output_format(display_headers,
+ widths, justs, line) %
+ tuple(line)).rstrip())
+ st = time.time()
+ if ssu:
+ raise ssu
except (api_errors.IncorrectIndexFileHash,
api_errors.InconsistentIndexException):
error(_("The search index appears corrupted. Please "
@@ -1113,7 +1243,7 @@
retcode = 4
elif bad_res:
retcode = 1
- elif not first:
+ elif good_res:
retcode = 0
return retcode
@@ -1255,76 +1385,178 @@
return err
-def display_contents_results(actionlist, attrs, sort_attrs, action_types,
- display_headers):
- """Print results of a "list" operation """
+def calc_widths(lines, attrs, widths=None):
+ """Given a set of lines and a set of attributes, calculate the minimum
+ width each column needs to hold its contents."""
- # widths is a list of tuples of column width and justification. Start
- # with the widths of the column headers.
- JUST_UNKN = 0
- JUST_LEFT = -1
- JUST_RIGHT = 1
- widths = [ (len(attr) - attr.find(".") - 1, JUST_UNKN)
- for attr in attrs ]
+ if not widths:
+ widths = [ len(attr) - attr.find(".") - 1 for attr in attrs ]
+ for l in lines:
+ for i, a in enumerate(l):
+ if len(str(a)) > widths[i]:
+ widths[i] = len(str(a))
+ return widths
+
+def calc_justs(attrs):
+ """Given a set of output attributes, find any attributes with known
+ justification directions and assign them."""
+
+ def __chose_just(attr):
+ if attr in ["action.name", "action.key", "action.raw",
+ "pkg.name", "pkg.fmri", "pkg.shortfmri", "pkg.publisher"]:
+ return JUST_LEFT
+ return JUST_UNKN
+ return [ __chose_just(attr) for attr in attrs ]
+
+def produce_lines(actionlist, attrs, action_types=None, show_all=False):
+ """Produces a list of n tuples (where n is the length of attrs)
+ containing the relevant information about the actions.
+
+ The "actionlist" parameter is a list of tuples which contain the fmri
+ of the package that's the source of the action, the action, and the
+ publisher the action's package came from. If the actionlist was
+ generated by searching, the last two pieces, "match" and "match_type"
+ contain information about why this action was selected.
+
+ The "attrs" parameter is a list of the attributes of the action that
+ should be displayed.
+
+ The "action_types" parameter may contain a list of the types of actions
+ that should be displayed.
+
+ The "show_all" parameter determines whether an action that lacks one
+ or more of the desired attributes will be displayed or not.
+ """
+
lines = []
-
- for manifest, action in actionlist:
+ for pfmri, action, pub, match, match_type in actionlist:
if action_types and action.name not in action_types:
continue
line = []
for i, attr in enumerate(attrs):
- just = JUST_UNKN
- # As a first approximation, numeric attributes
- # are right justified, non-numerics left.
- try:
- int(action.attrs[attr])
- just = JUST_RIGHT
- # attribute is non-numeric or is something like
- # a list.
- except (ValueError, TypeError):
- just = JUST_LEFT
- # attribute isn't in the list, so we don't know
- # what it might be
- except KeyError:
- pass
-
- if attr in action.attrs:
+ if action and attr in action.attrs:
a = action.attrs[attr]
elif attr == "action.name":
a = action.name
- just = JUST_LEFT
elif attr == "action.key":
a = action.attrs[action.key_attr]
- just = JUST_LEFT
elif attr == "action.raw":
a = action
- just = JUST_LEFT
elif attr == "pkg.name":
- a = manifest.fmri.get_name()
- just = JUST_LEFT
+ a = pfmri.get_name()
elif attr == "pkg.fmri":
- a = manifest.fmri
- just = JUST_LEFT
+ a = pfmri
elif attr == "pkg.shortfmri":
- a = manifest.fmri.get_short_fmri()
- just = JUST_LEFT
+ a = pfmri.get_short_fmri()
elif attr == "pkg.publisher":
- a = manifest.fmri.get_publisher()
- just = JUST_LEFT
+ a = pfmri.get_publisher()
+ if a is None:
+ a = pub
+ if a is None:
+ a = ""
+ elif attr == "search.match":
+ a = match
+ elif attr == "search.match_type":
+ a = match_type
else:
a = ""
line.append(a)
- # XXX What to do when a column's justification
- # changes?
- if just != JUST_UNKN:
- widths[i] = \
- (max(widths[i][0], len(str(a))), just)
+ if line and [l for l in line if str(l) != ""] or show_all:
+ lines.append(line)
+ return lines
- if line and [l for l in line if str(l) != ""]:
- lines.append(line)
+def default_left(v):
+ """For a given justification "v", use the default of left justification
+ if "v" is JUST_UNKN."""
+ if v == JUST_UNKN:
+ return JUST_LEFT
+ return v
+
+def print_headers(attrs, widths, justs):
+ """Print out the headers for the columns in the output.
+
+ The "attrs" parameter provides the headings that should be used.
+
+ The "widths" parameter provides the current estimates of the width
+ for each column. These may be changed due to the length of the headers.
+ This function does modify the values contained in "widths" outside this
+ function.
+
+ The "justs" parameter contains the justifications to use with each
+ header."""
+
+ headers = []
+ for i, attr in enumerate(attrs):
+ headers.append(str(attr.upper()))
+ widths[i] = max(widths[i], len(attr))
+
+ # Now that we know all the widths, multiply them by the
+ # justification values to get positive or negative numbers to
+ # pass to the %-expander.
+ widths = [ e[0] * default_left(e[1]) for e in zip(widths, justs) ]
+ fmt = ("%%%ss " * len(widths)) % tuple(widths)
+
+ msg((fmt % tuple(headers)).rstrip())
+
+def guess_unknown(j, v):
+ """If the justificaton to use for a value is unknown, assume that if
+ it is an integer, the output should be right justified, otherwise it
+ should be left justified."""
+
+ if j != JUST_UNKN:
+ return j
+ try:
+ int(v)
+ return JUST_RIGHT
+ # attribute is non-numeric or is something like
+ # a list.
+ except (ValueError, TypeError):
+ return JUST_LEFT
+
+def create_output_format(display_headers, widths, justs, line):
+ """Produce a format string that can be used to display results.
+
+ The "display_headers" parameter is whether headers have been displayed
+ or not. If they have not, then use a simple tab system. If they
+ have, use the information in the other parameters to control the
+ formatting of the line.
+
+ The "widths" parameter contains the width to use for each column.
+
+ The "justs" parameter contains the justifications to use for each
+ column.
+
+ The "line" parameter contains the information that will be displayed
+ using the resulting format. It's needed so that a choice can be made
+ about columns with unknown justifications.
+ """
+
+ if display_headers:
+ # Now that we know all the widths, multiply them by the
+ # justification values to get positive or negative numbers to
+ # pass to the %-expander.
+ line_widths = [
+ w * guess_unknown(j, a)
+ for w, j, a in zip(widths, justs, line)
+ ]
+ fmt = ("%%%ss " * len(line_widths)) % tuple(line_widths)
+
+ return fmt
+ fmt = "%s\t" * len(widths)
+ fmt.rstrip("\t")
+ return fmt
+
+def display_contents_results(actionlist, attrs, sort_attrs, action_types,
+ display_headers):
+ """Print results of a "list" operation """
+
+ justs = calc_justs(attrs)
+ lines = produce_lines(actionlist, attrs, action_types)
+ widths = calc_widths(lines, attrs)
+
sortidx = 0
for i, attr in enumerate(attrs):
if attr == sort_attrs[0]:
@@ -1332,7 +1564,7 @@
break
# Sort numeric columns numerically.
- if widths[sortidx][1] == JUST_RIGHT:
+ if justs[sortidx] == JUST_RIGHT:
def key_extract(x):
try:
return int(x[sortidx])
@@ -1342,25 +1574,22 @@
key_extract = lambda x: x[sortidx]
if display_headers:
- headers = []
- for i, attr in enumerate(attrs):
- headers.append(str(attr.upper()))
- widths[i] = \
- (max(widths[i][0], len(attr)), widths[i][1])
-
- # Now that we know all the widths, multiply them by the
- # justification values to get positive or negative numbers to
- # pass to the %-expander.
- widths = [ e[0] * e[1] for e in widths ]
- fmt = ("%%%ss " * len(widths)) % tuple(widths)
-
- msg((fmt % tuple(headers)).rstrip())
- else:
- fmt = "%s\t" * len(widths)
- fmt.rstrip("\t")
+ print_headers(attrs, widths, justs)
for line in sorted(lines, key=key_extract):
- msg((fmt % tuple(line)).rstrip())
+ msg((create_output_format(display_headers, widths, justs,
+ line) % tuple(line)).rstrip())
+
+def check_attrs(attrs, cmd, reference=valid_special_attrs,
+ prefixes=valid_special_prefixes):
+ """For a set of output attributes ("attrs") passed to a command
("cmd"),
+ if the attribute lives in a known name space, check whether it is
valid.
+ """
+
+ for a in attrs:
+ for p in prefixes:
+ if a.startswith(p) and not a in reference:
+ usage(_("Invalid attribute '%s'") % a, cmd)
def list_contents(img, args):
"""List package contents.
@@ -1375,10 +1604,6 @@
opts, pargs = getopt.getopt(args, "Ho:s:t:mfr")
- valid_special_attrs = [ "action.name", "action.key", "action.raw",
- "pkg.name", "pkg.fmri", "pkg.shortfmri", "pkg.publisher",
- "pkg.size", "pkg.csize" ]
-
display_headers = True
display_raw = False
remote = False
@@ -1423,12 +1648,7 @@
usage(_("-m and %s may not be specified at the same "
"time") % invalid.pop(), cmd="contents")
- for a in attrs:
- if a.startswith("action.") and not a in valid_special_attrs:
- usage(_("Invalid attribute '%s'") % a, cmd="contents")
-
- if a.startswith("pkg.") and not a in valid_special_attrs:
- usage(_("Invalid attribute '%s'") % a, cmd="contents")
+ check_attrs(attrs, "contents")
img.history.operation_name = "contents"
img.load_catalogs(progress.QuietProgressTracker())
@@ -1538,7 +1758,7 @@
manifests = ( img.get_manifest(f, all_arch=display_raw) for f in fmris
)
actionlist = [
- (m, a)
+ (m.fmri, a, None, None, None)
for m in manifests
for a in m.gen_actions(excludes)
]
diff -r 1adb555e1fef -r ba1529fbd205 src/man/pkg.1.txt
--- a/src/man/pkg.1.txt Fri Aug 14 22:12:12 2009 -0700
+++ b/src/man/pkg.1.txt Mon Aug 17 20:25:46 2009 -0700
@@ -16,7 +16,7 @@
/usr/bin/pkg contents [-Hmr] [-o attribute ...] [-s sort_key]
[-t action_type ... ] [pkg_fmri_pattern ...]
/usr/bin/pkg list [-Hafsuv] [--no-refresh] [pkg_fmri_pattern ...]
- /usr/bin/pkg search [-alprI] [-s repo_uri] query
+ /usr/bin/pkg search [-alprHI] [-o attribute ...] [-s repo_uri] query
/usr/bin/pkg refresh [--full] [publisher ...]
@@ -245,7 +245,7 @@
the package containing the action, such
as "opensolaris.org"
- search [-alprI] [-s repo_uri] query
+ search [-alprHI] [-o attribute ...] [-s repo_uri] query
Search for matches to the query, and display the results.
Which tokens are indexed are action-dependent, but may
include content hashes and pathnames.
@@ -255,6 +255,18 @@
With -l, search the image's installed packages.
+ With -o, the columns of the results may be controlled. The
+ -o option may be specified multiple times, or multiple attributes
+ may be specified as the argument to one -o option by separating
+ the attribute names with commas. In addition to the "pseudo"
+ attributes outlined above, more are defined for search results:
+
+ search.match Corresponds to the string which matched the
+ search query.
+
+ search.match_type Corresponds to the attribute which contained
+ the string that matched the search query.
+
With -p, perform the search and display the packages which contain
at least one action which matched the query. This is equivalent to
enclosing the entire query with '<>'. (For a description of the
@@ -263,6 +275,8 @@
By default, and with -r, search the repositories corresponding
to the image's publishers.
+ With -H, omit the headers.
+
With -I, use a case-sensitive search.
With -s, search the pkg(5) repository located at the given URI.
diff -r 1adb555e1fef -r ba1529fbd205 src/tests/baseline.txt
--- a/src/tests/baseline.txt Fri Aug 14 22:12:12 2009 -0700
+++ b/src/tests/baseline.txt Mon Aug 17 20:25:46 2009 -0700
@@ -497,6 +497,7 @@
cli.t_pkg_refresh.py
TestPkgRefreshMulti.test_set_publisher_induces_delayed_full_refresh|pass
cli.t_pkg_refresh.py
TestPkgRefreshMulti.test_set_publisher_induces_full_refresh|pass
cli.t_pkg_refresh.py TestPkgRefreshMulti.test_specific_refresh|pass
+cli.t_pkg_search.py TestPkgSearchBasics.test_bug_10515|pass
cli.t_pkg_search.py TestPkgSearchBasics.test_bug_1873|pass
cli.t_pkg_search.py TestPkgSearchBasics.test_bug_7835|pass
cli.t_pkg_search.py TestPkgSearchBasics.test_bug_8098|pass
diff -r 1adb555e1fef -r ba1529fbd205 src/tests/cli/t_pkg_search.py
--- a/src/tests/cli/t_pkg_search.py Fri Aug 14 22:12:12 2009 -0700
+++ b/src/tests/cli/t_pkg_search.py Mon Aug 17 20:25:46 2009 -0700
@@ -77,8 +77,8 @@
"""
bogus_fmri = fmri.PkgFmri("[email protected],5.11-0:20090326T233451Z")
- headers = "INDEX ACTION VALUE PACKAGE\n"
- pkg_headers = "PACKAGE\n"
+ headers = "INDEX ACTION VALUE PACKAGE\n"
+ pkg_headers = "PACKAGE PUBLISHER\n"
res_remote_path = set([
headers,
@@ -230,7 +230,7 @@
res_remote_pkg_ret_pkg = set([
pkg_headers,
- "pkg:/[email protected] (test)\n"
+ "pkg:/[email protected] test\n"
])
res_remote_file = set([
@@ -251,8 +251,33 @@
'820157a2043e3135f342b238129b556aade20347 file
bin/example_path pkg:/[email protected]\n'
])
+ o_headers = \
+ "ACTION.NAME ACTION.KEY PKG.NAME " \
+ "PKG.SHORTFMRI SEARCH.MATCH " \
+ "SEARCH.MATCH_TYPE MODE OWNER GROUP " \
+ "ACTION.RAW PKG.PUBLISHER\n"
-
+ o_results_no_pub = \
+ "file bin/example_path example_pkg " \
+ "pkg:/[email protected] bin/example_path " \
+ "basename 0555 root bin " \
+ "file 820157a2043e3135f342b238129b556aade20347
chash=bfa46fc98d1ca97f1260090797d35a35e76096a3 group=bin mode=0555 owner=root
path=bin/example_path pkg.csize=38 pkg.size=18\n"
+
+ o_results = o_results_no_pub.rstrip() + " test\n"
+
+
+ res_o_options_remote = set([o_headers, o_results])
+
+ res_o_options_local = set([o_headers, o_results_no_pub])
+
+ pkg_headers = "PKG.NAME PKG.SHORTFMRI PKG.PUBLISHER MODE"
+ pkg_results_no_pub = "example_pkg pkg:/[email protected]"
+ pkg_results = pkg_results_no_pub + " test"
+
+ res_pkg_options_remote = set([pkg_headers, pkg_results])
+ res_pkg_options_local = set([pkg_headers, pkg_results_no_pub])
+
+
def setUp(self):
for p in self.misc_files:
f = open(p, "w")
@@ -275,18 +300,24 @@
def _check(self, proposed_answer, correct_answer):
if correct_answer == proposed_answer:
return True
- else:
- print "Proposed Answer: " + str(proposed_answer)
- print "Correct Answer : " + str(correct_answer)
- if isinstance(correct_answer, set) and \
- isinstance(proposed_answer, set):
- print >> sys.stderr, "Missing: " + \
- str(correct_answer - proposed_answer)
- print >> sys.stderr, "Extra : " + \
- str(proposed_answer - correct_answer)
- self.assert_(correct_answer == proposed_answer)
+ if len(proposed_answer) == len(correct_answer) and \
+ sorted([p.strip().split() for p in proposed_answer]) == \
+ sorted([c.strip().split() for c in correct_answer]):
+ return True
+ print >> sys.stderr, "p:%s" % sorted([p.strip().split() for p
in proposed_answer])
+ print >> sys.stderr, "c:%s" % sorted([c.strip().split() for c
in correct_answer])
+ print "Proposed Answer: " + str(proposed_answer)
+ print "Correct Answer : " + str(correct_answer)
+ if isinstance(correct_answer, set) and \
+ isinstance(proposed_answer, set):
+ print >> sys.stderr, "Missing: " + \
+ str(correct_answer - proposed_answer)
+ print >> sys.stderr, "Extra : " + \
+ str(proposed_answer - correct_answer)
+ self.assert_(correct_answer == proposed_answer)
- def _search_op(self, remote, token, test_value, case_sensitive=False):
+ def _search_op(self, remote, token, test_value, case_sensitive=False,
+ return_actions=True):
outfile = os.path.join(self.testdata_dir, "res")
if remote:
token = "-r " + token
@@ -294,7 +325,11 @@
token = "-l " + token
if case_sensitive:
token = "-I " + token
- self.pkg("search -a " + token + " > " + outfile)
+ if return_actions:
+ token = "-a " + token
+ else:
+ token = "-p " + token
+ self.pkg("search " + token + " > " + outfile)
res_list = (open(outfile, "rb")).readlines()
self._check(set(res_list), test_value)
@@ -472,6 +507,24 @@
self.pkg("search -s httP://pkg.opensolaris.org bge")
self.pkg("search -s ftp://pkg.opensolaris.org:88 bge", exit=1)
+ # Testing interaction of -o and -p options
+ self.pkgsend_bulk(durl, self.example_pkg10)
+ self.pkg("search -o action.name -p pkg", exit=2)
+ self.pkg("search -o action.name '<pkg>'", exit=1)
+ self.pkg("search -o action.name '<example_path>'", exit=2)
+ self.pkg("search -o action.key -p pkg", exit=2)
+ self.pkg("search -o action.key '<pkg>'", exit=1)
+ self.pkg("search -o action.key '<example_path>'", exit=2)
+ self.pkg("search -o search.match -p pkg", exit=2)
+ self.pkg("search -o search.match '<pkg>'", exit=1)
+ self.pkg("search -o search.match '<example_path>'", exit=2)
+ self.pkg("search -o search.match_type -p pkg", exit=2)
+ self.pkg("search -o search.match_type '<pkg>'", exit=1)
+ self.pkg("search -o search.match_type '<example_path>'",
exit=2)
+ self.pkg("search -o action.foo pkg", exit=2)
+ self.pkg("search -o pkg.foo pkg", exit=2)
+ self.pkg("search -o search.foo -p pkg", exit=2)
+
def test_remote(self):
"""Test remote search."""
# Need to retain to check that default search does remote, not
@@ -552,7 +605,7 @@
set(self.res_bogus_number_result))
def test_bug_7835(self):
- """Check that installing a package without in a non-empty
+ """Check that installing a package in a non-empty
image without an index doesn't build an index."""
# This test can't be moved to t_api_search until bug 8497 has
# been resolved.
@@ -606,6 +659,54 @@
lambda x: x.code == 400, urllib2.urlopen,
"%s/search/1/False_2_None_None_foo%%20%%3Cbar%%3E" % durl)
+ def test_bug_10515(self):
+ """Check that -o and -H options work as expected."""
+
+ durl = self.dc.get_depot_url()
+ self.pkgsend_bulk(durl, self.example_pkg10)
+
+ self.image_create(durl)
+
+ o_options = "action.name,action.key,pkg.name,pkg.shortfmri," \
+ "search.match,search.match_type,mode,owner,group," \
+ "action.raw,pkg.publisher"
+
+ pkg_options = "-o pkg.name -o pkg.shortfmri -o pkg.publisher "
\
+ "-o mode"
+
+ self._search_op(True, "-o %s example_path" % o_options,
+ self.res_o_options_remote)
+ self._search_op(True, "-H -o %s example_path" % o_options,
+ [self.o_results])
+ self._search_op(True, "-s %s -o %s example_path" %
+ (durl, o_options), self.res_o_options_remote)
+
+ self._search_op(True, "-H -s "
+ "http://pkg.opensolaris.org/release -o pkg.publisher "
+ "/usr/bin/pkg", ["http://pkg.opensolaris.org/release/"])
+
+ self._search_op(True, "%s -p example_path" % pkg_options,
+ self.res_pkg_options_remote)
+ self._search_op(True, "%s '<example_path>'" % pkg_options,
+ self.res_pkg_options_remote)
+
+ self.pkg("install example_pkg")
+ self._search_op(False, "-o %s example_path" % o_options,
+ self.res_o_options_local)
+ self._search_op(False, "-H -o %s example_path" % o_options,
+ [self.o_results_no_pub])
+
+ self._search_op(False, "%s -p example_path" % pkg_options,
+ self.res_pkg_options_local)
+ self._search_op(False, "%s '<example_path>'" % pkg_options,
+ self.res_pkg_options_local)
+
+ id, tid = self._get_index_dirs()
+ shutil.rmtree(id)
+ self._search_op(False, "-o %s example_path" % o_options,
+ self.res_o_options_local)
+ self._search_op(False, "-H -o %s example_path" % o_options,
+ [self.o_results_no_pub])
if __name__ == "__main__":
unittest.main()
_______________________________________________
pkg-discuss mailing list
[email protected]
http://mail.opensolaris.org/mailman/listinfo/pkg-discuss