def test_mode_bit(tmp_path, bit, name): path = tmp_path / "file" path.touch() quoted_path = util.quote(str(path)) query = f"<path({quoted_path}).<file.<mode.<{name}" assert util.sq(query) == False path.chmod(stat.S_IMODE(path.stat().st_mode) | bit) assert util.sq(query) == True
def test_schema_to_primitive(): assert util.sq('<schema.<types[="SqRoot"]') == ["SqRoot"] assert util.sq('<schema.<primitive_types[="PrimitiveInt"]') == [ "PrimitiveInt" ] assert util.sq('<schema.<types[="SqRoot"].<fields[="schema"]') == [ ["schema"] ] assert util.sq( '<schema.<types[="SqRoot"].<fields[="int"].<params[="value"]' ) == [[["value"]]]
def test_hard_link_count(tmp_path): path = tmp_path / "file" quoted_path = util.quote(str(path)) path.touch() assert util.sq(f"<path({quoted_path}).<file.<hard_link_count") == 1 # Note: pathlib.Path.link_to() will be deprecated in Python 3.9 due to the # strange argument order (opposite to pathlib.Path.symlink_to()) but we # won't have the replacement, pathlib.Path.hardlink_to() until Python 3.9. # See https://bugs.python.org/issue39291 and # https://bugs.python.org/issue39950 path.link_to(tmp_path / "file2") assert util.sq(f"<path({quoted_path}).<file.<hard_link_count") == 2 path.link_to(tmp_path / "file3") assert util.sq(f"<path({quoted_path}).<file.<hard_link_count") == 3
def test_children(tmp_path, recurse, follow_symlinks): children = [tmp_path / f for f in ("f1", "x", "achild")] for child in children: child.touch() subdir = tmp_path / "a_subdir" subdir.mkdir() children.append(subdir) subchildren = [subdir / f for f in ("f2", "x2", "achild2")] for child in subchildren: child.touch() subsubdir = subdir / "another_subdir" subsubdir.mkdir() subchildren.append(subsubdir) link = tmp_path / "a_link" link.symlink_to(subdir) children.append(link) expected = children if recurse: expected.extend(subchildren) if (follow_symlinks): expected.extend( [link / f.relative_to(subdir) for f in subchildren]) expected = sorted([str(f) for f in expected]) recurse_str = f"recurse={util.bool_str(recurse)}" follow_str = f"follow_symlinks={util.bool_str(follow_symlinks)}" result = sorted( util.sq(f"<path.<children({recurse_str},{follow_str})", cwd=tmp_path)) assert result == expected
def test_canonical(tmp_path, path_info): path = path_info["path"] quoted_path = util.quote(path) canonical_path = (tmp_path / path).resolve() canonical_path.parent.mkdir(parents=True, exist_ok=True) canonical_path.touch(exist_ok=True) result = util.sq(f"<path({quoted_path}).<canonical", cwd=tmp_path) assert result == str(canonical_path)
def test_permissions(tmp_path): path = tmp_path / "file" path.touch() quoted_path = util.quote(str(path)) expected = path.stat().st_mode & (stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) assert util.sq( f"<path({quoted_path}).<file.<mode.<permissions") == expected
def test_time(tmp_path): quoted_path = util.quote(str(tmp_path)) result = util.sq(f"<path({quoted_path}).<file {{ atime mtime ctime }}") stat = tmp_path.stat() # We can't check for an exact atime value because the value might change # between accesses. Also, try not to underflow if atime is zero. assert result["atime"] + 10 > stat.st_atime assert result["atime"] < stat.st_atime + 10 assert result["mtime"] == math.floor(stat.st_mtime) assert result["ctime"] == math.floor(stat.st_ctime)
def test_absolute(path_info): path = path_info["path"] quoted_path = util.quote(path) absolute_path = path_info["path"] if not path_info["is_absolute"]: absolute_path = f"{pathlib.Path.cwd()}/{path}" result = util.sq(f"<path({quoted_path}).<absolute") assert result == absolute_path
def test_path(path_info): for attr in ( "parent", "filename", "stem", "extension", "parts", "is_absolute", ): quoted_path = util.quote(path_info["path"]) result = util.sq(f"<path({quoted_path}).<{attr}") assert result == path_info[attr]
def test_exists(tmp_path, symlink, follow_symlinks, exists): path = tmp_path / "file" if exists: path.touch() if symlink: link = tmp_path / "link" link.symlink_to(path) path = link follow_symlinks_param = "true" if follow_symlinks else "false" quoted_path = util.quote(str(path)) query = f"<path({quoted_path}).<exists({follow_symlinks_param})" assert util.sq(query) == (exists or (symlink and not follow_symlinks))
def test_file(tmp_path, symlink, follow_symlinks): path = tmp_path / "file" path.touch() expected = path.stat().st_ino if symlink: link = tmp_path / "link" link.symlink_to(path) path = link if not follow_symlinks: expected = path.lstat().st_ino follow_symlinks_param = "true" if follow_symlinks else "false" quoted_path = util.quote(str(path)) query = f"<path({quoted_path}).<file({follow_symlinks_param})" assert util.sq(query) == expected
def test_schema(sq_schema): # We're going to test that the schema returned by SQ is (mostly) the same # as the original schema in schema.json. # # There are a couple of things that will be different between the two # schemas though: # * doc arrays will have been converted to single strings with newlines. # * optional fields will always exist but might be null. # # Modify the schema we got from schema.json to match what we think SQ # should return, then just do a test using "==" schema = copy.deepcopy(sq_schema) for t in schema["types"]: flatten_doc_list(t) for f in t["fields"]: flatten_doc_list(f) for p in f["params"]: flatten_doc_list(p) if "default_value" not in p: p["default_value"] = None if "default_value_doc" not in p: p["default_value_doc"] = None result = util.sq( "schema {" "types {" "name doc fields { " "name doc return_type return_list null params {" "name doc index type required " "default_value default_value_doc" "}" "}" "}" " primitive_types { name doc }" " root_type" "}" ) assert result == {"schema": schema}
def test_data_size_units(exponent, unit, base, data_size): size_in_units = data_size / (base**exponent) result = util.sq(f"<data_size({data_size}).<{unit}") assert math.isclose(result, size_in_units)
def test_shell(tmp_path): quoted_path = util.quote(str(tmp_path)) expected = pwd.getpwuid(tmp_path.stat().st_uid).pw_shell result = util.sq(f"<path({quoted_path}).<file.<user.<shell") assert result == expected
def test_name(tmp_path): quoted_path = util.quote(str(tmp_path)) expected = pwd.getpwuid(tmp_path.stat().st_uid).pw_gecos.split(',')[0] result = util.sq(f"<path({quoted_path}).<file.<user.<name") assert result == expected
def test_uid(tmp_path): quoted_path = util.quote(str(tmp_path)) expected = tmp_path.stat().st_uid result = util.sq(f"<path({quoted_path}).<file.<user.<uid") assert result == expected
def test_simple(query, result): assert util.sq(query) == result
def test_members(tmp_path): quoted_path = util.quote(str(tmp_path)) expected = grp.getgrgid(tmp_path.stat().st_gid).gr_mem result = util.sq(f"<path({quoted_path}).<file.<group.<members") assert result == expected
def test_type(tmp_path, file_type): path = tmp_path / "file" create_file_of_type(path, file_type) quoted_path = util.quote(str(path)) query = f"<path({quoted_path}).<file(false).<type" assert util.sq(query) == file_type
def test_block_count(tmp_path): quoted_path = util.quote(str(tmp_path)) result = util.sq(f"<path({quoted_path}).<file.<block_count") assert result == tmp_path.stat().st_blocks
def test_inode(tmp_path): path = tmp_path / "file" path.touch() quoted_path = util.quote(str(path)) assert util.sq(f"<path({quoted_path}).<file.<inode") == path.stat().st_ino
def test_group(tmp_path): quoted_path = util.quote(str(tmp_path)) result = util.sq(f"<path({quoted_path}).<file.<group") assert result == tmp_path.stat().st_gid
def test_string(path): result = util.sq(f"<path({util.quote(path)}).<string") assert result == path
def test_user(tmp_path): quoted_path = util.quote(str(tmp_path)) result = util.sq(f"<path({quoted_path}).<file.<user") assert result == tmp_path.stat().st_uid
def test_mode(tmp_path): path = tmp_path / "file" path.touch() quoted_path = util.quote(str(path)) assert util.sq(f"<path({quoted_path}).<file.<mode") == stat.S_IMODE( path.stat().st_mode)