Skip to content

Commit

Permalink
WIP: Refactor conformance tests for firestore.
Browse files Browse the repository at this point in the history
See #6290.

Use 'pytest.mark.parametrize' to create a testcase per textproto file.

Note that a *bunch* of them fail. :(
  • Loading branch information
tseaver committed Oct 23, 2018
1 parent 6baadd8 commit ea5b22f
Showing 1 changed file with 159 additions and 107 deletions.
266 changes: 159 additions & 107 deletions firestore/tests/unit/test_cross_language.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,117 +19,169 @@
import unittest

import mock
from google.cloud.firestore_v1beta1.proto import test_pb2
import pytest

from google.protobuf import text_format
from google.cloud.firestore_v1beta1.proto import firestore_pb2
from google.cloud.firestore_v1beta1.proto import test_pb2
from google.cloud.firestore_v1beta1.proto import write_pb2


def _load_testproto(filename):

with open(filename, 'r') as tp_file:
tp_text = tp_file.read()
test_proto = test_pb2.Test()
text_format.Merge(tp_text, test_proto)
return test_proto


_ALL_TESTPROTOS = [
_load_testproto(filename) for filename in sorted(
glob.glob('tests/unit/testdata/*.textproto'))]

_CREATE_TESTPROTOS = [
test_proto for test_proto in _ALL_TESTPROTOS
if test_proto.WhichOneof('test') == 'create']

_GET_TESTPROTOS = [
test_proto for test_proto in _ALL_TESTPROTOS
if test_proto.WhichOneof('test') == 'get']

_SET_TESTPROTOS = [
test_proto for test_proto in _ALL_TESTPROTOS
if test_proto.WhichOneof('test') == 'set']

_UPDATE_TESTPROTOS = [
test_proto for test_proto in _ALL_TESTPROTOS
if test_proto.WhichOneof('test') == 'update']

_UPDATE_PATHS_TESTPROTOS = [
test_proto for test_proto in _ALL_TESTPROTOS
if test_proto.WhichOneof('test') == 'update_paths']

_DELETE_TESTPROTOS = [
test_proto for test_proto in _ALL_TESTPROTOS
if test_proto.WhichOneof('test') == 'delete']

_LISTEN_TESTPROTOS = [
test_proto for test_proto in _ALL_TESTPROTOS
if test_proto.WhichOneof('test') == 'listen']


def _mock_firestore_api():
firestore_api = mock.Mock(spec=['commit'])
commit_response = firestore_pb2.CommitResponse(
write_results=[write_pb2.WriteResult()],
)
firestore_api.commit.return_value = commit_response
return firestore_api

class TestCrossLanguage(unittest.TestCase):

def test_cross_language(self):
filenames = sorted(glob.glob('tests/unit/testdata/*.textproto'))
failed = 0
descs = []
for test_filename in filenames:
bytes = open(test_filename, 'r').read()
test_proto = test_pb2.Test()
text_format.Merge(bytes, test_proto)
desc = '%s (%s)' % (
test_proto.description,
os.path.splitext(os.path.basename(test_filename))[0])
try:
self.run_write_test(test_proto, desc)
except Exception as error:
failed += 1
# print(desc, test_proto) # for debugging
# print(error.args[0]) # for debugging
descs.append(desc)
# for desc in descs: # for debugging
# print(desc) # for debugging
# print(str(failed) + "/" + str(len(filenames))) # for debugging

def run_write_test(self, test_proto, desc):
from google.cloud.firestore_v1beta1.proto import firestore_pb2
from google.cloud.firestore_v1beta1.proto import write_pb2

# Create a minimal fake GAPIC with a dummy result.
firestore_api = mock.Mock(spec=['commit'])
commit_response = firestore_pb2.CommitResponse(
write_results=[write_pb2.WriteResult()],
)
firestore_api.commit.return_value = commit_response

kind = test_proto.WhichOneof("test")
call = None
if kind == "create":
tp = test_proto.create
client, doc = self.setup(firestore_api, tp)
data = convert_data(json.loads(tp.json_data))
call = functools.partial(doc.create, data)
elif kind == "get":
tp = test_proto.get
client, doc = self.setup(firestore_api, tp)
call = functools.partial(doc.get, None, None)
try:
tp.is_error
except AttributeError:
return
elif kind == "set":
tp = test_proto.set
client, doc = self.setup(firestore_api, tp)
data = convert_data(json.loads(tp.json_data))
if tp.HasField("option"):
merge = True
else:
merge = False
call = functools.partial(doc.set, data, merge)
elif kind == "update":
tp = test_proto.update
client, doc = self.setup(firestore_api, tp)
data = convert_data(json.loads(tp.json_data))
if tp.HasField("precondition"):
option = convert_precondition(tp.precondition)
else:
option = None
call = functools.partial(doc.update, data, option)
elif kind == "update_paths":
# Python client doesn't have a way to call update with
# a list of field paths.
return
else:
assert kind == "delete"
tp = test_proto.delete
client, doc = self.setup(firestore_api, tp)
if tp.HasField("precondition"):
option = convert_precondition(tp.precondition)
else:
option = None
call = functools.partial(doc.delete, option)

if tp.is_error:
# TODO: is there a subclass of Exception we can check for?
with self.assertRaises(Exception):
call()
else:

def _make_client_document(firestore_api, testcase):
from google.cloud.firestore_v1beta1 import Client
from google.cloud.firestore_v1beta1.client import DEFAULT_DATABASE
import google.auth.credentials

_, project, _, database, _, doc_path = testcase.doc_ref_path.split('/', 5)
assert database == DEFAULT_DATABASE

# Attach the fake GAPIC to a real client.
credentials = mock.Mock(spec=google.auth.credentials.Credentials)
client = Client(project=project, credentials=credentials)
client._firestore_api_internal = firestore_api
return client, client.document(doc_path)


def _run_testcase(testcase, call, firestore_api, client):
if testcase.is_error:
# TODO: is there a subclass of Exception we can check for?
with pytest.raises(Exception):
call()
firestore_api.commit.assert_called_once_with(
client._database_string,
list(tp.request.writes),
transaction=None,
metadata=client._rpc_metadata)

def setup(self, firestore_api, proto):
from google.cloud.firestore_v1beta1 import Client
from google.cloud.firestore_v1beta1.client import DEFAULT_DATABASE
import google.auth.credentials

_, project, _, database, _, doc_path = proto.doc_ref_path.split('/', 5)
self.assertEqual(database, DEFAULT_DATABASE)

# Attach the fake GAPIC to a real client.
credentials = mock.Mock(spec=google.auth.credentials.Credentials)
client = Client(project=project, credentials=credentials)
client._firestore_api_internal = firestore_api
return client, client.document(doc_path)
else:
call()
firestore_api.commit.assert_called_once_with(
client._database_string,
list(testcase.request.writes),
transaction=None,
metadata=client._rpc_metadata)


@pytest.mark.parametrize('test_proto', _CREATE_TESTPROTOS)
def test_create_testprotos(test_proto):
testcase = test_proto.create
firestore_api = _mock_firestore_api()
client, document = _make_client_document(firestore_api, testcase)
data = convert_data(json.loads(testcase.json_data))
call = functools.partial(document.create, data)
_run_testcase(testcase, call, firestore_api, client)


@pytest.mark.parametrize('test_proto', _GET_TESTPROTOS)
def test_get_testprotos(test_proto):
testcase = test_proto.get
try:
testcase.is_error
except AttributeError:
return
firestore_api = _mock_firestore_api()
client, document = _make_client_document(firestore_api, testcase)
call = functools.partial(document.get, None, None)
_run_testcase(testcase, call, firestore_api, client)


@pytest.mark.parametrize('test_proto', _SET_TESTPROTOS)
def test_set_testprotos(test_proto):
testcase = test_proto.set
firestore_api = _mock_firestore_api()
client, document = _make_client_document(firestore_api, testcase)
data = convert_data(json.loads(testcase.json_data))
if testcase.HasField("option"):
merge = True
else:
merge = False
call = functools.partial(document.set, data, merge)
_run_testcase(testcase, call, firestore_api, client)


@pytest.mark.parametrize('test_proto', _UPDATE_TESTPROTOS)
def test_update_testprotos(test_proto):
testcase = test_proto.update
firestore_api = _mock_firestore_api()
client, document = _make_client_document(firestore_api, testcase)
data = convert_data(json.loads(testcase.json_data))
if testcase.HasField("precondition"):
option = convert_precondition(testcase.precondition)
else:
option = None
call = functools.partial(document.update, data, option)
_run_testcase(testcase, call, firestore_api, client)


@pytest.mark.skip(
reason="Python has no way to call update with a list of field paths.")
@pytest.mark.parametrize('test_proto', _UPDATE_PATHS_TESTPROTOS)
def test_update_paths_testprotos(test_proto):
pass


@pytest.mark.parametrize('test_proto', _DELETE_TESTPROTOS)
def test_delete_testprotos(test_proto):
testcase = test_proto.delete
firestore_api = _mock_firestore_api()
client, document = _make_client_document(firestore_api, testcase)
if testcase.HasField("precondition"):
option = convert_precondition(testcase.precondition)
else:
option = None
call = functools.partial(document.delete, option)
_run_testcase(testcase, call, firestore_api, client)


@pytest.mark.skip(reason="Watch aka listen not yet implemented in Python.")
@pytest.mark.parametrize('test_proto', _LISTEN_TESTPROTOS)
def test_listen_paths_testprotos(test_proto):
pass


def convert_data(v):
Expand Down

0 comments on commit ea5b22f

Please sign in to comment.