1// Copyright 2014 The Cayley Authors. 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
15package graphtest
16
17import (
18	"context"
19	"fmt"
20	"os"
21	"path/filepath"
22	"reflect"
23	"sort"
24	"testing"
25	"time"
26
27	"github.com/stretchr/testify/require"
28
29	"github.com/cayleygraph/cayley/graph"
30	"github.com/cayleygraph/cayley/graph/graphtest/testutil"
31	"github.com/cayleygraph/cayley/internal"
32	"github.com/cayleygraph/cayley/query"
33	"github.com/cayleygraph/cayley/query/gizmo"
34	_ "github.com/cayleygraph/cayley/writer"
35)
36
37const (
38	format  = "nquads"
39	timeout = 300 * time.Second
40)
41
42const (
43	nSpeed = "Speed"
44	nLakeH = "The Lake House"
45
46	SandraB = "Sandra Bullock"
47	KeanuR  = "Keanu Reeves"
48)
49
50func checkIntegration(t testing.TB, force bool) {
51	if testing.Short() {
52		t.SkipNow()
53	}
54	if !force && os.Getenv("RUN_INTEGRATION") != "true" {
55		t.Skip("skipping integration tests; set RUN_INTEGRATION=true to run them")
56	}
57}
58
59func TestIntegration(t *testing.T, gen testutil.DatabaseFunc, force bool) {
60	checkIntegration(t, force)
61	h, closer := prepare(t, gen)
62	defer closer()
63
64	checkQueries(t, h, timeout)
65}
66
67func BenchmarkIntegration(t *testing.B, gen testutil.DatabaseFunc, force bool) {
68	checkIntegration(t, force)
69	benchmarkQueries(t, gen)
70}
71
72func costarTag(id, c1, c1m, c2, c2m string) map[string]string {
73	return map[string]string{
74		"id":            id,
75		"costar1_actor": c1,
76		"costar1_movie": c1m,
77		"costar2_actor": c2,
78		"costar2_movie": c2m,
79	}
80}
81
82var queries = []struct {
83	message string
84	long    bool
85	query   string
86	tag     string
87	// for testing
88	skip   bool
89	expect []interface{}
90}{
91	// Easy one to get us started. How quick is the most straightforward retrieval?
92	{
93		message: "name predicate",
94		query: `
95		g.V("Humphrey Bogart").In("<name>").All()
96		`,
97		expect: []interface{}{
98			map[string]string{"id": "</en/humphrey_bogart>"},
99		},
100	},
101
102	// Grunty queries.
103	// 2014-07-12: This one seems to return in ~20ms in memory;
104	// that's going to be measurably slower for every other backend.
105	{
106		message: "two large sets with no intersection",
107		query: `
108		function getId(x) { return g.V(x).In("<name>") }
109		var actor_to_film = g.M().In("</film/performance/actor>").In("</film/film/starring>")
110
111		getId("Oliver Hardy").Follow(actor_to_film).Out("<name>").Intersect(
112			getId("Mel Blanc").Follow(actor_to_film).Out("<name>")).All()
113			`,
114		expect: nil,
115	},
116
117	// 2014-07-12: This one takes about 4 whole seconds in memory. This is a behemoth.
118	{
119		message: "three huge sets with small intersection",
120		long:    true,
121		query: `
122			function getId(x) { return g.V(x).In("<name>") }
123			var actor_to_film = g.M().In("</film/performance/actor>").In("</film/film/starring>")
124
125			var a = getId("Oliver Hardy").Follow(actor_to_film).FollowR(actor_to_film)
126			var b = getId("Mel Blanc").Follow(actor_to_film).FollowR(actor_to_film)
127			var c = getId("Billy Gilbert").Follow(actor_to_film).FollowR(actor_to_film)
128
129			seen = {}
130
131			a.Intersect(b).Intersect(c).ForEach(function (d) {
132				if (!(d.id in seen)) {
133					seen[d.id] = true;
134					g.Emit(d)
135				}
136			})
137			`,
138		expect: []interface{}{
139			map[string]string{"id": "</en/sterling_holloway>"},
140			map[string]string{"id": "</en/billy_gilbert>"},
141		},
142	},
143
144	// This is more of an optimization problem that will get better over time. This takes a lot
145	// of wrong turns on the walk down to what is ultimately the name, but top AND has it easy
146	// as it has a fixed ID. Exercises Contains().
147	{
148		message: "the helpless checker",
149		long:    true,
150		query: `
151			g.V().As("person").In("<name>").In().In().Out("<name>").Is("Casablanca").All()
152			`,
153		tag: "person",
154		expect: []interface{}{
155			map[string]string{"id": "Casablanca", "person": "Ingrid Bergman"},
156			map[string]string{"id": "Casablanca", "person": "Madeleine LeBeau"},
157			map[string]string{"id": "Casablanca", "person": "Joy Page"},
158			map[string]string{"id": "Casablanca", "person": "Claude Rains"},
159			map[string]string{"id": "Casablanca", "person": "S.Z. Sakall"},
160			map[string]string{"id": "Casablanca", "person": "Helmut Dantine"},
161			map[string]string{"id": "Casablanca", "person": "Conrad Veidt"},
162			map[string]string{"id": "Casablanca", "person": "Paul Henreid"},
163			map[string]string{"id": "Casablanca", "person": "Peter Lorre"},
164			map[string]string{"id": "Casablanca", "person": "Sydney Greenstreet"},
165			map[string]string{"id": "Casablanca", "person": "Leonid Kinskey"},
166			map[string]string{"id": "Casablanca", "person": "Lou Marcelle"},
167			map[string]string{"id": "Casablanca", "person": "Dooley Wilson"},
168			map[string]string{"id": "Casablanca", "person": "John Qualen"},
169			map[string]string{"id": "Casablanca", "person": "Humphrey Bogart"},
170		},
171	},
172
173	// Exercises Not().Contains(), as above.
174	{
175		message: "the helpless checker, negated (films without Ingrid Bergman)",
176		long:    true,
177		query: `
178			g.V().As("person").In("<name>").In().In().Out("<name>").Except(g.V("Ingrid Bergman").In("<name>").In().In().Out("<name>")).Is("Casablanca").All()
179			`,
180		tag:    "person",
181		expect: nil,
182	},
183	{
184		message: "the helpless checker, negated (without actors Ingrid Bergman)",
185		long:    true,
186		query: `
187			g.V().As("person").In("<name>").Except(g.V("Ingrid Bergman").In("<name>")).In().In().Out("<name>").Is("Casablanca").All()
188			`,
189		tag: "person",
190		expect: []interface{}{
191			map[string]string{"id": "Casablanca", "person": "Madeleine LeBeau"},
192			map[string]string{"id": "Casablanca", "person": "Joy Page"},
193			map[string]string{"id": "Casablanca", "person": "Claude Rains"},
194			map[string]string{"id": "Casablanca", "person": "S.Z. Sakall"},
195			map[string]string{"id": "Casablanca", "person": "Helmut Dantine"},
196			map[string]string{"id": "Casablanca", "person": "Conrad Veidt"},
197			map[string]string{"id": "Casablanca", "person": "Paul Henreid"},
198			map[string]string{"id": "Casablanca", "person": "Peter Lorre"},
199			map[string]string{"id": "Casablanca", "person": "Sydney Greenstreet"},
200			map[string]string{"id": "Casablanca", "person": "Leonid Kinskey"},
201			map[string]string{"id": "Casablanca", "person": "Lou Marcelle"},
202			map[string]string{"id": "Casablanca", "person": "Dooley Wilson"},
203			map[string]string{"id": "Casablanca", "person": "John Qualen"},
204			map[string]string{"id": "Casablanca", "person": "Humphrey Bogart"},
205		},
206	},
207
208	//Q: Who starred in both "The Net" and "Speed" ?
209	//A: "Sandra Bullock"
210	{
211		message: "Net and Speed",
212		query: common + `m1_actors.Intersect(m2_actors).Out("<name>").All()
213`,
214		expect: []interface{}{
215			map[string]string{"id": SandraB, "movie1": "The Net", "movie2": nSpeed},
216		},
217	},
218
219	//Q: Did "Keanu Reeves" star in "The Net" ?
220	//A: No
221	{
222		message: "Keanu in The Net",
223		query: common + `actor2.Intersect(m1_actors).Out("<name>").All()
224`,
225		expect: nil,
226	},
227
228	//Q: Did "Keanu Reeves" star in "Speed" ?
229	//A: Yes
230	{
231		message: "Keanu in Speed",
232		query: common + `actor2.Intersect(m2_actors).Out("<name>").All()
233`,
234		expect: []interface{}{
235			map[string]string{"id": KeanuR, "movie2": nSpeed},
236		},
237	},
238
239	//Q: Has "Keanu Reeves" co-starred with anyone who starred in "The Net" ?
240	//A: "Keanu Reeves" was in "Speed" and "The Lake House" with "Sandra Bullock",
241	//   who was in "The Net"
242	{
243		message: "Keanu with other in The Net",
244		long:    true,
245		query: common + `actor2.Follow(coStars1).Intersect(m1_actors).Out("<name>").All()
246`,
247		expect: []interface{}{
248			map[string]string{"id": SandraB, "movie1": "The Net", "costar1_movie": nSpeed},
249			map[string]string{"movie1": "The Net", "costar1_movie": nLakeH, "id": SandraB},
250		},
251	},
252
253	//Q: Do "Keanu Reeves" and "Sandra Bullock" have any commons co-stars?
254	//A: Yes, many. For example: SB starred with "Steve Martin" in "The Prince
255	//    of Egypt", and KR starred with Steven Martin in "Parenthood".
256	{
257		message: "Keanu and Bullock with other",
258		long:    true,
259		query: common + `actor1.Save("<name>","costar1_actor").Follow(coStars1).Intersect(actor2.Save("<name>","costar2_actor").Follow(coStars2)).Out("<name>").All()
260`,
261		expect: []interface{}{
262			costarTag(SandraB, SandraB, "The Proposal", KeanuR, nSpeed),
263			costarTag(SandraB, SandraB, "The Proposal", KeanuR, nLakeH),
264			costarTag("Mary Steenburgen", SandraB, "The Proposal", KeanuR, "Parenthood"),
265			costarTag("Craig T. Nelson", SandraB, "The Proposal", KeanuR, "The Devil's Advocate"),
266			costarTag(SandraB, SandraB, "Crash", KeanuR, nSpeed),
267			costarTag(SandraB, SandraB, "Crash", KeanuR, nLakeH),
268			costarTag(SandraB, SandraB, "Gun Shy", KeanuR, nSpeed),
269			costarTag(SandraB, SandraB, "Gun Shy", KeanuR, nLakeH),
270			costarTag(SandraB, SandraB, "Demolition Man", KeanuR, nSpeed),
271			costarTag(SandraB, SandraB, "Demolition Man", KeanuR, nLakeH),
272			costarTag("Benjamin Bratt", SandraB, "Demolition Man", KeanuR, "Thumbsucker"),
273			costarTag(SandraB, SandraB, "Divine Secrets of the Ya-Ya Sisterhood", KeanuR, nSpeed),
274			costarTag(SandraB, SandraB, "Divine Secrets of the Ya-Ya Sisterhood", KeanuR, nLakeH),
275			costarTag("Shirley Knight", SandraB, "Divine Secrets of the Ya-Ya Sisterhood", KeanuR, "The Private Lives of Pippa Lee"),
276			costarTag(SandraB, SandraB, "A Time to Kill", KeanuR, nSpeed),
277			costarTag(SandraB, SandraB, "A Time to Kill", KeanuR, nLakeH),
278			costarTag(SandraB, SandraB, "Forces of Nature", KeanuR, nSpeed),
279			costarTag(SandraB, SandraB, "Forces of Nature", KeanuR, nLakeH),
280			costarTag(SandraB, SandraB, "Hope Floats", KeanuR, nSpeed),
281			costarTag(SandraB, SandraB, "Hope Floats", KeanuR, nLakeH),
282			costarTag(SandraB, SandraB, "Infamous", KeanuR, nSpeed),
283			costarTag(SandraB, SandraB, "Infamous", KeanuR, nLakeH),
284			costarTag("Jeff Daniels", SandraB, "Infamous", KeanuR, nSpeed),
285			costarTag(SandraB, SandraB, "Love Potion No. 9", KeanuR, nSpeed),
286			costarTag(SandraB, SandraB, "Love Potion No. 9", KeanuR, nLakeH),
287			costarTag(SandraB, SandraB, "Miss Congeniality", KeanuR, nSpeed),
288			costarTag(SandraB, SandraB, "Miss Congeniality", KeanuR, nLakeH),
289			costarTag("Benjamin Bratt", SandraB, "Miss Congeniality", KeanuR, "Thumbsucker"),
290			costarTag(SandraB, SandraB, "Miss Congeniality 2: Armed and Fabulous", KeanuR, nSpeed),
291			costarTag(SandraB, SandraB, "Miss Congeniality 2: Armed and Fabulous", KeanuR, nLakeH),
292			costarTag(SandraB, SandraB, "Murder by Numbers", KeanuR, nSpeed),
293			costarTag(SandraB, SandraB, "Murder by Numbers", KeanuR, nLakeH),
294			costarTag(SandraB, SandraB, "Practical Magic", KeanuR, nSpeed),
295			costarTag(SandraB, SandraB, "Practical Magic", KeanuR, nLakeH),
296			costarTag("Dianne Wiest", SandraB, "Practical Magic", KeanuR, "Parenthood"),
297			costarTag(KeanuR, SandraB, nSpeed, KeanuR, "Flying"),
298			costarTag(KeanuR, SandraB, nSpeed, KeanuR, "The Animatrix"),
299			costarTag(KeanuR, SandraB, nSpeed, KeanuR, "Tune in Tomorrow"),
300			costarTag(KeanuR, SandraB, nSpeed, KeanuR, "The Last Time I Committed Suicide"),
301			costarTag(KeanuR, SandraB, nSpeed, KeanuR, "Constantine"),
302			costarTag(KeanuR, SandraB, nSpeed, KeanuR, "Permanent Record"),
303			costarTag(KeanuR, SandraB, nSpeed, KeanuR, "Dangerous Liaisons"),
304			costarTag(KeanuR, SandraB, nSpeed, KeanuR, "The Private Lives of Pippa Lee"),
305			costarTag(KeanuR, SandraB, nSpeed, KeanuR, "A Scanner Darkly"),
306			costarTag(KeanuR, SandraB, nSpeed, KeanuR, "A Walk in the Clouds"),
307			costarTag(KeanuR, SandraB, nSpeed, KeanuR, "Hardball"),
308			costarTag(KeanuR, SandraB, nSpeed, KeanuR, "Life Under Water"),
309			costarTag(KeanuR, SandraB, nSpeed, KeanuR, "Much Ado About Nothing"),
310			costarTag(KeanuR, SandraB, nSpeed, KeanuR, "My Own Private Idaho"),
311			costarTag(KeanuR, SandraB, nSpeed, KeanuR, "Parenthood"),
312			costarTag(KeanuR, SandraB, nSpeed, KeanuR, "Point Break"),
313			costarTag(KeanuR, SandraB, nSpeed, KeanuR, "Providence"),
314			costarTag(KeanuR, SandraB, nSpeed, KeanuR, "River's Edge"),
315			costarTag(KeanuR, SandraB, nSpeed, KeanuR, "Something's Gotta Give"),
316			costarTag(KeanuR, SandraB, nSpeed, KeanuR, nSpeed),
317			costarTag(KeanuR, SandraB, nSpeed, KeanuR, "Sweet November"),
318			costarTag(KeanuR, SandraB, nSpeed, KeanuR, nLakeH),
319			costarTag(KeanuR, SandraB, nSpeed, KeanuR, "The Matrix Reloaded"),
320			costarTag(KeanuR, SandraB, nSpeed, KeanuR, "The Matrix Revisited"),
321			costarTag(KeanuR, SandraB, nSpeed, KeanuR, "The Prince of Pennsylvania"),
322			costarTag(KeanuR, SandraB, nSpeed, KeanuR, "The Replacements"),
323			costarTag(KeanuR, SandraB, nSpeed, KeanuR, "Even Cowgirls Get the Blues"),
324			costarTag(KeanuR, SandraB, nSpeed, KeanuR, "Youngblood"),
325			costarTag(KeanuR, SandraB, nSpeed, KeanuR, "Bill & Ted's Bogus Journey"),
326			costarTag(KeanuR, SandraB, nSpeed, KeanuR, "Bill & Ted's Excellent Adventure"),
327			costarTag(KeanuR, SandraB, nSpeed, KeanuR, "Johnny Mnemonic"),
328			costarTag(KeanuR, SandraB, nSpeed, KeanuR, "The Devil's Advocate"),
329			costarTag(KeanuR, SandraB, nSpeed, KeanuR, "Thumbsucker"),
330			costarTag(KeanuR, SandraB, nSpeed, KeanuR, "I Love You to Death"),
331			costarTag(KeanuR, SandraB, nSpeed, KeanuR, "Bram Stoker's Dracula"),
332			costarTag(KeanuR, SandraB, nSpeed, KeanuR, "The Gift"),
333			costarTag(KeanuR, SandraB, nSpeed, KeanuR, "Little Buddha"),
334			costarTag(KeanuR, SandraB, nSpeed, KeanuR, "The Night Watchman"),
335			costarTag(KeanuR, SandraB, nSpeed, KeanuR, "Chain Reaction"),
336			costarTag(KeanuR, SandraB, nSpeed, KeanuR, "Babes in Toyland"),
337			costarTag(KeanuR, SandraB, nSpeed, KeanuR, "The Day the Earth Stood Still"),
338			costarTag(SandraB, SandraB, nSpeed, KeanuR, nSpeed),
339			costarTag(SandraB, SandraB, nSpeed, KeanuR, nLakeH),
340			costarTag("Dennis Hopper", SandraB, nSpeed, KeanuR, "River's Edge"),
341			costarTag("Dennis Hopper", SandraB, nSpeed, KeanuR, nSpeed),
342			costarTag("Jeff Daniels", SandraB, nSpeed, KeanuR, nSpeed),
343			costarTag("Joe Morton", SandraB, nSpeed, KeanuR, nSpeed),
344			costarTag("Alan Ruck", SandraB, nSpeed, KeanuR, nSpeed),
345			costarTag("Glenn Plummer", SandraB, nSpeed, KeanuR, nSpeed),
346			costarTag("Carlos Carrasco", SandraB, nSpeed, KeanuR, nSpeed),
347			costarTag("Beth Grant", SandraB, nSpeed, KeanuR, nSpeed),
348			costarTag("Richard Lineback", SandraB, nSpeed, KeanuR, nSpeed),
349			costarTag("Hawthorne James", SandraB, nSpeed, KeanuR, nSpeed),
350			costarTag("Jordan Lund", SandraB, nSpeed, KeanuR, nSpeed),
351			costarTag("Thomas Rosales, Jr.", SandraB, nSpeed, KeanuR, nSpeed),
352			costarTag(SandraB, SandraB, "Speed 2: Cruise Control", KeanuR, nSpeed),
353			costarTag(SandraB, SandraB, "Speed 2: Cruise Control", KeanuR, nLakeH),
354			costarTag("Glenn Plummer", SandraB, "Speed 2: Cruise Control", KeanuR, nSpeed),
355			costarTag(KeanuR, SandraB, nLakeH, KeanuR, "Flying"),
356			costarTag(KeanuR, SandraB, nLakeH, KeanuR, "The Animatrix"),
357			costarTag(KeanuR, SandraB, nLakeH, KeanuR, "Tune in Tomorrow"),
358			costarTag(KeanuR, SandraB, nLakeH, KeanuR, "The Last Time I Committed Suicide"),
359			costarTag(KeanuR, SandraB, nLakeH, KeanuR, "Constantine"),
360			costarTag(KeanuR, SandraB, nLakeH, KeanuR, "Permanent Record"),
361			costarTag(KeanuR, SandraB, nLakeH, KeanuR, "Dangerous Liaisons"),
362			costarTag(KeanuR, SandraB, nLakeH, KeanuR, "The Private Lives of Pippa Lee"),
363			costarTag(KeanuR, SandraB, nLakeH, KeanuR, "A Scanner Darkly"),
364			costarTag(KeanuR, SandraB, nLakeH, KeanuR, "A Walk in the Clouds"),
365			costarTag(KeanuR, SandraB, nLakeH, KeanuR, "Hardball"),
366			costarTag(KeanuR, SandraB, nLakeH, KeanuR, "Life Under Water"),
367			costarTag(KeanuR, SandraB, nLakeH, KeanuR, "Much Ado About Nothing"),
368			costarTag(KeanuR, SandraB, nLakeH, KeanuR, "My Own Private Idaho"),
369			costarTag(KeanuR, SandraB, nLakeH, KeanuR, "Parenthood"),
370			costarTag(KeanuR, SandraB, nLakeH, KeanuR, "Point Break"),
371			costarTag(KeanuR, SandraB, nLakeH, KeanuR, "Providence"),
372			costarTag(KeanuR, SandraB, nLakeH, KeanuR, "River's Edge"),
373			costarTag(KeanuR, SandraB, nLakeH, KeanuR, "Something's Gotta Give"),
374			costarTag(KeanuR, SandraB, nLakeH, KeanuR, nSpeed),
375			costarTag(KeanuR, SandraB, nLakeH, KeanuR, "Sweet November"),
376			costarTag(KeanuR, SandraB, nLakeH, KeanuR, nLakeH),
377			costarTag(KeanuR, SandraB, nLakeH, KeanuR, "The Matrix Reloaded"),
378			costarTag(KeanuR, SandraB, nLakeH, KeanuR, "The Matrix Revisited"),
379			costarTag(KeanuR, SandraB, nLakeH, KeanuR, "The Prince of Pennsylvania"),
380			costarTag(KeanuR, SandraB, nLakeH, KeanuR, "The Replacements"),
381			costarTag(KeanuR, SandraB, nLakeH, KeanuR, "Even Cowgirls Get the Blues"),
382			costarTag(KeanuR, SandraB, nLakeH, KeanuR, "Youngblood"),
383			costarTag(KeanuR, SandraB, nLakeH, KeanuR, "Bill & Ted's Bogus Journey"),
384			costarTag(KeanuR, SandraB, nLakeH, KeanuR, "Bill & Ted's Excellent Adventure"),
385			costarTag(KeanuR, SandraB, nLakeH, KeanuR, "Johnny Mnemonic"),
386			costarTag(KeanuR, SandraB, nLakeH, KeanuR, "The Devil's Advocate"),
387			costarTag(KeanuR, SandraB, nLakeH, KeanuR, "Thumbsucker"),
388			costarTag(KeanuR, SandraB, nLakeH, KeanuR, "I Love You to Death"),
389			costarTag(KeanuR, SandraB, nLakeH, KeanuR, "Bram Stoker's Dracula"),
390			costarTag(KeanuR, SandraB, nLakeH, KeanuR, "The Gift"),
391			costarTag(KeanuR, SandraB, nLakeH, KeanuR, "Little Buddha"),
392			costarTag(KeanuR, SandraB, nLakeH, KeanuR, "The Night Watchman"),
393			costarTag(KeanuR, SandraB, nLakeH, KeanuR, "Chain Reaction"),
394			costarTag(KeanuR, SandraB, nLakeH, KeanuR, "Babes in Toyland"),
395			costarTag(KeanuR, SandraB, nLakeH, KeanuR, "The Day the Earth Stood Still"),
396			costarTag(SandraB, SandraB, nLakeH, KeanuR, nSpeed),
397			costarTag(SandraB, SandraB, nLakeH, KeanuR, nLakeH),
398			costarTag("Christopher Plummer", SandraB, nLakeH, KeanuR, nLakeH),
399			costarTag("Dylan Walsh", SandraB, nLakeH, KeanuR, nLakeH),
400			costarTag("Shohreh Aghdashloo", SandraB, nLakeH, KeanuR, nLakeH),
401			costarTag("Lynn Collins", SandraB, nLakeH, KeanuR, nLakeH),
402			costarTag(SandraB, SandraB, "The Net", KeanuR, nSpeed),
403			costarTag(SandraB, SandraB, "The Net", KeanuR, nLakeH),
404			costarTag("Michelle Pfeiffer", SandraB, "The Prince of Egypt", KeanuR, "Dangerous Liaisons"),
405			costarTag(SandraB, SandraB, "The Prince of Egypt", KeanuR, nSpeed),
406			costarTag(SandraB, SandraB, "The Prince of Egypt", KeanuR, nLakeH),
407			costarTag("Steve Martin", SandraB, "The Prince of Egypt", KeanuR, "Parenthood"),
408			costarTag(SandraB, SandraB, "Two Weeks Notice", KeanuR, nSpeed),
409			costarTag(SandraB, SandraB, "Two Weeks Notice", KeanuR, nLakeH),
410			costarTag(SandraB, SandraB, "While You Were Sleeping", KeanuR, nSpeed),
411			costarTag(SandraB, SandraB, "While You Were Sleeping", KeanuR, nLakeH),
412			costarTag("Jack Warden", SandraB, "While You Were Sleeping", KeanuR, "The Replacements"),
413			costarTag(SandraB, SandraB, "28 Days", KeanuR, nSpeed),
414			costarTag(SandraB, SandraB, "28 Days", KeanuR, nLakeH),
415			costarTag(SandraB, SandraB, "Premonition", KeanuR, nSpeed),
416			costarTag(SandraB, SandraB, "Premonition", KeanuR, nLakeH),
417			costarTag("Peter Stormare", SandraB, "Premonition", KeanuR, "Constantine"),
418			costarTag(SandraB, SandraB, "Wrestling Ernest Hemingway", KeanuR, nSpeed),
419			costarTag(SandraB, SandraB, "Wrestling Ernest Hemingway", KeanuR, nLakeH),
420			costarTag(SandraB, SandraB, "Fire on the Amazon", KeanuR, nSpeed),
421			costarTag(SandraB, SandraB, "Fire on the Amazon", KeanuR, nLakeH),
422			costarTag("River Phoenix", SandraB, "The Thing Called Love", KeanuR, "My Own Private Idaho"),
423			costarTag("River Phoenix", SandraB, "The Thing Called Love", KeanuR, "I Love You to Death"),
424			costarTag(SandraB, SandraB, "The Thing Called Love", KeanuR, nSpeed),
425			costarTag(SandraB, SandraB, "The Thing Called Love", KeanuR, nLakeH),
426			costarTag(SandraB, SandraB, "In Love and War", KeanuR, nSpeed),
427			costarTag(SandraB, SandraB, "In Love and War", KeanuR, nLakeH),
428		},
429	},
430	{
431		message: "Save a number of predicates around a set of nodes",
432		query: `
433		g.V("_:9037", "_:49278", "_:44112", "_:44709", "_:43382").Save("</film/performance/character>", "char").Save("</film/performance/actor>", "act").SaveR("</film/film/starring>", "film").All()
434		`,
435		expect: []interface{}{
436			map[string]string{"act": "</en/humphrey_bogart>", "char": "Rick Blaine", "film": "</en/casablanca_1942>", "id": "_:9037"},
437			map[string]string{"act": "</en/humphrey_bogart>", "char": "Sam Spade", "film": "</en/the_maltese_falcon_1941>", "id": "_:49278"},
438			map[string]string{"act": "</en/humphrey_bogart>", "char": "Philip Marlowe", "film": "</en/the_big_sleep_1946>", "id": "_:44112"},
439			map[string]string{"act": "</en/humphrey_bogart>", "char": "Captain Queeg", "film": "</en/the_caine_mutiny_1954>", "id": "_:44709"},
440			map[string]string{"act": "</en/humphrey_bogart>", "char": "Charlie Allnut", "film": "</en/the_african_queen>", "id": "_:43382"},
441		},
442	},
443}
444
445const common = `
446var movie1 = g.V().Has("<name>", "The Net")
447var movie2 = g.V().Has("<name>", "Speed")
448var actor1 = g.V().Has("<name>", "Sandra Bullock")
449var actor2 = g.V().Has("<name>", "Keanu Reeves")
450
451// (film) -> starring -> (actor)
452var filmToActor = g.Morphism().Out("</film/film/starring>").Out("</film/performance/actor>")
453
454// (actor) -> starring -> [film -> starring -> (actor)]
455var coStars1 = g.Morphism().In("</film/performance/actor>").In("</film/film/starring>").Save("<name>","costar1_movie").Follow(filmToActor)
456var coStars2 = g.Morphism().In("</film/performance/actor>").In("</film/film/starring>").Save("<name>","costar2_movie").Follow(filmToActor)
457
458// Stars for the movies "The Net" and "Speed"
459var m1_actors = movie1.Save("<name>","movie1").Follow(filmToActor)
460var m2_actors = movie2.Save("<name>","movie2").Follow(filmToActor)
461`
462
463func prepare(t testing.TB, gen testutil.DatabaseFunc) (*graph.Handle, func()) {
464	qs, _, closer := gen(t)
465
466	qw, err := graph.NewQuadWriter("single", qs, nil)
467	if err != nil {
468		closer()
469		require.NoError(t, err)
470	}
471
472	h := &graph.Handle{QuadStore: qs, QuadWriter: qw}
473
474	const needsLoad = true // TODO: support local setup
475	if needsLoad {
476		start := time.Now()
477		var err error
478		for _, p := range []string{"./", "../"} {
479			err = internal.Load(h.QuadWriter, 0, filepath.Join(p, "../../data/30kmoviedata.nq.gz"), format)
480			if err == nil || !os.IsNotExist(err) {
481				break
482			}
483		}
484		if err != nil {
485			qw.Close()
486			closer()
487			require.NoError(t, err)
488		}
489		t.Logf("loaded data in %v", time.Since(start))
490	}
491	return h, func() {
492		qw.Close()
493		closer()
494	}
495}
496
497func checkQueries(t *testing.T, h *graph.Handle, timeout time.Duration) {
498	if h == nil {
499		t.Fatal("not initialized")
500	}
501	for _, test := range queries {
502		t.Run(test.message, func(t *testing.T) {
503			if testing.Short() && test.long {
504				t.SkipNow()
505			}
506			if test.skip {
507				t.SkipNow()
508			}
509			start := time.Now()
510			ses := gizmo.NewSession(h.QuadStore)
511			c := make(chan query.Result, 5)
512			ctx := context.Background()
513			if timeout > 0 {
514				var cancel func()
515				ctx, cancel = context.WithTimeout(ctx, timeout)
516				defer cancel()
517			}
518			go ses.Execute(ctx, test.query, c, -1)
519			var got []interface{}
520			for r := range c {
521				if err := r.Err(); err != nil {
522					t.Error("Error:", err)
523					continue
524				}
525				ses.Collate(r)
526				j, err := ses.Results()
527				if j == nil && err == nil {
528					continue
529				}
530				got = append(got, j.([]interface{})...)
531			}
532			t.Logf("%12v %v", time.Since(start), test.message)
533
534			if len(got) != len(test.expect) {
535				t.Errorf("Unexpected number of results, got:%d expect:%d on %s.", len(got), len(test.expect), test.message)
536				return
537			}
538			if unsortedEqual(got, test.expect) {
539				return
540			}
541			t.Errorf("Unexpected results for %s:\n", test.message)
542			for i := range got {
543				t.Errorf("\n\tgot:%#v\n\texpect:%#v\n", got[i], test.expect[i])
544			}
545		})
546	}
547}
548
549func unsortedEqual(got, expect []interface{}) bool {
550	gotList := convertToStringList(got)
551	expectList := convertToStringList(expect)
552	return reflect.DeepEqual(gotList, expectList)
553}
554
555func convertToStringList(in []interface{}) []string {
556	var out []string
557	for _, x := range in {
558		if xc, ok := x.(map[string]string); ok {
559			for k, v := range xc {
560				out = append(out, fmt.Sprint(k, ":", v))
561			}
562		} else {
563			for k, v := range x.(map[string]interface{}) {
564				out = append(out, fmt.Sprint(k, ":", v))
565			}
566		}
567	}
568	sort.Strings(out)
569	return out
570}
571
572func benchmarkQueries(b *testing.B, gen testutil.DatabaseFunc) {
573	h, closer := prepare(b, gen)
574	defer closer()
575
576	for _, bench := range queries {
577		b.Run(bench.message, func(b *testing.B) {
578			if testing.Short() && bench.long {
579				b.Skip()
580			}
581			b.StopTimer()
582			b.ResetTimer()
583			for i := 0; i < b.N; i++ {
584				c := make(chan query.Result, 5)
585				ctx := context.Background()
586				var cancel func()
587				if timeout > 0 {
588					ctx, cancel = context.WithTimeout(ctx, timeout)
589				}
590				ses := gizmo.NewSession(h.QuadStore)
591				b.StartTimer()
592				go ses.Execute(ctx, bench.query, c, -1)
593				n := 0
594				for range c {
595					n++
596				}
597				b.StopTimer()
598				if n != len(bench.expect) {
599					b.Fatalf("unexpected number of results: %d vs %d", n, len(bench.expect))
600				}
601				if cancel != nil {
602					cancel()
603				}
604			}
605		})
606	}
607}
608