def test_tree_concurrent_timeout(self, monkeypatch): # Much shorter timeout monkeypatch.setattr('fmf.utils.NODE_LOCK_TIMEOUT', 2) def long_fetch(*args, **kwargs): # Longer than timeout time.sleep(7) return EXAMPLES # Patch fetch to sleep and later return tmpdir path monkeypatch.setattr('fmf.utils.fetch_repo', long_fetch) # Background thread to get node() acquiring lock def target(): Tree.node({ 'url': 'localhost', 'name': '/', }) thread = threading.Thread(target=target) thread.start() # Small sleep to mitigate race time.sleep(2) # "Real" fetch shouldn't get the lock with pytest.raises(utils.GeneralError): Tree.node({ 'url': 'localhost', 'name': '/', }) # Wait on parallel thread to finish thread.join()
def test_scatter(self): """ Scattered files """ scatter = Tree(EXAMPLES + "scatter").find("/object") assert (len(list(scatter.climb())) == 1) assert (scatter.data['one'] == 1) assert (scatter.data['two'] == 2) assert (scatter.data['three'] == 3)
def test_deep_dictionary(self): """ Get value from a deep dictionary """ deep = Tree(EXAMPLES + "deep") assert deep.data['hardware']['memory']['size'] == 8 assert deep.get(['hardware', 'memory', 'size']) == 8 assert deep.get(['hardware', 'bad', 'size'], 12) == 12 assert deep.get('nonexistent', default=3) == 3
def test_tree_node_remote(self): reference = { 'url': FMF_REPO, 'ref': '0.10', 'path': 'examples/deep', 'name': '/one/two/three', } # Values of test in 0.10 tag expected_data = { 'hardware': { 'memory': { 'size': 8 }, 'network': { 'model': 'e1000' } }, 'key': 'value' } # Full identifier node = Tree.node(reference) assert (node.get() == expected_data) # Default ref reference.pop('ref') node = Tree.node(reference) assert (node.get() == expected_data) # Raise exception for invalid tree nodes with pytest.raises(utils.ReferenceError): reference['name'] = 'not_existing_name_' node = Tree.node(reference)
def test_deep_modify(self): """ Deep structures """ requirements = self.wget.find('/requirements') protocols = self.wget.find('/requirements/protocols') ftp = self.wget.find('/requirements/protocols/ftp') # Modify data and store to disk with requirements as data: data['new'] = 'some' with protocols as data: data.update(dict(coverage="changed", new_attr="val")) with ftp as data: data['server'] = 'vsftpd' # Reload the data and verify self.wget = Tree(self.tempdir) requirements = self.wget.find('/requirements') protocols = self.wget.find('/requirements/protocols') ftp = self.wget.find('/requirements/protocols/ftp') self.assertEqual(requirements.data["new"], "some") self.assertEqual(protocols.data["new"], "some") self.assertEqual(ftp.data["new"], "some") self.assertNotIn("server", protocols.data) self.assertIn("server", ftp.data) self.assertNotIn("new_attr", requirements.data) self.assertIn("new_attr", protocols.data) self.assertIn("new_attr", ftp.data) self.assertEqual(protocols.data["coverage"], "changed") self.assertIn('adjust', ftp.data) self.assertEqual(ftp.data['adjust'][0]['enabled'], False) self.assertEqual(ftp.data['adjust'][0]['when'], "arch != x86_64")
def test_modify_empty(self): """ Nodes with no content should be handled as an empty dict """ with self.wget.find('/download/requirements/spider') as data: data['x'] = 1 self.wget = Tree(self.tempdir) node = self.wget.find('/download/requirements/spider') self.assertEqual(node.data['x'], 1)
def test_basic(self): """ No directory path given """ with pytest.raises(utils.GeneralError): Tree("") with pytest.raises(utils.GeneralError): Tree(None) with pytest.raises(utils.RootError): Tree("/")
def test_modify_unicode(self): """ Ensure that unicode characters are properly handled """ path = os.path.join(self.tempdir, 'unicode.fmf') with io.open(path, 'w', encoding='utf-8') as file: file.write('jméno: Leoš') with Tree(self.tempdir).find('/unicode') as data: data['příjmení'] = 'Janáček' reloaded = Tree(self.tempdir).find('/unicode') assert reloaded.get('jméno') == 'Leoš' assert reloaded.get('příjmení') == 'Janáček'
def test_modify_clear(self): """ Clear node data """ item = '/requirements/protocols/ftp' with self.wget.find(item) as data: data.clear() self.wget = Tree(self.tempdir) node = self.wget.find(item) self.assertNotIn('coverage', node.data) self.assertIn('tester', node.data) self.assertNotIn('requirement', node.data)
def test_deep_hierarchy(self): """ Multiple virtual hierarchy levels shortcut """ with open(os.path.join(self.tempdir, 'deep.fmf'), 'w') as file: file.write('/one/two/three:\n x: 1\n') deep = Tree(self.tempdir).find('/deep/one/two/three') with deep as data: data['y'] = 2 deep = Tree(self.tempdir).find('/deep/one/two/three') self.assertEqual(deep.get('x'), 1) self.assertEqual(deep.get('y'), 2)
def test_modify_pop(self): """ Pop elements from node data """ item = '/requirements/protocols/ftp' with self.wget.find(item) as data: data.pop('coverage') data.pop('tester+') self.wget = Tree(self.tempdir) node = self.wget.find(item) self.assertNotIn('coverage', node.data) self.assertIn('tester', node.data) self.assertIn('requirement', node.data)
def test_context_manager(self): """ Use context manager to save node data """ item = '/requirements/protocols/ftp' with self.wget.find(item) as data: data.pop("coverage") data.pop("tester+") data.update(dict(server="vsftpd")) self.wget = Tree(self.tempdir) node = self.wget.find(item) self.assertNotIn('coverage', node.data) self.assertIn('tester', node.data) self.assertIn('requirement', node.data) self.assertIn("server", node.data)
def test_tree_commit(self, tmpdir): # Tag node = Tree.node(dict(url=FMF_REPO, ref='0.12')) assert node.commit == '6570aa5f10729991625d74036473a71f384d745b' # Hash node = Tree.node(dict(url=FMF_REPO, ref='fa05dd9')) assert 'fa05dd9' in node.commit assert 'fa05dd9' in node.commit # return already detected value # Data node = Tree(dict(x=1)) assert node.commit is False # No git repository tree = Tree(Tree.init(str(tmpdir))) assert tree.commit is False
def test_tree_node_local(self): reference = { 'path': EXAMPLES + 'wget', 'name': '/protocols/https', } node = Tree.node(reference) assert node.get('time') == '1 min'
def test_scattered_inheritance(self): """ Inheritance of scattered files """ grandson = Tree(EXAMPLES + "child").find("/son/grandson") assert (grandson.data['name'] == 'Hugo') assert (grandson.data['eyes'] == 'blue') assert (grandson.data['nose'] == 'long') assert (grandson.data['hair'] == 'fair')
def test_inheritance(self): """ Inheritance and data types """ item = self.wget.find('/recursion/deep') item_parent = self.wget.find('/recursion') # Modify data and store to disk with item as data: data.update(dict(depth=2000, new="two\nlines")) with item.parent as data: data.update(dict(parent_attr="value")) # Reload the data and verify self.wget = Tree(self.tempdir) item = self.wget.find('/recursion/deep') self.assertEqual(item.data['tags'], ['Tier2']) self.assertEqual(item.parent.data['tags'], ['Tier2']) self.assertEqual(item.data['depth'], 2000) self.assertIn('depth', item.data) self.assertNotIn('depth', item.parent.data) self.assertEqual(item.data['new'], "two\nlines") self.assertEqual(item.data['parent_attr'], "value") with open(os.path.join(self.tempdir, 'recursion/deep.fmf')) as file: self.assertTrue(re.search('two\n +lines', file.read()))
def test_modify_after_adjust(self): """ Preserve original data even when adjust is used """ item = '/requirements/protocols/ftp' wget = Tree(self.tempdir) # Expects new attribute + original data expected = {'new_attr': "new_value"} expected.update(wget.find(item).copy().get()) wget.adjust(Context(arch='ppc64le')) with wget.find(item) as data: data['new_attr'] = "new_value" assert expected == Tree(self.tempdir).find(item).get()
def test_inaccessible_directories(self): """ Inaccessible directories should be silently ignored """ directory = tempfile.mkdtemp() accessible = os.path.join(directory, 'accessible') inaccessible = os.path.join(directory, 'inaccessible') os.mkdir(accessible, 511) os.mkdir(inaccessible, 000) with open(os.path.join(accessible, 'main.fmf'), 'w') as main: main.write('key: value\n') Tree.init(directory) tree = Tree(directory) assert tree.find('/accessible').get('key') == 'value' assert tree.find('/inaccessible') is None os.chmod(inaccessible, 511) rmtree(directory)
def setUp(self): self.wget_path = EXAMPLES + "wget" self.tempdir = tempfile.mktemp() copytree(self.wget_path, self.tempdir) self.wget = Tree(self.tempdir)
class TestModify(unittest.TestCase): """ Verify storing modifed data to disk """ def setUp(self): self.wget_path = EXAMPLES + "wget" self.tempdir = tempfile.mktemp() copytree(self.wget_path, self.tempdir) self.wget = Tree(self.tempdir) def tearDown(self): rmtree(self.tempdir) def test_inheritance(self): """ Inheritance and data types """ item = self.wget.find('/recursion/deep') item_parent = self.wget.find('/recursion') # Modify data and store to disk with item as data: data.update(dict(depth=2000, new="two\nlines")) with item.parent as data: data.update(dict(parent_attr="value")) # Reload the data and verify self.wget = Tree(self.tempdir) item = self.wget.find('/recursion/deep') self.assertEqual(item.data['tags'], ['Tier2']) self.assertEqual(item.parent.data['tags'], ['Tier2']) self.assertEqual(item.data['depth'], 2000) self.assertIn('depth', item.data) self.assertNotIn('depth', item.parent.data) self.assertEqual(item.data['new'], "two\nlines") self.assertEqual(item.data['parent_attr'], "value") with open(os.path.join(self.tempdir, 'recursion/deep.fmf')) as file: self.assertTrue(re.search('two\n +lines', file.read())) def test_deep_modify(self): """ Deep structures """ requirements = self.wget.find('/requirements') protocols = self.wget.find('/requirements/protocols') ftp = self.wget.find('/requirements/protocols/ftp') # Modify data and store to disk with requirements as data: data['new'] = 'some' with protocols as data: data.update(dict(coverage="changed", new_attr="val")) with ftp as data: data['server'] = 'vsftpd' # Reload the data and verify self.wget = Tree(self.tempdir) requirements = self.wget.find('/requirements') protocols = self.wget.find('/requirements/protocols') ftp = self.wget.find('/requirements/protocols/ftp') self.assertEqual(requirements.data["new"], "some") self.assertEqual(protocols.data["new"], "some") self.assertEqual(ftp.data["new"], "some") self.assertNotIn("server", protocols.data) self.assertIn("server", ftp.data) self.assertNotIn("new_attr", requirements.data) self.assertIn("new_attr", protocols.data) self.assertIn("new_attr", ftp.data) self.assertEqual(protocols.data["coverage"], "changed") self.assertIn('adjust', ftp.data) self.assertEqual(ftp.data['adjust'][0]['enabled'], False) self.assertEqual(ftp.data['adjust'][0]['when'], "arch != x86_64") def test_deep_hierarchy(self): """ Multiple virtual hierarchy levels shortcut """ with open(os.path.join(self.tempdir, 'deep.fmf'), 'w') as file: file.write('/one/two/three:\n x: 1\n') deep = Tree(self.tempdir).find('/deep/one/two/three') with deep as data: data['y'] = 2 deep = Tree(self.tempdir).find('/deep/one/two/three') self.assertEqual(deep.get('x'), 1) self.assertEqual(deep.get('y'), 2) def test_modify_empty(self): """ Nodes with no content should be handled as an empty dict """ with self.wget.find('/download/requirements/spider') as data: data['x'] = 1 self.wget = Tree(self.tempdir) node = self.wget.find('/download/requirements/spider') self.assertEqual(node.data['x'], 1) def test_modify_pop(self): """ Pop elements from node data """ item = '/requirements/protocols/ftp' with self.wget.find(item) as data: data.pop('coverage') data.pop('tester+') self.wget = Tree(self.tempdir) node = self.wget.find(item) self.assertNotIn('coverage', node.data) self.assertIn('tester', node.data) self.assertIn('requirement', node.data) def test_modify_clear(self): """ Clear node data """ item = '/requirements/protocols/ftp' with self.wget.find(item) as data: data.clear() self.wget = Tree(self.tempdir) node = self.wget.find(item) self.assertNotIn('coverage', node.data) self.assertIn('tester', node.data) self.assertNotIn('requirement', node.data) def test_modify_unsupported_method(self): """ Raise error for trees initialized from a dict """ with pytest.raises(GeneralError, match='No raw data'): with Tree(dict(x=1)) as data: data['y'] = 2 def test_context_manager(self): """ try to use context manager for save node data """ item = '/requirements/protocols/ftp' with self.wget.find(item) as data: data.pop("coverage") data.pop("tester+") data.update(dict(server="vsftpd")) self.wget = Tree(self.tempdir) node = self.wget.find(item) self.assertNotIn('coverage', node.data) self.assertIn('tester', node.data) self.assertIn('requirement', node.data) self.assertIn("server", node.data)
def test_modify_unsupported_method(self): """ Raise error for trees initialized from a dict """ with pytest.raises(GeneralError, match='No raw data'): with Tree(dict(x=1)) as data: data['y'] = 2
class TestTree(object): """ Tree class """ def setup_method(self, method): """ Load examples """ self.wget = Tree(EXAMPLES + "wget") self.merge = Tree(EXAMPLES + "merge") def test_basic(self): """ No directory path given """ with pytest.raises(utils.GeneralError): Tree("") with pytest.raises(utils.GeneralError): Tree(None) with pytest.raises(utils.RootError): Tree("/") def test_hidden(self): """ Hidden files and directories """ assert (".hidden" not in self.wget.children) def test_inheritance(self): """ Inheritance and data types """ deep = self.wget.find('/recursion/deep') assert (deep.data['depth'] == 1000) assert (deep.data['description'] == 'Check recursive download options') assert (deep.data['tags'] == ['Tier2']) def test_scatter(self): """ Scattered files """ scatter = Tree(EXAMPLES + "scatter").find("/object") assert (len(list(scatter.climb())) == 1) assert (scatter.data['one'] == 1) assert (scatter.data['two'] == 2) assert (scatter.data['three'] == 3) def test_scattered_inheritance(self): """ Inheritance of scattered files """ grandson = Tree(EXAMPLES + "child").find("/son/grandson") assert (grandson.data['name'] == 'Hugo') assert (grandson.data['eyes'] == 'blue') assert (grandson.data['nose'] == 'long') assert (grandson.data['hair'] == 'fair') def test_subtrees(self): """ Subtrees should be ignored """ child = Tree(EXAMPLES + "child") assert child.find("/nobody") is None def test_empty(self): """ Empty structures should be ignored """ child = Tree(EXAMPLES + "empty") assert child.find("/nothing") is None assert child.find("/zero") is None def test_none_key(self): """ Handle None keys """ with pytest.raises(utils.FormatError): tree = Tree({None: "weird key"}) def test_deep_hierarchy(self): """ Deep hierarchy on one line """ deep = Tree(EXAMPLES + "deep") assert len(deep.children) == 1 def test_deep_dictionary(self): """ Get value from a deep dictionary """ deep = Tree(EXAMPLES + "deep") assert deep.data['hardware']['memory']['size'] == 8 assert deep.get(['hardware', 'memory', 'size']) == 8 assert deep.get(['hardware', 'bad', 'size'], 12) == 12 assert deep.get('nonexistent', default=3) == 3 def test_merge_plus(self): """ Extending attributes using the '+' suffix """ child = self.merge.find('/parent/extended') assert ('General' in child.data['description']) assert ('Specific' in child.data['description']) assert (child.data['tags'] == ['Tier1', 'Tier2', 'Tier3']) assert (child.data['time'] == 15) assert (child.data['vars'] == dict(x=1, y=2, z=3)) assert (child.data['disabled'] == True) assert ('time+' not in child.data) with pytest.raises(utils.MergeError): child.data["time+"] = "string" child.inherit() def test_merge_minus(self): """ Reducing attributes using the '-' suffix """ child = self.merge.find('/parent/reduced') assert ('General' in child.data['description']) assert ('description' not in child.data['description']) assert (child.data['tags'] == ['Tier1']) assert (child.data['time'] == 5) assert (child.data['vars'] == dict(x=1, y=2)) assert ('time+' not in child.data) with pytest.raises(utils.MergeError): child.data["disabled-"] = True child.inherit() child.data.pop('disabled-') with pytest.raises(utils.MergeError): child.data["time-"] = "bad" child.inherit() def test_get(self): """ Get attributes """ assert (isinstance(self.wget.get(), dict)) assert ('Petr' in self.wget.get('tester')) def test_show(self): """ Show metadata """ assert (isinstance(self.wget.show(brief=True), type(""))) assert (self.wget.show(brief=True).endswith("\n")) assert (isinstance(self.wget.show(), type(""))) assert (self.wget.show().endswith("\n")) assert ('tester' in self.wget.show()) def test_update(self): """ Update data """ data = self.wget.get() self.wget.update(None) assert (self.wget.data == data) def test_find_node(self): """ Find node by name """ assert (self.wget.find("non-existent") == None) protocols = self.wget.find('/protocols') assert (isinstance(protocols, Tree)) def test_find_root(self): """ Find metadata tree root """ tree = Tree(os.path.join(EXAMPLES, "wget", "protocols")) assert (tree.find("/download/test")) def test_yaml_syntax_errors(self): """ Handle YAML syntax errors """ path = tempfile.mkdtemp() fmf.cli.main("fmf init", path) with open(os.path.join(path, "main.fmf"), "w") as main: main.write("missing\ncolon:") with pytest.raises(utils.FileError): tree = fmf.Tree(path) rmtree(path)
def setUp(self): self.wget_path = EXAMPLES + "wget" self.wget = Tree(self.wget_path)
def target(): Tree.node({ 'url': 'localhost', 'name': '/', })
def test_deep_hierarchy(self): """ Deep hierarchy on one line """ deep = Tree(EXAMPLES + "deep") assert len(deep.children) == 1
def test_none_key(self): """ Handle None keys """ with pytest.raises(utils.FormatError): tree = Tree({None: "weird key"})
def test_empty(self): """ Empty structures should be ignored """ child = Tree(EXAMPLES + "empty") assert child.find("/nothing") is None assert child.find("/zero") is None
def test_find_root(self): """ Find metadata tree root """ tree = Tree(os.path.join(EXAMPLES, "wget", "protocols")) assert (tree.find("/download/test"))
def test_subtrees(self): """ Subtrees should be ignored """ child = Tree(EXAMPLES + "child") assert child.find("/nobody") is None
def setup_method(self, method): """ Load examples """ self.wget = Tree(EXAMPLES + "wget") self.merge = Tree(EXAMPLES + "merge")