def assert_metadata_is(sid, orig_md, correct_dependent_md): result_md = self._assert_get_song(sid) with Check() as check: for name, expt in md_expectations.items(): if name in orig_md: # TODO really need to factor out to test_utils? # Check mutability if it's not volatile or dependent. if not expt.volatile and expt.depends_on is None: same, message = test_utils.md_entry_same(name, orig_md, result_md) check.equal(not expt.mutable, same, "metadata mutability incorrect: " + message) # Check dependent md. if expt.depends_on is not None: same, message = test_utils.md_entry_same(name, correct_dependent_md, result_md) check.true(same, "dependent metadata incorrect: " + message)
def assert_metadata_is(sid, orig_md, correct_dependent_md): result_md = self._assert_get_song(sid) with Check() as check: for name, expt in md_expectations.items(): if name in orig_md: #TODO really need to factor out to test_utils? #Check mutability if it's not volatile or dependent. if not expt.volatile and expt.depends_on is None: same, message = test_utils.md_entry_same(name, orig_md, result_md) check.equal(not expt.mutable, same, "metadata mutability incorrect: " + message) #Check dependent md. if expt.depends_on is not None: same, message = test_utils.md_entry_same( name, correct_dependent_md, result_md ) check.true(same, "dependent metadata incorrect: " + message)
from gmusicapi.compat import json from gmusicapi.exceptions import CallFailure, ValidationException from gmusicapi.protocol.metadata import md_expectations from gmusicapi.protocol.shared import Call, authtypes from gmusicapi.utils import utils, jsarray base_url = 'https://play.google.com/music/' service_url = base_url + 'services/' #Shared response schemas, built to include metadata expectations. song_schema = { "type": "object", "properties": dict((name, expt.get_schema()) for name, expt in md_expectations.items()), #don't allow metadata not in expectations "additionalProperties": False } song_array = {"type": "array", "items": song_schema} pl_schema = { "type": "object", "properties": { "continuation": { "type": "boolean" }, "playlist": song_array, "playlistId": {
from gmusicapi.compat import json from gmusicapi.exceptions import CallFailure, ValidationException from gmusicapi.protocol.metadata import md_expectations from gmusicapi.protocol.shared import Call, authtypes from gmusicapi.utils import utils base_url = 'https://play.google.com/music/' service_url = base_url + 'services/' #Shared response schemas, built to include metadata expectations. song_schema = { "type": "object", "properties": dict( (name, expt.get_schema()) for name, expt in md_expectations.items() ), #don't allow metadata not in expectations "additionalProperties": False } song_array = { "type": "array", "items": song_schema } pl_schema = { "type": "object", "properties": { "continuation": {"type": "boolean"}, "playlist": song_array,
def test_change_song_metadata(self): """Change a song's metadata, then restore it.""" #Get a random song's metadata. orig_md = [s for s in self.library if s["id"] == self.r_song_id][0] log.debug("original md: %s", repr(orig_md)) #Generate noticably changed metadata for ones we can change. #Changing immutable ones voids the request (although we get back success:True and our expected values). new_md = copy.deepcopy(orig_md) for name, expt in md_expectations.items(): if name in orig_md and expt.mutable: old_val = orig_md[name] new_val = test_utils.modify_md(name, old_val) log.debug("%s: %s modified to %s", name, repr(old_val), repr(new_val)) self.assertNotEqual(new_val, old_val) new_md[name] = new_val #Make the call to change the metadata. #This should succeed, even though we _shouldn't_ be able to change some entries. #The call only fails if you give the wrong datatype. self.api.change_song_metadata(new_md) #Recreate the dependent md to what they should be (based on how orig_md was changed) correct_dependent_md = {} for name, expt in md_expectations.items(): if expt.depends_on and name in orig_md: master_name = expt.depends_on correct_dependent_md[name] = expt.dependent_transformation(new_md[master_name]) # master_key, trans = dependent_md[name] # correct_dependent_md[dep_key] = trans(new_md[master_key]) log.debug("dependents (%s): %s -> %s", name, new_md[master_name], correct_dependent_md[name]) #The library needs to be refreshed to flush the changes. #This might not happen right away, so we allow a few retries. max_attempts = 3 sleep_for = 3 attempts = 0 success = False #TODO: this is cludgey, and should be pulled out with the below retry logic. while not success and attempts < max_attempts: time.sleep(sleep_for) self.library = self.api.get_all_songs() attempts += 1 result_md = [s for s in self.library if s["id"] == orig_md["id"]][0] log.debug("result md: %s", repr(result_md)) try: #Verify everything went as expected: for name, expt in md_expectations.items(): if name in orig_md: #Check mutability if it's not volatile or dependent. if not expt.volatile and expt.depends_on is None: same, message = test_utils.md_entry_same(name, orig_md, result_md) self.assertEqual(same, (not expt.mutable), "metadata mutability incorrect: " + message) #Check dependent md. if expt.depends_on is not None: same, message = test_utils.md_entry_same(name, correct_dependent_md, result_md) self.assertTrue(same, "dependent metadata incorrect: " + message) except AssertionError: log.info("retrying server for changed metadata") if not attempts < max_attempts: raise else: success = True #Revert the metadata. self.api.change_song_metadata(orig_md) #Verify everything is as it was. attempts = 0 success = False while not success and attempts < max_attempts: time.sleep(sleep_for) self.library = self.api.get_all_songs() attempts += 1 result_md = [s for s in self.library if s["id"] == orig_md["id"]][0] log.debug("result md: %s", repr(result_md)) try: for name in orig_md: #If it's not volatile, it should be back to what it was. if not md_expectations[name].volatile: same, message = test_utils.md_entry_same(name, orig_md, result_md) self.assertTrue(same, "failed to revert: " + message) except AssertionError: log.info("retrying server for reverted metadata") if not attempts < max_attempts: raise else: success = True
def change_metadata(self): orig_md = self._assert_get_song(self.song.sid) # Change all mutable entries. new_md = copy(orig_md) for name, expt in md_expectations.items(): if name in orig_md and expt.mutable: old_val = orig_md[name] new_val = test_utils.modify_md(name, old_val) assert_not_equal(new_val, old_val) new_md[name] = new_val #TODO check into attempting to mutate non mutables self.wc.change_song_metadata(new_md) #Recreate the dependent md to what they should be (based on how orig_md was changed) correct_dependent_md = {} for name, expt in md_expectations.items(): if expt.depends_on and name in orig_md: master_name = expt.depends_on correct_dependent_md[name] = expt.dependent_transformation( new_md[master_name]) @retry def assert_metadata_is(sid, orig_md, correct_dependent_md): result_md = self._assert_get_song(sid) with Check() as check: for name, expt in md_expectations.items(): if name in orig_md: #TODO really need to factor out to test_utils? #Check mutability if it's not volatile or dependent. if not expt.volatile and expt.depends_on is None: same, message = test_utils.md_entry_same( name, orig_md, result_md) check.equal( not expt.mutable, same, "metadata mutability incorrect: " + message) #Check dependent md. if expt.depends_on is not None: same, message = test_utils.md_entry_same( name, correct_dependent_md, result_md) check.true( same, "dependent metadata incorrect: " + message) assert_metadata_is(self.song.sid, orig_md, correct_dependent_md) #Revert the metadata. self.wc.change_song_metadata(orig_md) @retry def assert_metadata_reverted(sid, orig_md): result_md = self._assert_get_song(sid) with Check() as check: for name in orig_md: #If it's not volatile, it should be back to what it was. if not md_expectations[name].volatile: same, message = test_utils.md_entry_same( name, orig_md, result_md) check.true(same, "failed to revert: " + message) assert_metadata_reverted(self.song.sid, orig_md)
def change_metadata(self): orig_md = self._assert_get_song(self.song.sid) # Change all mutable entries. new_md = copy(orig_md) for name, expt in md_expectations.items(): if name in orig_md and expt.mutable: old_val = orig_md[name] new_val = test_utils.modify_md(name, old_val) assert_not_equal(new_val, old_val) new_md[name] = new_val # TODO check into attempting to mutate non mutables self.wc.change_song_metadata(new_md) # Recreate the dependent md to what they should be (based on how orig_md was changed) correct_dependent_md = {} for name, expt in md_expectations.items(): if expt.depends_on and name in orig_md: master_name = expt.depends_on correct_dependent_md[name] = expt.dependent_transformation(new_md[master_name]) @retry def assert_metadata_is(sid, orig_md, correct_dependent_md): result_md = self._assert_get_song(sid) with Check() as check: for name, expt in md_expectations.items(): if name in orig_md: # TODO really need to factor out to test_utils? # Check mutability if it's not volatile or dependent. if not expt.volatile and expt.depends_on is None: same, message = test_utils.md_entry_same(name, orig_md, result_md) check.equal(not expt.mutable, same, "metadata mutability incorrect: " + message) # Check dependent md. if expt.depends_on is not None: same, message = test_utils.md_entry_same(name, correct_dependent_md, result_md) check.true(same, "dependent metadata incorrect: " + message) assert_metadata_is(self.song.sid, orig_md, correct_dependent_md) # Revert the metadata. self.wc.change_song_metadata(orig_md) @retry def assert_metadata_reverted(sid, orig_md): result_md = self._assert_get_song(sid) with Check() as check: for name in orig_md: # If it's not volatile, it should be back to what it was. if not md_expectations[name].volatile: same, message = test_utils.md_entry_same(name, orig_md, result_md) check.true(same, "failed to revert: " + message) assert_metadata_reverted(self.song.sid, orig_md)
import validictory from gmusicapi.compat import json from gmusicapi.exceptions import CallFailure, ValidationException from gmusicapi.protocol.metadata import md_expectations from gmusicapi.protocol.shared import Call, authtypes from gmusicapi.utils import utils, jsarray base_url = "https://play.google.com/music/" service_url = base_url + "services/" # Shared response schemas, built to include metadata expectations. song_schema = { "type": "object", "properties": dict((name, expt.get_schema()) for name, expt in md_expectations.items()), # don't allow metadata not in expectations "additionalProperties": False, } song_array = {"type": "array", "items": song_schema} pl_schema = { "type": "object", "properties": { "continuation": {"type": "boolean"}, "playlist": song_array, "playlistId": {"type": "string"}, "unavailableTrackCount": {"type": "integer"}, # unsure what this field does. sometimes it's not there. "token": {"type": "string", "required": False},
def test_change_song_metadata(self): """Change a song's metadata, then restore it.""" #Get a random song's metadata. orig_md = [s for s in self.library if s["id"] == self.r_song_id][0] log.debug("original md: %s", repr(orig_md)) #Generate noticably changed metadata for ones we can change. #Changing immutable ones voids the request (although we get back success:True and our expected values). new_md = copy.deepcopy(orig_md) for name, expt in md_expectations.items(): if name in orig_md and expt.mutable: old_val = orig_md[name] new_val = test_utils.modify_md(name, old_val) log.debug("%s: %s modified to %s", name, repr(old_val), repr(new_val)) self.assertNotEqual(new_val, old_val) new_md[name] = new_val #Make the call to change the metadata. #This should succeed, even though we _shouldn't_ be able to change some entries. #The call only fails if you give the wrong datatype. self.api.change_song_metadata(new_md) #Recreate the dependent md to what they should be (based on how orig_md was changed) correct_dependent_md = {} for name, expt in md_expectations.items(): if expt.depends_on and name in orig_md: master_name = expt.depends_on correct_dependent_md[name] = expt.dependent_transformation( new_md[master_name]) # master_key, trans = dependent_md[name] # correct_dependent_md[dep_key] = trans(new_md[master_key]) log.debug("dependents (%s): %s -> %s", name, new_md[master_name], correct_dependent_md[name]) #The library needs to be refreshed to flush the changes. #This might not happen right away, so we allow a few retries. max_attempts = 3 sleep_for = 3 attempts = 0 success = False #TODO: this is cludgey, and should be pulled out with the below retry logic. while not success and attempts < max_attempts: time.sleep(sleep_for) self.library = self.api.get_all_songs() attempts += 1 result_md = [s for s in self.library if s["id"] == orig_md["id"]][0] log.debug("result md: %s", repr(result_md)) try: #Verify everything went as expected: for name, expt in md_expectations.items(): if name in orig_md: #Check mutability if it's not volatile or dependent. if not expt.volatile and expt.depends_on is None: same, message = test_utils.md_entry_same( name, orig_md, result_md) self.assertEqual( same, (not expt.mutable), "metadata mutability incorrect: " + message) #Check dependent md. if expt.depends_on is not None: same, message = test_utils.md_entry_same( name, correct_dependent_md, result_md) self.assertTrue( same, "dependent metadata incorrect: " + message) except AssertionError: log.info("retrying server for changed metadata") if not attempts < max_attempts: raise else: success = True #Revert the metadata. self.api.change_song_metadata(orig_md) #Verify everything is as it was. attempts = 0 success = False while not success and attempts < max_attempts: time.sleep(sleep_for) self.library = self.api.get_all_songs() attempts += 1 result_md = [s for s in self.library if s["id"] == orig_md["id"]][0] log.debug("result md: %s", repr(result_md)) try: for name in orig_md: #If it's not volatile, it should be back to what it was. if not md_expectations[name].volatile: same, message = test_utils.md_entry_same( name, orig_md, result_md) self.assertTrue(same, "failed to revert: " + message) except AssertionError: log.info("retrying server for reverted metadata") if not attempts < max_attempts: raise else: success = True