Skip to content

Commit 770e2d1

Browse files
committed
website: automated tests for the Your First App tutorial
-Refactoring the project stub to work for both quickstart and Your First App -Simplifying how CAT and EOF lines are hidden -Making changes and adding tests for the Your First App so the code compiles ( but does not work ) Change-Id: I1254f50c0c6452becfdb94768256f64ef48f85cb
1 parent 5e553e5 commit 770e2d1

File tree

23 files changed

+131
-114
lines changed

23 files changed

+131
-114
lines changed

Makefile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ tutJavaAndroid = tutorials/java/android
104104
tutJavaFortune = tutorials/java/fortune
105105

106106
syncbaseAndroidQuickstart = syncbase/quickstart
107+
syncbaseAndroidFirstApp = syncbase/first-app
107108

108109
# Scripts that 'complete' the named tutorials, creating all relevant files
109110
# (code, credentials, etc.) but skipping ephemeral steps like starting servers,
@@ -382,7 +383,8 @@ depsOneBigJavaTutorialTest = \
382383
content/$(tutJavaAndroid).md
383384

384385
depsOneBigSyncbaseAndroidTest = \
385-
content/$(syncbaseAndroidQuickstart).md
386+
content/$(syncbaseAndroidQuickstart).md \
387+
content/$(syncbaseAndroidFirstApp).md
386388

387389
.PHONY: test
388390
test: test-site test-tutorials-core test-tutorials-java test-syncbase-android

content/syncbase/first-app.md

Lines changed: 116 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,21 @@ The code below compiles, but may not execute successfully. Please join
1111
our [mailing list](/community/mailing-lists.html) for updates.
1212
{{/ helpers.warning }}
1313

14+
{{# helpers.hidden }}
15+
<!-- @setupEnvironment @test -->
16+
```
17+
export PROJECT_DIR=$(mktemp -d "${TMPDIR:-/tmp}/tmp.XXXXXXXXXX")
18+
cp -r $JIRI_ROOT/website/tools/android_project_stubs/example/* $PROJECT_DIR
19+
```
20+
{{/ helpers.hidden }}
21+
1422
# Introduction
1523

1624
In this quick tutorial, we will build a *Dice Roller* Android app where
1725
one can simply generate a random number between 1-6 and have it sync
1826
across multiple devices peer-to-peer, even with Wi-Fi turned off!
1927

20-
<div class="rows">
21-
<img style="width:250px" src="/images/syncbase-dice-device-1.gif">
22-
</div>
28+
<img style="width:250px" src="/images/syncbase-dice-device-1.gif">
2329

2430
# Setup
2531
This tutorial uses Android Studio, but feel free to use your IDE of choice.
@@ -33,10 +39,13 @@ template.
3339
## Install Syncbase
3440
Add the following to your `build.gradle` file.
3541

42+
<!-- @addSyncbaseDependency @test -->
3643
```
44+
cat - <<EOF >> $PROJECT_DIR/app/build.gradle
3745
dependencies {
38-
compile 'io.v:vanadium-android:2.1.3+'
46+
compile 'io.v:syncbase:0.1.4'
3947
}
48+
EOF
4049
```
4150

4251
## Setup Cloud Syncbase
@@ -55,9 +64,11 @@ used without a cloud Syncbase very soon.
5564
## Initialize Syncbase
5665

5766
**MainActivity.java**
67+
<!-- @generateMainActivity @test -->
5868
```
69+
cat - <<EOF | sed 's/{{.*}}//' > $PROJECT_DIR/app/src/main/java/io/v/syncbase/example/MainActivity.java
5970
{{# helpers.codedim }}
60-
package io.v.myfirstsyncbaseapp;
71+
package io.v.syncbase.example;
6172
6273
import android.support.v7.app.AppCompatActivity;
6374
@@ -74,23 +85,31 @@ public class MainActivity extends AppCompatActivity {
7485
7586
super.onCreate(savedInstanceState);
7687
{{/ helpers.codedim }}
77-
User currUser = Users.loginWithDefaultAccount();
7888
79-
DatabaseOptions dbOpt = new DatabaseOptions();
80-
dbOpt.cloudSyncbaseAddress = "<Your Cloud Syncbase Address>"
81-
dbOpt.cloudSyncbaseBlessing = "<Your Cloud Syncbase Blessing>"
89+
Syncbase.DatabaseOptions options = new Syncbase.DatabaseOptions();
90+
// dbOpt.cloudSyncbaseAddress = "<Your Cloud Syncbase Address>";
91+
// dbOpt.cloudSyncbaseBlessing = "<Your Cloud Syncbase Blessing>";
8292
83-
Database db = Syncbase.getDatabase();
93+
Syncbase.database(new Syncbase.DatabaseCallback() {
94+
@Override
95+
public void onSuccess(final Database db) {
96+
97+
// Use database to interact with Syncbase.
98+
99+
Log.i("info", "Syncbase is ready");
100+
}
101+
}, options);
84102
85-
Log.i("info", "Welcome: " + currUser.getEmail());
86103
{{# helpers.codedim }}
87104
setContentView(R.layout.activity_main);
88105
}
89106
}
90107
{{/ helpers.codedim }}
108+
EOF
91109
```
110+
92111
Now, let's run the app to make sure login and Syncbase initialization are working.
93-
After running, you should see `Welcome <email>` in logcat under Android Monitor
112+
After running, you should see `Syncbase is ready` in logcat under Android Monitor
94113
or in the console.
95114

96115
# UI Code
@@ -99,9 +118,11 @@ result and a `Button`s to roll the dice.
99118
Here is the UI code
100119

101120
**activity_main.xml**
121+
122+
<!-- @generateMainActivityXML @test -->
102123
```
103-
{{# helpers.codedim }}
104-
<?xml version="1.0" encoding="utf-8"?>
124+
cat - <<EOF | sed 's/{{.*}}//' > $PROJECT_DIR/app/src/main/res/layout/activity_main.xml
125+
{{# helpers.codedim }}<?xml version="1.0" encoding="utf-8"?>
105126
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
106127
xmlns:tools="http://schemas.android.com/tools"
107128
android:layout_width="match_parent"
@@ -110,7 +131,7 @@ Here is the UI code
110131
android:paddingLeft="@dimen/activity_horizontal_margin"
111132
android:paddingRight="@dimen/activity_horizontal_margin"
112133
android:paddingTop="@dimen/activity_vertical_margin"
113-
tools:context="io.v.myfirstsyncbaseapp.MainActivity">
134+
tools:context="io.v.syncbase.example.MainActivity">
114135
{{/ helpers.codedim }}
115136
<TextView
116137
{{# helpers.codedim }}
@@ -139,11 +160,20 @@ Here is the UI code
139160
android:layout_centerHorizontal="true" />
140161
</RelativeLayout>
141162
{{/ helpers.codedim }}
163+
EOF
142164
```
165+
143166
Running the project at this point should result in the following UI:
144167

145168
<img style="width:250px" src="/images/syncbase-dice-1.png" alt="Screenshot of the Dice Roll app">
146169

170+
{{# helpers.hidden }}
171+
<!-- @firstStepCompile_mayTakeMinutes @test -->
172+
```
173+
cd $PROJECT_DIR && ./gradlew assembleRelease
174+
```
175+
{{/ helpers.hidden }}
176+
147177
# Data Binding
148178
The data model for this app is simple. We just need a single collection (`dice`)
149179
and a single key/value pair (`'result'`, `int`) to store the result of the dice
@@ -162,9 +192,11 @@ from a remote device.
162192

163193
Now let's hook up this model to our code.
164194

195+
<!-- @updateMainActivity @test -->
165196
```
197+
cat - <<EOF | sed 's/{{.*}}//' > $PROJECT_DIR/app/src/main/java/io/v/syncbase/example/MainActivity.java
166198
{{# helpers.codedim }}
167-
package io.v.myfirstsyncbaseapp;
199+
package io.v.syncbase.example;
168200
169201
import android.support.v7.app.AppCompatActivity;
170202
import android.os.Bundle;
@@ -182,70 +214,77 @@ public class MainActivity extends AppCompatActivity {
182214
183215
@Override
184216
protected void onCreate(Bundle savedInstanceState) {
185-
super.onCreate(savedInstanceState);
186-
187-
User currUser = Users.loginWithDefaultAccount();
188-
189-
DatabaseOptions dbOpt = new DatabaseOptions();
190-
dbOpt.cloudSyncbaseAddress = "<Your Cloud Syncbase Address>"
191-
dbOpt.cloudSyncbaseBlessing = "<Your Cloud Syncbase Blessing>"
192-
193-
Database db = Syncbase.getDatabase();
194-
195-
Log.i("info", "Welcome: " + currUser.getEmail());
196217
197-
setContentView(R.layout.activity_main);
198-
199-
{{/ helpers.codedim }}
200-
201-
// On dice roll, put a new random number under key "result"
202-
// in the "dice" collection.
203-
final Button button = (Button) findViewById(R.id.buttonRoll);
204-
button.setOnClickListener(new View.OnClickListener() {
205-
public void onClick(View v) {
206-
int randomNumber = new Random().nextInt(6) + 1;
207-
208-
Collection diceCollection = db.collection("dice");
209-
diceCollection.put("result", randomNumber);
210-
}
211-
});
212-
213-
// Watch the database and update the UI whenever a new value
214-
// is encountered.
215-
db.removeWatchChangeHandler(new Database.WatchChangeHandler() {
216-
217-
void onInitialState(Iterator<WatchChange> values) {
218-
// onInitialState is called with any existing data in Syncbase.
219-
// Since we only have a single collection, single key/value,
220-
// there can only be 0 or 1 values.
221-
if (values.hasNext()) {
222-
int result = (int) values.next().getValue(int.class);
223-
updateResult(result);
224-
}
225-
}
218+
super.onCreate(savedInstanceState);
226219
227-
void onChangeBatch(Iterator<WatchChange> changes) {
228-
// onChangeBatch is called with any updates to the data.
229-
// Since we only have a single collection, single key/value.
230-
// there can only be 1 WatchChange whenever the value is mutated
231-
// and the type of change would always be `put` in our case.
232-
int result = (int) changes.next().getValue(int.class);
233-
updateResult(result);
220+
Syncbase.DatabaseOptions options = new Syncbase.DatabaseOptions();
221+
// dbOpt.cloudSyncbaseAddress = "<Your Cloud Syncbase Address>";
222+
// dbOpt.cloudSyncbaseBlessing = "<Your Cloud Syncbase Blessing>";
223+
224+
Syncbase.database(new Syncbase.DatabaseCallback() {
225+
@Override
226+
public void onSuccess(final Database db) {
227+
228+
// Use database to interact with Syncbase.
229+
230+
Log.i("info", "Syncbase is ready");
231+
{{/ helpers.codedim }}
232+
233+
// On dice roll, put a new random number under key "result"
234+
// in the "dice" collection.
235+
final Button button = (Button) findViewById(R.id.buttonRoll);
236+
button.setOnClickListener(new View.OnClickListener() {
237+
public void onClick(View v) {
238+
int randomNumber = new Random().nextInt(6) + 1;
239+
240+
Collection diceCollection = db.collection("dice");
241+
diceCollection.put("result", randomNumber);
242+
}
243+
});
244+
245+
// Watch the database and update the UI whenever a new value
246+
// is encountered.
247+
db.addWatchChangeHandler(new Database.WatchChangeHandler() {
248+
@Override
249+
public void onInitialState(Iterator<WatchChange> values) {
250+
// onInitialState is called with any existing data in Syncbase.
251+
// Since we only have a single collection, single key/value,
252+
// there can only be 0 or 1 values.
253+
if (values.hasNext()) {
254+
int result = (int) values.next().getValue();
255+
updateResult(result);
256+
}
257+
}
258+
259+
@Override
260+
public void onChangeBatch(Iterator<WatchChange> changes) {
261+
// onChangeBatch is called with any updates to the data.
262+
// Since we only have a single collection, single key/value.
263+
// there can only be 1 WatchChange whenever the value is mutated
264+
// and the type of change would always be `put` in our case.
265+
int result = (int) changes.next().getValue();
266+
updateResult(result);
267+
}
268+
269+
@Override
270+
public void onError(Throwable e) {
271+
// Something went wrong. Watch is no longer active.
272+
}
273+
}, new Database.AddWatchChangeHandlerOptions());
274+
{{# helpers.codedim }}
234275
}
276+
}, new Syncbase.DatabaseOptions());
235277
236-
void onError(Exception e) {
237-
// Something went wrong. Watch is no longer active.
238-
}
239-
});
278+
setContentView(R.layout.activity_main);
240279
}
241280
281+
{{/ helpers.codedim }}
242282
private void updateResult(int newValue) {
243283
final TextView result = (TextView) findViewById(R.id.textViewResult);
244284
result.setText(String.valueOf(newValue));
245285
}
246-
{{# helpers.codedim }}
247286
}
248-
{{/ helpers.codedim }}
287+
EOF
249288
```
250289

251290
# Running The App
@@ -271,9 +310,14 @@ After running the application on 2 or more devices with Internet connectivity,
271310
ensure Bluetooth is enabled on both devices and turn off Wi-Fi, the dice rolls
272311
should still sync between the devices just fine!
273312

274-
<div class="rows">
275-
<img style="width:250px" src="/images/syncbase-dice-device-1.gif">
276-
</div>
313+
<img style="width:250px" src="/images/syncbase-dice-device-1.gif">
314+
315+
{{# helpers.hidden }}
316+
<!-- @secondStepCompile_mayTakeMinutes @test -->
317+
```
318+
cd $PROJECT_DIR && ./gradlew assembleRelease
319+
```
320+
{{/ helpers.hidden }}
277321

278322
# Want to dive deeper?
279323
Checkout the [Tutorial] to build a full-fledged Todo app and learn more Syncbase

content/syncbase/quickstart.md

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ our [mailing list](/community/mailing-lists.html) for updates.
1515
<!-- @setupEnvironment @test -->
1616
```
1717
export PROJECT_DIR=$(mktemp -d "${TMPDIR:-/tmp}/tmp.XXXXXXXXXX")
18-
cp -r $JIRI_ROOT/website/tools/android_project_stubs/quickstart/* $PROJECT_DIR
18+
cp -r $JIRI_ROOT/website/tools/android_project_stubs/example/* $PROJECT_DIR
1919
```
2020
{{/ helpers.hidden }}
2121

@@ -32,7 +32,6 @@ template.
3232
Syncbase's Android library is published to both [JCenter] and [MavenCentral].
3333
To install the library, add the following to your `build.gradle` file.
3434

35-
{{# helpers.hide_cat_eof_lines }}
3635
<!-- @addSyncbaseDependency @test -->
3736
```
3837
cat - <<EOF >> $PROJECT_DIR/app/build.gradle
@@ -41,7 +40,6 @@ dependencies {
4140
}
4241
EOF
4342
```
44-
{{/ helpers.hide_cat_eof_lines }}
4543

4644
# Setup Cloud Syncbase
4745
Head to [https://sb-allocator.v.io/](https://sb-allocator.v.io/) to setup a free
@@ -59,13 +57,11 @@ used without a cloud Syncbase soon.
5957
# Use Syncbase
6058
In your `MainActivity`, import Syncbase and read/write some data!
6159

62-
63-
{{# helpers.hide_cat_eof_lines }}
6460
<!-- @generateMainActivity @test -->
6561
```
66-
cat - <<EOF | sed 's/{{.*}}//' > $PROJECT_DIR/app/src/main/java/syncbase/io/v/quickstart/MainActivity.java
62+
cat - <<EOF | sed 's/{{.*}}//' > $PROJECT_DIR/app/src/main/java/io/v/syncbase/example/MainActivity.java
6763
{{# helpers.codedim}}
68-
package syncbase.io.v.quickstart;
64+
package io.v.syncbase.example;
6965
7066
import android.support.v7.app.AppCompatActivity;
7167
import android.os.Bundle;
@@ -85,7 +81,7 @@ public class MainActivity extends AppCompatActivity {
8581
8682
Syncbase.database(new Syncbase.DatabaseCallback() {
8783
@Override
88-
public void onSuccess(Database db) {
84+
public void onSuccess(final Database db) {
8985
9086
// Use database to interact with Syncbase.
9187
@@ -104,7 +100,6 @@ public class MainActivity extends AppCompatActivity {
104100
{{/ helpers.codedim}}
105101
EOF
106102
```
107-
{{/ helpers.hide_cat_eof_lines }}
108103

109104
**That's all!** You are now using Syncbase!
110105

helpers.js

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,6 @@ exports.codedim = function(text) {
3636
return '{#dim}{#dim-children}' + text + '{/dim-children}{/dim}\n';
3737
};
3838

39-
// Hides the "cat <<EOF > ..." and "EOF" lines in tutorial code.
40-
exports.hide_cat_eof_lines = function(text) {
41-
return '<div class="hide-cat-eof-lines">' + marked(text) + '</div>\n';
42-
};
43-
4439
////////////////////////////////////////
4540
// Internal helpers
4641

templates/layouts/syncbase.mustache

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<body>
55
{{> partials/syncbase_header }}
66
{{> partials/syncbase_sidebar }}
7-
<main>
7+
<main class="hide-cat-eof-lines">
88
<h1 class="title">
99
{{# page.fullTitle }}{{ page.fullTitle }}{{/ page.fullTitle }}
1010
{{^ page.fullTitle }}{{ page.title }}{{/ page.fullTitle }}

0 commit comments

Comments
 (0)