Skip to content

Commit ca26599

Browse files
author
Ilya Gurov
authored
test: move Django tests to emulator (#27)
1 parent 8183247 commit ca26599

File tree

8 files changed

+126
-30
lines changed

8 files changed

+126
-30
lines changed
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
on:
2+
push:
3+
branches:
4+
- master
5+
pull_request:
6+
name: Run Django backend tests against emulator
7+
jobs:
8+
system-tests:
9+
runs-on: ubuntu-latest
10+
11+
services:
12+
emulator:
13+
image: gcr.io/cloud-spanner-emulator/emulator:latest
14+
ports:
15+
- 9010:9010
16+
- 9020:9020
17+
18+
steps:
19+
- name: Checkout code
20+
uses: actions/checkout@v2
21+
- name: Setup Python
22+
uses: actions/setup-python@v2
23+
with:
24+
python-version: 3.8
25+
- name: Run system tests
26+
run: sh build.sh
27+
env:
28+
SPANNER_EMULATOR_HOST: localhost:9010
29+
GOOGLE_CLOUD_PROJECT: emulator-test-project
30+
GOOGLE_CLOUD_TESTS_CREATE_SPANNER_INSTANCE: true
31+
DJANGO_WORKER_INDEX: 0
32+
DJANGO_WORKER_COUNT: 1
33+
SPANNER_TEST_INSTANCE: google-cloud-django-backend-tests

.kokoro/build.sh

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@
1313
# See the License for the specific language governing permissions and
1414
# limitations under the License.
1515

16-
set -eo pipefail
17-
1816
cd github/python-spanner-django
1917

2018
# Disable buffering, so that the logs stream through.

build.sh

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#!/bin/bash
2+
# Copyright 2020 Google LLC
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# https://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
set -x pipefail
17+
18+
# Disable buffering, so that the logs stream through.
19+
export PYTHONUNBUFFERED=1
20+
21+
# Debug: show build environment
22+
env | grep KOKORO
23+
24+
# Setup service account credentials.
25+
# export GOOGLE_APPLICATION_CREDENTIALS=${KOKORO_GFILE_DIR}/service-account.json
26+
27+
# Setup project id.
28+
# export PROJECT_ID=$(cat "${KOKORO_GFILE_DIR}/project-id.json")
29+
30+
# Export essential environment variables for Django tests.
31+
export RUNNING_SPANNER_BACKEND_TESTS=1
32+
33+
# The emulator is currently unusable for our tests because:
34+
# a) It doesn't support INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE
35+
# b) Cannot accept parameters whose types aren't known, so can't be used for
36+
# Python and other dynamic languages.
37+
# export USE_SPANNER_EMULATOR=0
38+
39+
pip3 install .
40+
# Create a unique DJANGO_TESTS_DIR per worker to avoid
41+
# any clashes with configured tests by other workers.
42+
export DJANGO_TESTS_DIR="django_tests_$DJANGO_WORKER_INDEX"
43+
mkdir -p $DJANGO_TESTS_DIR && git clone --depth 1 --single-branch --branch spanner-2.2.x https://github.com/timgraham/django.git $DJANGO_TESTS_DIR/django
44+
45+
# Install dependencies for Django tests.
46+
sudo apt-get update
47+
apt-get install -y libffi-dev libjpeg-dev zlib1g-dev libmemcached-dev
48+
cd $DJANGO_TESTS_DIR/django && pip3 install -e . && pip3 install -r tests/requirements/py3.txt; cd ../../
49+
50+
python3 create_test_instance.py
51+
bash django_test_suite.sh

create_test_instance.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Copyright 2016 Google LLC All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import os
16+
17+
from google.cloud.spanner_v1 import Client
18+
19+
20+
client = Client(
21+
project=os.getenv("GOOGLE_CLOUD_PROJECT", "emulator-test-project")
22+
)
23+
24+
instance = client.instance("google-cloud-django-backend-tests")
25+
created_op = instance.create()
26+
created_op.result(30) # block until completion

django_spanner/base.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
# license that can be found in the LICENSE file or at
55
# https://developers.google.com/open-source/licenses/bsd
66

7+
import os
8+
79
from django.db.backends.base.base import BaseDatabaseWrapper
810
from google.cloud import spanner_dbapi as Database, spanner_v1 as spanner
911

@@ -103,7 +105,9 @@ class DatabaseWrapper(BaseDatabaseWrapper):
103105

104106
@property
105107
def instance(self):
106-
return spanner.Client().instance(self.settings_dict["INSTANCE"])
108+
return spanner.Client(
109+
project=os.environ["GOOGLE_CLOUD_PROJECT"]
110+
).instance(self.settings_dict["INSTANCE"])
107111

108112
@property
109113
def _nodb_connection(self):
@@ -113,7 +117,7 @@ def _nodb_connection(self):
113117

114118
def get_connection_params(self):
115119
return {
116-
"project": self.settings_dict["PROJECT"],
120+
"project": os.environ["GOOGLE_CLOUD_PROJECT"],
117121
"instance_id": self.settings_dict["INSTANCE"],
118122
"database_id": self.settings_dict["NAME"],
119123
"user_agent": "django_spanner/2.2.0a1",

django_spanner/creation.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ def _create_test_db(self, verbosity, autoclobber, keepdb=False):
5151
# just return and skip it all.
5252
if keepdb:
5353
return test_database_name
54+
5455
self.log("Got an error creating the test database: %s" % e)
5556
if not autoclobber:
5657
confirm = input(
@@ -81,6 +82,9 @@ def _create_test_db(self, verbosity, autoclobber, keepdb=False):
8182
return test_database_name
8283

8384
def _execute_create_test_db(self, cursor, parameters, keepdb=False):
85+
self.log(
86+
"emulator: " + str(self.connection.instance._client._emulator_host)
87+
)
8488
self.connection.instance.database(parameters["dbname"]).create()
8589

8690
def _destroy_test_db(self, test_database_name, verbosity):

django_test_suite.sh

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
# license that can be found in the LICENSE file.
66

77
# exit when any command fails
8-
set -e
8+
set -x pipefail
99

1010
# If no SPANNER_TEST_DB is set, generate a unique one
1111
# so that we can have multiple tests running without
@@ -15,7 +15,9 @@ TEST_DBNAME=${SPANNER_TEST_DB:-$(python3 -c 'import os, time; print(chr(ord("a")
1515
TEST_DBNAME_OTHER="$TEST_DBNAME-ot"
1616
TEST_APPS=${DJANGO_TEST_APPS:-basic}
1717
INSTANCE=${SPANNER_TEST_INSTANCE:-django-tests}
18-
PROJECT=${PROJECT_ID:-appdev-soda-spanner-staging}
18+
PROJECT=${PROJECT_ID}
19+
SPANNER_EMULATOR_HOST=${SPANNER_EMULATOR_HOST}
20+
GOOGLE_CLOUD_PROJECT=${GOOGLE_CLOUD_PROJECT}
1921
SETTINGS_FILE="$TEST_DBNAME-settings"
2022
TESTS_DIR=${DJANGO_TESTS_DIR:-django_tests}
2123

@@ -42,21 +44,11 @@ PASSWORD_HASHERS = [
4244
!
4345
}
4446

45-
setup_emulator_if_needed() {
46-
if [[ $SPANNER_EMULATOR_HOST != "" ]]
47-
then
48-
echo "Running the emulator at: $SPANNER_EMULATOR_HOST"
49-
./emulator_main --host_port "$SPANNER_EMULATOR_HOST" &
50-
SPANNER_INSTANCE=$INSTANCE python3 .kokoro/ensure_instance_exists.py
51-
fi
52-
}
53-
5447
run_django_tests() {
5548
cd $TESTS_DIR/django/tests
5649
create_settings
5750
echo -e "\033[32mRunning Django tests: $TEST_APPS\033[00m"
58-
python3 runtests.py $TEST_APPS --verbosity=2 --noinput --settings $SETTINGS_FILE
51+
python3 runtests.py $TEST_APPS --verbosity=3 --noinput --settings $SETTINGS_FILE
5952
}
6053

61-
setup_emulator_if_needed
6254
run_django_tests

parallelize_tests.go

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,30 +31,18 @@ import (
3131
)
3232

3333
func main() {
34-
workerIndex, err := strconv.ParseInt(os.Getenv("DJANGO_WORKER_INDEX"), 10, 64)
35-
if err != nil {
36-
log.Fatalf("Failed to parse DJANGO_WORKER_INDEX as an integer: %v", err)
37-
}
3834
workerCount, err := strconv.ParseInt(os.Getenv("DJANGO_WORKER_COUNT"), 10, 64)
3935
if err != nil {
4036
log.Fatalf("Failed to parse DJANGO_WORKER_COUNT as an integer: %v", err)
4137
}
42-
if workerIndex >= workerCount {
43-
// Re-enable when we figure out how to deal with Cloud Spanner's very low quotas.
44-
fmt.Printf("workerIndex (%d) >= workerCount (%d)", workerIndex, workerCount)
45-
return
46-
}
4738

4839
allAppsBlob, err := ioutil.ReadFile("django_test_apps.txt")
4940
if err != nil {
5041
panic(err)
5142
}
5243
allApps := strings.Split(string(allAppsBlob), "\n")
5344
appsPerWorker := int(math.Ceil(float64(len(allApps)) / float64(workerCount)))
54-
startIndex := int(workerIndex) * appsPerWorker
55-
if startIndex >= len(allApps) {
56-
startIndex = int(workerIndex)
57-
}
45+
startIndex := 0
5846
endIndex := startIndex + appsPerWorker
5947
if endIndex >= len(allApps) {
6048
endIndex = len(allApps)

0 commit comments

Comments
 (0)