Merge lp:~xavi-garcia-mena/go-unityscopes/go-subsearch into lp:go-unityscopes/v2

Proposed by Xavi Garcia
Status: Needs review
Proposed branch: lp:~xavi-garcia-mena/go-unityscopes/go-subsearch
Merge into: lp:go-unityscopes/v2
Prerequisite: lp:~xavi-garcia-mena/go-unityscopes/find-list-child-scopes
Diff against target: 1200 lines (+922/-32)
16 files modified
metadata_test.go (+2/-2)
reply.cpp (+46/-0)
reply.go (+51/-4)
result.cpp (+9/-0)
result.go (+12/-0)
scope.cpp (+6/-1)
shim.h (+3/-0)
tests/aggregated/aggregated.go (+141/-0)
tests/aggregated/aggregated.ini.in (+16/-0)
tests/aggregated/aggregated_test.py (+269/-0)
tests/aggregated/simple-scope-2.ini.in (+16/-0)
tests/aggregated/simple-scope.ini.in (+16/-0)
tests/goscope/goscope.go (+2/-22)
tests/simple-scope-2/simple-scope-2.go (+157/-0)
tests/simple-scope/simple-scope.go (+157/-0)
unityscope.go (+19/-3)
To merge this branch: bzr merge lp:~xavi-garcia-mena/go-unityscopes/go-subsearch
Reviewer Review Type Date Requested Status
Kyle Fazzari (community) Approve
James Henstridge Needs Fixing
Review via email: mp+260066@code.launchpad.net

Commit message

This branch adds full functionality to implement aggregated scopes in go.
It implements the subsearch method from SearchQueryBase.

The branch also includes scope-harness to verify aggregated scopes.

Description of the change

This branch adds full functionality to implement aggregated scopes in go.
It implements the subsearch method from SearchQueryBase.

The branch also includes scope-harness to verify aggregated scopes.

To post a comment you must log in.
76. By Xavi Garcia

Fixed typo and removed commented include

77. By Xavi Garcia

Removed some comments

78. By Xavi Garcia

Hide wait mechanism for child scopes to the developer. Now Aggregated scopes wait by default transparently

79. By Xavi Garcia

Removed binary

Revision history for this message
James Henstridge (jamesh) wrote :

I've left some inline comments. The main conceptual problem I see is tying the Subsearch API to the ScopeBase class, with other issues stemming from that (e.g. the WaitGroup on ScopeBase).

review: Needs Fixing
80. By Xavi Garcia

Moved Subsearch to SearchReply. Passed go fmt to the test files. NOTE: This branch is still work in progress, it's not fully functional

81. By Xavi Garcia

Moved subsearch method to SeachReply. Working version

82. By Xavi Garcia

Removed SetSearchReply and GetSearchReply from the subsearch listener, as they are no longer needed

83. By Xavi Garcia

Added Finished method to SearchListener. Some functions moved from goscope.go to reply.go.

Revision history for this message
Kyle Fazzari (kyrofa) wrote :

A few little things, comments inline.

review: Needs Fixing
84. By Xavi Garcia

added suggested changes

Revision history for this message
Xavi Garcia (xavi-garcia-mena) wrote :

Thanks for the review Kyle!

There is no reason to not use const & in the parameters, so I've add it :).

Regarding making the members private I didn't add it because that's a class in a cpp file that cannot be accessed from anywhere but that file.

I've added the private as it doesn't harm ;).

Revision history for this message
Kyle Fazzari (kyrofa) wrote :

Looks good to me!

Hmm, that class being inaccessible to others must be an artifact of cgo, then? Since it's not wrapped in an anonymous namespace? Regardless, encapsulation is still a best-practice, even if you're the only one using it :) . Worst case, it doesn't hurt; best case, it catches a mistake later.

review: Approve

Unmerged revisions

84. By Xavi Garcia

added suggested changes

83. By Xavi Garcia

Added Finished method to SearchListener. Some functions moved from goscope.go to reply.go.

82. By Xavi Garcia

Removed SetSearchReply and GetSearchReply from the subsearch listener, as they are no longer needed

81. By Xavi Garcia

Moved subsearch method to SeachReply. Working version

80. By Xavi Garcia

Moved Subsearch to SearchReply. Passed go fmt to the test files. NOTE: This branch is still work in progress, it's not fully functional

79. By Xavi Garcia

Removed binary

78. By Xavi Garcia

Hide wait mechanism for child scopes to the developer. Now Aggregated scopes wait by default transparently

77. By Xavi Garcia

Removed some comments

76. By Xavi Garcia

Fixed typo and removed commented include

75. By Xavi Garcia

Added Subsearch method to retrieve results from child scopes. Added scope-harness tests for aggregated scopes

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'metadata_test.go'
--- metadata_test.go 2015-05-21 14:58:07 +0000
+++ metadata_test.go 2015-06-17 07:29:19 +0000
@@ -170,6 +170,6 @@
170 c.Check(pageHeader["background"], Equals, "color:///#ffffff")170 c.Check(pageHeader["background"], Equals, "color:///#ffffff")
171 c.Check(pageHeader["divider-color"], Equals, "#b31217")171 c.Check(pageHeader["divider-color"], Equals, "#b31217")
172 c.Check(pageHeader["logo"], Equals, "unity-scope-youtube/build/src/logo.png")172 c.Check(pageHeader["logo"], Equals, "unity-scope-youtube/build/src/logo.png")
173 173
174 c.Check(scopeMetadata.Keywords, DeepEquals, []string{"music","video"})174 c.Check(scopeMetadata.Keywords, DeepEquals, []string{"music", "video"})
175}175}
176176
=== modified file 'reply.cpp'
--- reply.cpp 2015-03-25 07:46:21 +0000
+++ reply.cpp 2015-06-17 07:29:19 +0000
@@ -2,19 +2,50 @@
2#include <cstring>2#include <cstring>
3#include <iostream>3#include <iostream>
44
5#include <unity/scopes/ChildScope.h>
5#include <unity/scopes/PreviewReply.h>6#include <unity/scopes/PreviewReply.h>
7#include <unity/scopes/SearchListenerBase.h>
8#include <unity/scopes/SearchReplyProxyFwd.h>
6#include <unity/scopes/SearchReply.h>9#include <unity/scopes/SearchReply.h>
7#include <unity/scopes/Version.h>10#include <unity/scopes/Version.h>
811
9extern "C" {12extern "C" {
10#include "_cgo_export.h"13#include "_cgo_export.h"
11}14}
15#include "scope.h"
12#include "helpers.h"16#include "helpers.h"
13#include "smartptr_helper.h"17#include "smartptr_helper.h"
1418
15using namespace unity::scopes;19using namespace unity::scopes;
16using namespace gounityscopes::internal;20using namespace gounityscopes::internal;
1721
22class ResultForwarder : public SearchListenerBase
23{
24public:
25 ResultForwarder(GoInterface const& listener, GoInterface const& goreply, SearchReplyProxy const& upstream)
26 : SearchListenerBase()
27 , listener_(listener)
28 , reply_(goreply)
29 , upstream_(upstream){
30 }
31
32 void push(unity::scopes::CategorisedResult result) override {
33 if (callFilterResult(listener_, reinterpret_cast<_CategorisedResult*>(static_cast<Result*>(&result)))) {
34 upstream_->push(result);
35 }
36 }
37
38 virtual void finished(CompletionDetails const& details) override {
39 callSearchListenerFinished(reply_);
40 callListenerFinished(listener_);
41 }
42
43private:
44 GoInterface listener_;
45 GoInterface reply_;
46 SearchReplyProxy upstream_;
47};
48
18void init_search_reply_ptr(SharedPtrData dest, SharedPtrData src) {49void init_search_reply_ptr(SharedPtrData dest, SharedPtrData src) {
19 std::shared_ptr<SearchReply> reply = get_ptr<SearchReply>(src);50 std::shared_ptr<SearchReply> reply = get_ptr<SearchReply>(src);
20 init_ptr<SearchReply>(dest, reply);51 init_ptr<SearchReply>(dest, reply);
@@ -76,6 +107,21 @@
76#endif107#endif
77}108}
78109
110void search_reply_subsearch(_ChildScope *child_scope, void *query, void *department_id, _SearchMetadata *metadata,
111 SharedPtrData reply, _SearchQueryBase *search_query_base, void *pointer_to_iface, void *pointer_to_go_reply) {
112 ChildScope *c_child_scope = reinterpret_cast<ChildScope*>(child_scope);
113 std::string c_query = from_gostring(query);
114 std::string c_department_id = from_gostring(department_id);
115 SearchMetadata *c_metadata = reinterpret_cast<SearchMetadata*>(metadata);
116 auto c_reply = get_ptr<SearchReply>(reply);
117 SearchQueryBase *c_search_query_base = reinterpret_cast<SearchQueryBase*>(search_query_base);
118 GoInterface *listener = reinterpret_cast<GoInterface*>(pointer_to_iface);
119 GoInterface *go_reply = reinterpret_cast<GoInterface*>(pointer_to_go_reply);
120
121 auto forwarder = std::make_shared<ResultForwarder>(*listener, *go_reply, c_reply);
122 c_search_query_base->subsearch(*c_child_scope, c_query, c_department_id, FilterState(), *c_metadata, forwarder);
123}
124
79void init_preview_reply_ptr(SharedPtrData dest, SharedPtrData src) {125void init_preview_reply_ptr(SharedPtrData dest, SharedPtrData src) {
80 std::shared_ptr<PreviewReply> reply = get_ptr<PreviewReply>(src);126 std::shared_ptr<PreviewReply> reply = get_ptr<PreviewReply>(src);
81 init_ptr<PreviewReply>(dest, reply);127 init_ptr<PreviewReply>(dest, reply);
82128
=== modified file 'reply.go'
--- reply.go 2015-03-25 08:14:19 +0000
+++ reply.go 2015-06-17 07:29:19 +0000
@@ -6,18 +6,35 @@
6import (6import (
7 "encoding/json"7 "encoding/json"
8 "runtime"8 "runtime"
9 "sync"
9 "unsafe"10 "unsafe"
10)11)
1112
13type childScopeSubsearchSync interface {
14 finishSubsearch()
15 waitForChildScopesFinished()
16}
17
12// SearchReply is used to send results of search queries to the client.18// SearchReply is used to send results of search queries to the client.
13type SearchReply struct {19type SearchReply struct {
14 r C.SharedPtrData20 r C.SharedPtrData
15}21 s *C._SearchQueryBase
1622 waitGroup sync.WaitGroup
17func makeSearchReply(replyData *C.uintptr_t) *SearchReply {23}
24
25func (reply *SearchReply) finishSubsearch() {
26 reply.waitGroup.Done()
27}
28
29func (reply *SearchReply) waitForChildScopesFinished() {
30 reply.waitGroup.Wait()
31}
32
33func makeSearchReply(replyData *C.uintptr_t, searchQueryBase *C._SearchQueryBase) *SearchReply {
18 reply := new(SearchReply)34 reply := new(SearchReply)
19 runtime.SetFinalizer(reply, finalizeSearchReply)35 runtime.SetFinalizer(reply, finalizeSearchReply)
20 C.init_search_reply_ptr(&reply.r[0], replyData)36 C.init_search_reply_ptr(&reply.r[0], replyData)
37 reply.s = searchQueryBase
21 return reply38 return reply
22}39}
2340
@@ -99,6 +116,36 @@
99 return checkError(errorString)116 return checkError(errorString)
100}117}
101118
119// Subsearch method is used by aggregating scopes.
120// When an aggregator passes a query to its child scopes, it should
121// use Subsearch() instead of the normal Search()
122// that would be called by a client. Subsearch() takes care
123// of automatically forwarding query cancellation to child scopes.
124// This means that there is no need for an aggregating scope to
125// explicitly forward cancellation to child scopes.
126func (reply *SearchReply) Subsearch(childScope *ChildScope, query string, departmentId string, metadata *SearchMetadata, searchListener SearchListener) {
127 reply.waitGroup.Add(1)
128 var syncInterface childScopeSubsearchSync
129 syncInterface = reply
130 C.search_reply_subsearch(childScope.c, unsafe.Pointer(&query), unsafe.Pointer(&departmentId), (*C._SearchMetadata)(metadata.m), &reply.r[0], reply.s, unsafe.Pointer(&searchListener), unsafe.Pointer(&syncInterface))
131}
132
133//export callSearchListenerFinished
134func callSearchListenerFinished(reply childScopeSubsearchSync) {
135 reply.finishSubsearch()
136}
137
138//export callFilterResult
139func callFilterResult(listener SearchListener, result *C._CategorisedResult) bool {
140 goresult := makeCategorisedResult(result)
141 return listener.FilterResult(goresult)
142}
143
144//export callListenerFinished
145func callListenerFinished(listener SearchListener) {
146 listener.Finished()
147}
148
102// PreviewReply is used to send result previews to the client.149// PreviewReply is used to send result previews to the client.
103type PreviewReply struct {150type PreviewReply struct {
104 r C.SharedPtrData151 r C.SharedPtrData
105152
=== modified file 'result.cpp'
--- result.cpp 2015-03-02 05:43:42 +0000
+++ result.cpp 2015-06-17 07:29:19 +0000
@@ -47,3 +47,12 @@
47void result_set_intercept_activation(_Result *res) {47void result_set_intercept_activation(_Result *res) {
48 reinterpret_cast<Result*>(res)->set_intercept_activation();48 reinterpret_cast<Result*>(res)->set_intercept_activation();
49}49}
50
51void result_set_category(_Result *res, SharedPtrData category) {
52 Result *result = reinterpret_cast<Result*>(res);
53 CategorisedResult *cat_result = dynamic_cast<CategorisedResult*>(result);
54 if (cat_result) {
55 auto cat = get_ptr<Category>(category);
56 cat_result->set_category(cat);
57 }
58}
5059
=== modified file 'result.go'
--- result.go 2015-03-23 11:16:52 +0000
+++ result.go 2015-06-17 07:29:19 +0000
@@ -142,3 +142,15 @@
142func finalizeCategorisedResult(res *CategorisedResult) {142func finalizeCategorisedResult(res *CategorisedResult) {
143 finalizeResult(&res.Result)143 finalizeResult(&res.Result)
144}144}
145
146func makeCategorisedResult(res *C._Result) *CategorisedResult {
147 result := new(CategorisedResult)
148 runtime.SetFinalizer(result, finalizeCategorisedResult)
149 result.result = res
150 return result
151}
152
153// SetCategory Updates the category of this result.
154func (res *CategorisedResult) SetCategory(category *Category) {
155 C.result_set_category(res.result, &category.c[0])
156}
145157
=== modified file 'scope.cpp'
--- scope.cpp 2015-06-17 07:29:19 +0000
+++ scope.cpp 2015-06-17 07:29:19 +0000
@@ -1,11 +1,15 @@
1#include <unity/scopes/Category.h>1#include <unity/scopes/Category.h>
2#include <unity/scopes/utility/BufferedResultForwarder.h>
2extern "C" {3extern "C" {
3#include "_cgo_export.h"4#include "_cgo_export.h"
4}5}
5#include "scope.h"6#include "scope.h"
7#include "helpers.h"
6#include "smartptr_helper.h"8#include "smartptr_helper.h"
79
8using namespace unity::scopes;10using namespace unity::scopes;
11using namespace gounityscopes::internal;
12using namespace unity::scopes::utility;
913
10ScopeAdapter::ScopeAdapter(GoInterface goscope) : goscope(goscope) {14ScopeAdapter::ScopeAdapter(GoInterface goscope) : goscope(goscope) {
11}15}
@@ -40,9 +44,9 @@
40}44}
4145
42ChildScopeList ScopeAdapter::find_child_scopes() const {46ChildScopeList ScopeAdapter::find_child_scopes() const {
43 ChildScopeList child_scopes;
44 GoSlice go_slice = callFindChildScopes(goscope);47 GoSlice go_slice = callFindChildScopes(goscope);
45 ChildScope **data = reinterpret_cast<ChildScope **>(go_slice.data);48 ChildScope **data = reinterpret_cast<ChildScope **>(go_slice.data);
49 ChildScopeList child_scopes;
46 for(uint i = 0;i < go_slice.len; ++i)50 for(uint i = 0;i < go_slice.len; ++i)
47 {51 {
48 ChildScope child_scope(data[i]->id, data[i]->metadata, data[i]->enabled, data[i]->keywords);52 ChildScope child_scope(data[i]->id, data[i]->metadata, data[i]->enabled, data[i]->keywords);
@@ -68,6 +72,7 @@
68 reinterpret_cast<_CannedQuery*>(new CannedQuery(query())),72 reinterpret_cast<_CannedQuery*>(new CannedQuery(query())),
69 reinterpret_cast<_SearchMetadata*>(new SearchMetadata(search_metadata())),73 reinterpret_cast<_SearchMetadata*>(new SearchMetadata(search_metadata())),
70 const_cast<uintptr_t*>(reinterpret_cast<const uintptr_t*>(&reply)),74 const_cast<uintptr_t*>(reinterpret_cast<const uintptr_t*>(&reply)),
75 reinterpret_cast<_SearchQueryBase*>(this),
71 cancel_channel.get());76 cancel_channel.get());
72}77}
7378
7479
=== modified file 'shim.h'
--- shim.h 2015-06-17 07:29:19 +0000
+++ shim.h 2015-06-17 07:29:19 +0000
@@ -20,6 +20,7 @@
20typedef struct _QueryMetadata _QueryMetadata;20typedef struct _QueryMetadata _QueryMetadata;
21typedef struct _ColumnLayout _ColumnLayout;21typedef struct _ColumnLayout _ColumnLayout;
22typedef struct _ChildScope _ChildScope;22typedef struct _ChildScope _ChildScope;
23typedef struct _SearchQueryBase _SearchQueryBase;
23typedef void _ScopeBase;24typedef void _ScopeBase;
24typedef struct _GoString _GoString;25typedef struct _GoString _GoString;
2526
@@ -52,6 +53,7 @@
52void search_reply_register_departments(SharedPtrData reply, SharedPtrData dept);53void search_reply_register_departments(SharedPtrData reply, SharedPtrData dept);
53void search_reply_push(SharedPtrData reply, _CategorisedResult *result, char **error);54void search_reply_push(SharedPtrData reply, _CategorisedResult *result, char **error);
54void search_reply_push_filters(SharedPtrData reply, void *filters_json, void *filter_state_json, char **error);55void search_reply_push_filters(SharedPtrData reply, void *filters_json, void *filter_state_json, char **error);
56void search_reply_subsearch(_ChildScope *child_scope, void *query, void *department_id, _SearchMetadata *metadata, SharedPtrData reply, _SearchQueryBase *query_base, void *pointer_to_iface, void *pointer_to_go_reply);
5557
56/* PreviewReply objects */58/* PreviewReply objects */
57void init_preview_reply_ptr(SharedPtrData dest, SharedPtrData src);59void init_preview_reply_ptr(SharedPtrData dest, SharedPtrData src);
@@ -80,6 +82,7 @@
80/* CategorisedResult objects */82/* CategorisedResult objects */
81_Result *new_categorised_result(SharedPtrData category);83_Result *new_categorised_result(SharedPtrData category);
82void destroy_result(_Result *res);84void destroy_result(_Result *res);
85void result_set_category(_Result *res, SharedPtrData category);
8386
84/* Result objects */87/* Result objects */
85void *result_get_attr(_Result *res, void *attr, int *length, char **error);88void *result_get_attr(_Result *res, void *attr, int *length, char **error);
8689
=== added directory 'tests/aggregated'
=== added file 'tests/aggregated/aggregated.go'
--- tests/aggregated/aggregated.go 1970-01-01 00:00:00 +0000
+++ tests/aggregated/aggregated.go 2015-06-17 07:29:19 +0000
@@ -0,0 +1,141 @@
1package main
2
3import (
4 "fmt"
5 "launchpad.net/go-unityscopes/v2"
6 "log"
7)
8
9const searchCategoryTemplate = `{
10 "schema-version": 1,
11 "template": {
12 "category-layout": "grid",
13 "card-size": "small"
14 },
15 "components": {
16 "title": "title",
17 "art": "art",
18 "subtitle": "username"
19 }
20}`
21
22const searchCategoryTemplate2 = `{
23 "schema-version": 1,
24 "template": {
25 "category-layout": "grid",
26 "card-size": "small"
27 },
28 "components": {
29 "title": "title",
30 "art": "art",
31 "subtitle": "username"
32 }
33}`
34
35// SCOPE ***********************************************************************
36
37var scope_interface scopes.AggregatedScope
38
39type MyScope struct {
40 base *scopes.ScopeBase
41}
42
43type TestScopeListener struct {
44 category *scopes.Category
45 id string
46
47}
48
49func NewTestScopeListener(id string, category *scopes.Category) *TestScopeListener {
50 listener := new(TestScopeListener)
51 listener.SetCategory(category)
52 listener.id = id
53 return listener
54}
55
56func (l *TestScopeListener) SetCategory(category *scopes.Category) {
57 l.category = category
58}
59
60func (l *TestScopeListener) Finished() {
61 fmt.Printf("SUBSEARCH [%s] FINISHED\n", l.id)
62}
63
64func (l *TestScopeListener) FilterResult(result *scopes.CategorisedResult) bool {
65 fmt.Print("PUSHING RESULT\n")
66 result.SetCategory(l.category)
67 fmt.Print(result.URI())
68 fmt.Print("\n")
69 // we just filter one of the results to verify
70 // this method is taken into account when pushing results
71 return result.Title() != "TEST"
72}
73
74func (s *MyScope) Preview(result *scopes.Result, metadata *scopes.ActionMetadata, reply *scopes.PreviewReply, cancelled <-chan bool) error {
75 return nil
76}
77
78func (s *MyScope) Search(query *scopes.CannedQuery, metadata *scopes.SearchMetadata, reply *scopes.SearchReply, cancelled <-chan bool) error {
79 // THIS IS JUST A TEST ON THE STDOUT
80 childScopes := s.base.ChildScopes()
81 fmt.Print("CHILD SCOPES ****************************************************************\n")
82 for k := range childScopes {
83 fmt.Print(childScopes[k].Id())
84 fmt.Print("\n")
85 // create the cateogry for this child scope
86 cat := reply.RegisterCategory("cat_"+childScopes[k].Id(), fmt.Sprintf("Category %s", childScopes[k].Id()), "", searchCategoryTemplate)
87 // create a search listener
88 listener := NewTestScopeListener(childScopes[k].Id(), cat)
89 reply.Subsearch(childScopes[k], query.QueryString(), "", metadata, listener)
90 }
91 fmt.Print("CHILD SCOPES ****************************************************************\n")
92 fmt.Print("EXIT SEARCH -------------------------------------\n")
93 return nil
94}
95
96func (s *MyScope) FindChildScopes() []*scopes.ChildScope {
97 child_scopes := make([]*scopes.ChildScope, 0)
98
99 listScopes := s.base.ListRegistryScopes()
100 for _, item := range listScopes {
101 fmt.Print("Adding.... ")
102 fmt.Print(item.ScopeId)
103 fmt.Print("\n")
104 if item.ScopeId != "aggregated" {
105 fmt.Print("Done. \n")
106 child_scope := scopes.NewChildScope(item.ScopeId, item, true, []string{"music"})
107 child_scopes = append(child_scopes, child_scope)
108 }
109 }
110 return child_scopes
111}
112
113func (s *MyScope) SetScopeBase(base *scopes.ScopeBase) {
114 s.base = base
115
116 // THIS IS JUST A TEST ON THE STDOUT
117 listScopes := s.base.ListRegistryScopes()
118 fmt.Print("LIST SCOPES ****************************************************************\n")
119 for k := range listScopes {
120 fmt.Print(k)
121 fmt.Print("\n")
122 fmt.Print(*listScopes[k])
123 fmt.Print("\n")
124 }
125 fmt.Print("LIST SCOPES ****************************************************************\n")
126}
127
128func (s *MyScope) GetScopeBase() *scopes.ScopeBase {
129 return s.base
130}
131
132// MAIN ************************************************************************
133
134func main() {
135 var sc MyScope
136 scope_interface = &sc
137
138 if err := scopes.Run(&MyScope{}); err != nil {
139 log.Fatalln(err)
140 }
141}
0142
=== added file 'tests/aggregated/aggregated.ini.in'
--- tests/aggregated/aggregated.ini.in 1970-01-01 00:00:00 +0000
+++ tests/aggregated/aggregated.ini.in 2015-06-17 07:29:19 +0000
@@ -0,0 +1,16 @@
1[ScopeConfig]
2ScopeRunner=$GOPATH$/bin/aggregated --runtime %R --scope %S
3DisplayName = mock.DisplayName
4Description = mock.Description
5Author = test
6Art = mock.Art
7Icon = /mock.Icon
8SearchHint = mock.SearchHint
9HotKey = mock.HotKey
10IsAggregator=false
11
12[Appearance]
13PageHeader.Logo = http://assets.ubuntu.com/sites/ubuntu/1110/u/img/logos/logo-ubuntu-orange.svg
14PageHeader.ForegroundColor = white
15PageHeader.Background = color://black
16ShapeImages = false
0\ No newline at end of file17\ No newline at end of file
118
=== added file 'tests/aggregated/aggregated_test.py'
--- tests/aggregated/aggregated_test.py 1970-01-01 00:00:00 +0000
+++ tests/aggregated/aggregated_test.py 2015-06-17 07:29:19 +0000
@@ -0,0 +1,269 @@
1#!/usr/bin/env python3
2#
3# Copyright (C) 2015 Canonical Ltd.
4# Author: Xavi Garcia <xavi.garcia.mena@canonical.com>
5#
6# This program is free software; you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation; version 3.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program. If not, see <http://www.gnu.org/licenses/>.
17#
18
19"""
20This is a test of an aggregated go-bindings scope.
21"""
22
23from scope_harness import ( ScopeHarness, CategoryMatcher, CategoryMatcherMode, CategoryListMatcher,
24 CategoryListMatcherMode, ResultMatcher, PreviewMatcher, PreviewWidgetMatcher,
25 PreviewColumnMatcher, PreviewView,
26 Parameters, DepartmentMatcher, ChildDepartmentMatcher )
27from scope_harness.testing import ScopeHarnessTestCase
28import unittest
29import sys
30import os
31from shutil import copyfile
32import inspect
33
34TEST_DATA_DIR = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
35SCOPE_NAME = "aggregated"
36CONFIG_FILE = "aggregated.ini"
37
38CHILD_SCOPE_NAME_1 = "simple-scope"
39CHILD_SCOPE_NAME_2 = "simple-scope-2"
40CHILD_SCOPE_CONFIG_FILE = "simple-scope.ini"
41CHILD_SCOPE_CONFIG_FILE_2 = "simple-scope-2.ini"
42
43def remove_ini_file(config_file):
44 os.remove(TEST_DATA_DIR + "/" + config_file)
45
46def copy_binary(scope_name):
47 binary = os.environ.get('GOPATH') + "/bin/" + scope_name
48 if not os.path.exists(binary):
49 raise Exception("The binary %s does not exist." % binary)
50 copyfile(binary, TEST_DATA_DIR + "/" + scope_name)
51
52def remove_binary(scope_name):
53 os.remove(TEST_DATA_DIR + "/" + scope_name)
54
55def prepare_ini_file(config_file):
56 copy_and_set_go_path(TEST_DATA_DIR + "/" + config_file + ".in", TEST_DATA_DIR + "/" + config_file)
57
58def copy_and_set_go_path(infile, outfile):
59 with open(outfile, "wt") as fout:
60 with open(infile, "rt") as fin:
61 for line in fin:
62 fout.write(line.replace("$GOPATH$", os.environ.get('GOPATH')))
63
64class ResultsTest(ScopeHarnessTestCase):
65 @classmethod
66 def setUpClass(cls):
67 copy_binary(SCOPE_NAME)
68 copy_binary(CHILD_SCOPE_NAME_1)
69 copy_binary(CHILD_SCOPE_NAME_2)
70
71 prepare_ini_file(CONFIG_FILE)
72 prepare_ini_file(CHILD_SCOPE_CONFIG_FILE)
73 prepare_ini_file(CHILD_SCOPE_CONFIG_FILE_2)
74 cls.harness = ScopeHarness.new_from_scope_list(Parameters([
75 TEST_DATA_DIR + "/" + CONFIG_FILE,
76 TEST_DATA_DIR + "/" + CHILD_SCOPE_CONFIG_FILE,
77 TEST_DATA_DIR + "/" + CHILD_SCOPE_CONFIG_FILE_2
78 ]))
79 cls.view = cls.harness.results_view
80 cls.view.active_scope = SCOPE_NAME
81 cls.view.search_query = ""
82
83 @classmethod
84 def tearDownClass(cls):
85 remove_ini_file(CONFIG_FILE)
86 remove_ini_file(CHILD_SCOPE_CONFIG_FILE)
87 remove_ini_file(CHILD_SCOPE_CONFIG_FILE_2)
88
89 remove_binary(SCOPE_NAME)
90 remove_binary(CHILD_SCOPE_NAME_1)
91 remove_binary(CHILD_SCOPE_NAME_2)
92
93 def test_basic_result(self):
94 self.assertMatchResult(CategoryListMatcher()
95 .has_at_least(1)
96 .mode(CategoryListMatcherMode.BY_ID)
97 .category(CategoryMatcher("cat_simple-scope")
98 .has_at_least(1)
99 .mode(CategoryMatcherMode.BY_URI)
100 .title("Category simple-scope")
101 .icon("")
102 )
103 .match(self.view.categories)
104 )
105
106 self.assertMatchResult(CategoryListMatcher()
107 .has_at_least(1)
108 .mode(CategoryListMatcherMode.BY_ID)
109 .category(CategoryMatcher("cat_simple-scope-2")
110 .has_at_least(1)
111 .mode(CategoryMatcherMode.BY_URI)
112 .title("Category simple-scope-2")
113 .icon("")
114 )
115 .match(self.view.categories)
116 )
117
118 def test_scope_properties(self):
119 self.assertEqual(self.view.scope_id, SCOPE_NAME)
120 self.assertEqual(self.view.display_name, 'mock.DisplayName')
121 self.assertEqual(self.view.icon_hint, '/mock.Icon')
122 self.assertEqual(self.view.description, 'mock.Description')
123 self.assertEqual(self.view.search_hint, 'mock.SearchHint')
124 self.assertEqual(self.view.shortcut, 'mock.HotKey')
125
126 customizations = self.view.customizations
127 self.assertTrue(len(customizations)!=0)
128
129 header_customizations = customizations["page-header"]
130 self.assertEqual(header_customizations["logo"], "http://assets.ubuntu.com/sites/ubuntu/1110/u/img/logos/logo-ubuntu-orange.svg")
131 self.assertEqual(header_customizations["background"], "color://black")
132 self.assertEqual(header_customizations["foreground-color"], "white")
133 self.assertEqual(customizations["shape-images"], False)
134
135 def test_result_data(self):
136 self.assertMatchResult(CategoryListMatcher()
137 .has_at_least(1)
138 .mode(CategoryListMatcherMode.BY_ID)
139 .category(CategoryMatcher("cat_simple-scope")
140 .has_at_least(1)
141 .mode(CategoryMatcherMode.BY_URI)
142 .result(ResultMatcher("http://localhost2/")
143 .properties({'test_value_bool': False})
144 .properties({'test_value_string': "test_value2"})
145 .properties({'test_value_int': 2000})
146 .properties({'test_value_float': 2.1})
147 .dnd_uri("http://localhost_dnduri2")
148 .properties({'test_value_map': {'value1':1,'value2':'string_value'}})
149 .properties({'test_value_array': [1999,"string_value"]})
150 .art("https://pbs.twimg.com/profile_images/1117820653/5ttls5.jpg.png")
151 ))
152 .match(self.view.categories)
153 )
154
155 self.assertMatchResult(CategoryListMatcher()
156 .has_at_least(1)
157 .mode(CategoryListMatcherMode.BY_ID)
158 .category(CategoryMatcher("cat_simple-scope-2")
159 .has_at_least(1)
160 .mode(CategoryMatcherMode.BY_URI)
161 .result(ResultMatcher("http://TEST-FROM-SCOPE-2_1/")
162 .properties({'test_value_bool': True})
163 .properties({'test_value_string': "test_value"})
164 .properties({'test_value_int': 1999})
165 .properties({'test_value_float': 1.999})
166 .dnd_uri("http://localhost_dnduri")
167 .art("https://help.ubuntu.com/stable/ubuntu-help/figures/ubuntu-logo.png")
168 ))
169 .match(self.view.categories)
170 )
171
172 self.assertMatchResult(CategoryListMatcher()
173 .has_at_least(1)
174 .mode(CategoryListMatcherMode.BY_ID)
175 .category(CategoryMatcher("cat_simple-scope-2")
176 .has_at_least(1)
177 .mode(CategoryMatcherMode.BY_URI)
178 .result(ResultMatcher("http://TEST-FROM-SCOPE-2_2/")
179 .properties({'test_value_bool': False})
180 .properties({'test_value_string': "test_value2"})
181 .properties({'test_value_int': 2000})
182 .properties({'test_value_float': 2.1})
183 .dnd_uri("http://localhost_dnduri2")
184 .properties({'test_value_map': {'value1':1,'value2':'string_value'}})
185 .properties({'test_value_array': [1999,"string_value"]})
186 .art("https://help.ubuntu.com/stable/ubuntu-help/figures/ubuntu-logo.png")
187 ))
188 .match(self.view.categories)
189 )
190
191 def test_result_data_query(self):
192 self.view.active_scope = SCOPE_NAME
193 test_query = "test_query"
194 self.view.search_query = test_query
195
196 self.assertMatchResult(CategoryListMatcher()
197 .has_at_least(1)
198 .mode(CategoryListMatcherMode.BY_ID)
199 .category(CategoryMatcher("cat_simple-scope")
200 .has_at_least(1)
201 .mode(CategoryMatcherMode.BY_URI)
202 .result(ResultMatcher("http://localhost/" + test_query)
203 .properties({'test_value_bool': True})
204 .properties({'test_value_string': "test_value" + test_query})
205 .properties({'test_value_int': 1999})
206 .properties({'test_value_float': 1.999})
207 .dnd_uri("http://localhost_dnduri" + test_query)
208 .art("https://pbs.twimg.com/profile_images/1117820653/5ttls5.jpg.png")
209 ))
210 .match(self.view.categories)
211 )
212
213 self.assertMatchResult(CategoryListMatcher()
214 .has_at_least(1)
215 .mode(CategoryListMatcherMode.BY_ID)
216 .category(CategoryMatcher("cat_simple-scope")
217 .has_at_least(1)
218 .mode(CategoryMatcherMode.BY_URI)
219 .result(ResultMatcher("http://localhost2/" + test_query)
220 .properties({'test_value_bool': False})
221 .properties({'test_value_string': "test_value2" + test_query})
222 .properties({'test_value_int': 2000})
223 .properties({'test_value_float': 2.1})
224 .dnd_uri("http://localhost_dnduri2" + test_query)
225 .properties({'test_value_map': {'value1':1,'value2':'string_value'}})
226 .properties({'test_value_array': [1999,"string_value"]})
227 .art("https://pbs.twimg.com/profile_images/1117820653/5ttls5.jpg.png")
228 ))
229 .match(self.view.categories)
230 )
231
232 self.assertMatchResult(CategoryListMatcher()
233 .has_at_least(1)
234 .mode(CategoryListMatcherMode.BY_ID)
235 .category(CategoryMatcher("cat_simple-scope-2")
236 .has_at_least(1)
237 .mode(CategoryMatcherMode.BY_URI)
238 .result(ResultMatcher("http://TEST-FROM-SCOPE-2_1/" + test_query)
239 .properties({'test_value_bool': True})
240 .properties({'test_value_string': "test_value" + test_query})
241 .properties({'test_value_int': 1999})
242 .properties({'test_value_float': 1.999})
243 .dnd_uri("http://localhost_dnduri" + test_query)
244 .art("https://help.ubuntu.com/stable/ubuntu-help/figures/ubuntu-logo.png")
245 ))
246 .match(self.view.categories)
247 )
248
249 self.assertMatchResult(CategoryListMatcher()
250 .has_at_least(1)
251 .mode(CategoryListMatcherMode.BY_ID)
252 .category(CategoryMatcher("cat_simple-scope-2")
253 .has_at_least(1)
254 .mode(CategoryMatcherMode.BY_URI)
255 .result(ResultMatcher("http://TEST-FROM-SCOPE-2_2/" + test_query)
256 .properties({'test_value_bool': False})
257 .properties({'test_value_string': "test_value2" + test_query})
258 .properties({'test_value_int': 2000})
259 .properties({'test_value_float': 2.1})
260 .dnd_uri("http://localhost_dnduri2" + test_query)
261 .properties({'test_value_map': {'value1':1,'value2':'string_value'}})
262 .properties({'test_value_array': [1999,"string_value"]})
263 .art("https://help.ubuntu.com/stable/ubuntu-help/figures/ubuntu-logo.png")
264 ))
265 .match(self.view.categories)
266 )
267
268if __name__ == '__main__':
269 unittest.main(argv = sys.argv[:1])
0270
=== added file 'tests/aggregated/simple-scope-2.ini.in'
--- tests/aggregated/simple-scope-2.ini.in 1970-01-01 00:00:00 +0000
+++ tests/aggregated/simple-scope-2.ini.in 2015-06-17 07:29:19 +0000
@@ -0,0 +1,16 @@
1[ScopeConfig]
2ScopeRunner=$GOPATH$/bin/simple-scope-2 --runtime %R --scope %S
3DisplayName = mock.DisplayName
4Description = mock.Description
5Author = test
6Art = mock.Art
7Icon = /mock.Icon
8SearchHint = mock.SearchHint
9HotKey = mock.HotKey
10IsAggregator=false
11
12[Appearance]
13PageHeader.Logo = http://assets.ubuntu.com/sites/ubuntu/1110/u/img/logos/logo-ubuntu-orange.svg
14PageHeader.ForegroundColor = white
15PageHeader.Background = color://black
16ShapeImages = false
017
=== added file 'tests/aggregated/simple-scope.ini.in'
--- tests/aggregated/simple-scope.ini.in 1970-01-01 00:00:00 +0000
+++ tests/aggregated/simple-scope.ini.in 2015-06-17 07:29:19 +0000
@@ -0,0 +1,16 @@
1[ScopeConfig]
2ScopeRunner=$GOPATH$/bin/simple-scope --runtime %R --scope %S
3DisplayName = mock.DisplayName
4Description = mock.Description
5Author = test
6Art = mock.Art
7Icon = /mock.Icon
8SearchHint = mock.SearchHint
9HotKey = mock.HotKey
10IsAggregator=false
11
12[Appearance]
13PageHeader.Logo = http://assets.ubuntu.com/sites/ubuntu/1110/u/img/logos/logo-ubuntu-orange.svg
14PageHeader.ForegroundColor = white
15PageHeader.Background = color://black
16ShapeImages = false
017
=== modified file 'tests/goscope/goscope.go'
--- tests/goscope/goscope.go 2015-06-17 07:29:19 +0000
+++ tests/goscope/goscope.go 2015-06-17 07:29:19 +0000
@@ -3,7 +3,6 @@
3import (3import (
4 "launchpad.net/go-unityscopes/v2"4 "launchpad.net/go-unityscopes/v2"
5 "log"5 "log"
6 "fmt"
7)6)
87
9const searchCategoryTemplate = `{8const searchCategoryTemplate = `{
@@ -21,7 +20,7 @@
2120
22// SCOPE ***********************************************************************21// SCOPE ***********************************************************************
2322
24var scope_interface scopes.AggregatedScope23var scope_interface scopes.Scope
2524
26type MyScope struct {25type MyScope struct {
27 base *scopes.ScopeBase26 base *scopes.ScopeBase
@@ -102,15 +101,7 @@
102 var filterState scopes.FilterState101 var filterState scopes.FilterState
103 // for RTM version of libunity-scopes we should see a log message102 // for RTM version of libunity-scopes we should see a log message
104 reply.PushFilters([]scopes.Filter{filter1}, filterState)103 reply.PushFilters([]scopes.Filter{filter1}, filterState)
105 104
106 // THIS IS JUST A TEST ON THE STDOUT
107 childScopes := s.base.ChildScopes()
108 fmt.Print("CHILD SCOPES ****************************************************************\n")
109 for k := range childScopes {
110 fmt.Print(childScopes[k].Id())
111 fmt.Print("\n")
112 }
113 fmt.Print("CHILD SCOPES ****************************************************************\n")
114 return s.AddQueryResults(reply, query.QueryString())105 return s.AddQueryResults(reply, query.QueryString())
115}106}
116107
@@ -127,17 +118,6 @@
127118
128func (s *MyScope) SetScopeBase(base *scopes.ScopeBase) {119func (s *MyScope) SetScopeBase(base *scopes.ScopeBase) {
129 s.base = base120 s.base = base
130
131 // THIS IS JUST A TEST ON THE STDOUT
132 listScopes := s.base.ListRegistryScopes()
133 fmt.Print("LIST SCOPES ****************************************************************\n")
134 for k := range listScopes {
135 fmt.Print(k)
136 fmt.Print("\n")
137 fmt.Print(*listScopes[k])
138 fmt.Print("\n")
139 }
140 fmt.Print("LIST SCOPES ****************************************************************\n")
141}121}
142122
143// RESULTS *********************************************************************123// RESULTS *********************************************************************
144124
=== added directory 'tests/simple-scope'
=== added directory 'tests/simple-scope-2'
=== added file 'tests/simple-scope-2/simple-scope-2.go'
--- tests/simple-scope-2/simple-scope-2.go 1970-01-01 00:00:00 +0000
+++ tests/simple-scope-2/simple-scope-2.go 2015-06-17 07:29:19 +0000
@@ -0,0 +1,157 @@
1package main
2
3import (
4 "launchpad.net/go-unityscopes/v2"
5 "log"
6)
7
8const searchCategoryTemplate = `{
9 "schema-version": 1,
10 "template": {
11 "category-layout": "grid",
12 "card-size": "small"
13 },
14 "components": {
15 "title": "title",
16 "art": "art",
17 "subtitle": "username"
18 }
19}`
20
21// SCOPE ***********************************************************************
22
23var scope_interface scopes.Scope
24
25type MyScope struct {
26 base *scopes.ScopeBase
27}
28
29func (s *MyScope) Preview(result *scopes.Result, metadata *scopes.ActionMetadata, reply *scopes.PreviewReply, cancelled <-chan bool) error {
30 layout1col := scopes.NewColumnLayout(1)
31 layout2col := scopes.NewColumnLayout(2)
32 layout3col := scopes.NewColumnLayout(3)
33
34 // Single column layout
35 layout1col.AddColumn("image", "header", "summary", "actions")
36
37 // Two column layout
38 layout2col.AddColumn("image")
39 layout2col.AddColumn("header", "summary", "actions")
40
41 // Three cokumn layout
42 layout3col.AddColumn("image")
43 layout3col.AddColumn("header", "summary", "actions")
44 layout3col.AddColumn()
45
46 // Register the layouts we just created
47 reply.RegisterLayout(layout1col, layout2col, layout3col)
48
49 header := scopes.NewPreviewWidget("header", "header")
50
51 // It has title and a subtitle properties
52 header.AddAttributeMapping("title", "title")
53 header.AddAttributeMapping("subtitle", "subtitle")
54
55 // Define the image section
56 image := scopes.NewPreviewWidget("image", "image")
57 // It has a single source property, mapped to the result's art property
58 image.AddAttributeMapping("source", "art")
59
60 // Define the summary section
61 description := scopes.NewPreviewWidget("summary", "text")
62 // It has a text property, mapped to the result's description property
63 description.AddAttributeMapping("text", "description")
64
65 // build variant map.
66 tuple1 := make(map[string]interface{})
67 tuple1["id"] = "open"
68 tuple1["label"] = "Open"
69 tuple1["uri"] = "application:///tmp/non-existent.desktop"
70
71 tuple2 := make(map[string]interface{})
72 tuple1["id"] = "download"
73 tuple1["label"] = "Download"
74
75 tuple3 := make(map[string]interface{})
76 tuple1["id"] = "hide"
77 tuple1["label"] = "Hide"
78
79 actions := scopes.NewPreviewWidget("actions", "actions")
80 actions.AddAttributeValue("actions", []interface{}{tuple1, tuple2, tuple3})
81
82 var scope_data string
83 metadata.ScopeData(scope_data)
84 if len(scope_data) > 0 {
85 extra := scopes.NewPreviewWidget("extra", "text")
86 extra.AddAttributeValue("text", "test Text")
87 reply.PushWidgets(header, image, description, actions, extra)
88 } else {
89 reply.PushWidgets(header, image, description, actions)
90 }
91
92 return nil
93}
94
95func (s *MyScope) Search(query *scopes.CannedQuery, metadata *scopes.SearchMetadata, reply *scopes.SearchReply, cancelled <-chan bool) error {
96 return s.AddQueryResults(reply, query.QueryString())
97}
98
99func (s *MyScope) SetScopeBase(base *scopes.ScopeBase) {
100 s.base = base
101}
102
103// RESULTS *********************************************************************
104
105func (s *MyScope) AddQueryResults(reply *scopes.SearchReply, query string) error {
106 cat := reply.RegisterCategory("category", "Category", "", searchCategoryTemplate)
107
108 result := scopes.NewCategorisedResult(cat)
109 result.SetURI("http://TEST-FROM-SCOPE-2_1/" + query)
110 result.SetDndURI("http://localhost_dnduri" + query)
111 result.SetTitle("TEST-FROM-SCOPE-2_1" + query)
112 result.SetArt("https://help.ubuntu.com/stable/ubuntu-help/figures/ubuntu-logo.png")
113 result.Set("test_value_bool", true)
114 result.Set("test_value_string", "test_value"+query)
115 result.Set("test_value_int", 1999)
116 result.Set("test_value_float", 1.999)
117 if err := reply.Push(result); err != nil {
118 return err
119 }
120
121 result.SetURI("http://TEST-FROM-SCOPE-2_2/" + query)
122 result.SetDndURI("http://localhost_dnduri2" + query)
123 result.SetTitle("TEST-FROM-SCOPE-2_1")
124 result.SetArt("https://help.ubuntu.com/stable/ubuntu-help/figures/ubuntu-logo.png")
125 result.Set("test_value_bool", false)
126 result.Set("test_value_string", "test_value2"+query)
127 result.Set("test_value_int", 2000)
128 result.Set("test_value_float", 2.100)
129
130 // add a variant map value
131 m := make(map[string]interface{})
132 m["value1"] = 1
133 m["value2"] = "string_value"
134 result.Set("test_value_map", m)
135
136 // add a variant array value
137 l := make([]interface{}, 0)
138 l = append(l, 1999)
139 l = append(l, "string_value")
140 result.Set("test_value_array", l)
141 if err := reply.Push(result); err != nil {
142 return err
143 }
144
145 return nil
146}
147
148// MAIN ************************************************************************
149
150func main() {
151 var sc MyScope
152 scope_interface = &sc
153
154 if err := scopes.Run(&MyScope{}); err != nil {
155 log.Fatalln(err)
156 }
157}
0158
=== added file 'tests/simple-scope/simple-scope.go'
--- tests/simple-scope/simple-scope.go 1970-01-01 00:00:00 +0000
+++ tests/simple-scope/simple-scope.go 2015-06-17 07:29:19 +0000
@@ -0,0 +1,157 @@
1package main
2
3import (
4 "launchpad.net/go-unityscopes/v2"
5 "log"
6)
7
8const searchCategoryTemplate = `{
9 "schema-version": 1,
10 "template": {
11 "category-layout": "grid",
12 "card-size": "small"
13 },
14 "components": {
15 "title": "title",
16 "art": "art",
17 "subtitle": "username"
18 }
19}`
20
21// SCOPE ***********************************************************************
22
23var scope_interface scopes.Scope
24
25type MyScope struct {
26 base *scopes.ScopeBase
27}
28
29func (s *MyScope) Preview(result *scopes.Result, metadata *scopes.ActionMetadata, reply *scopes.PreviewReply, cancelled <-chan bool) error {
30 layout1col := scopes.NewColumnLayout(1)
31 layout2col := scopes.NewColumnLayout(2)
32 layout3col := scopes.NewColumnLayout(3)
33
34 // Single column layout
35 layout1col.AddColumn("image", "header", "summary", "actions")
36
37 // Two column layout
38 layout2col.AddColumn("image")
39 layout2col.AddColumn("header", "summary", "actions")
40
41 // Three cokumn layout
42 layout3col.AddColumn("image")
43 layout3col.AddColumn("header", "summary", "actions")
44 layout3col.AddColumn()
45
46 // Register the layouts we just created
47 reply.RegisterLayout(layout1col, layout2col, layout3col)
48
49 header := scopes.NewPreviewWidget("header", "header")
50
51 // It has title and a subtitle properties
52 header.AddAttributeMapping("title", "title")
53 header.AddAttributeMapping("subtitle", "subtitle")
54
55 // Define the image section
56 image := scopes.NewPreviewWidget("image", "image")
57 // It has a single source property, mapped to the result's art property
58 image.AddAttributeMapping("source", "art")
59
60 // Define the summary section
61 description := scopes.NewPreviewWidget("summary", "text")
62 // It has a text property, mapped to the result's description property
63 description.AddAttributeMapping("text", "description")
64
65 // build variant map.
66 tuple1 := make(map[string]interface{})
67 tuple1["id"] = "open"
68 tuple1["label"] = "Open"
69 tuple1["uri"] = "application:///tmp/non-existent.desktop"
70
71 tuple2 := make(map[string]interface{})
72 tuple1["id"] = "download"
73 tuple1["label"] = "Download"
74
75 tuple3 := make(map[string]interface{})
76 tuple1["id"] = "hide"
77 tuple1["label"] = "Hide"
78
79 actions := scopes.NewPreviewWidget("actions", "actions")
80 actions.AddAttributeValue("actions", []interface{}{tuple1, tuple2, tuple3})
81
82 var scope_data string
83 metadata.ScopeData(scope_data)
84 if len(scope_data) > 0 {
85 extra := scopes.NewPreviewWidget("extra", "text")
86 extra.AddAttributeValue("text", "test Text")
87 reply.PushWidgets(header, image, description, actions, extra)
88 } else {
89 reply.PushWidgets(header, image, description, actions)
90 }
91
92 return nil
93}
94
95func (s *MyScope) Search(query *scopes.CannedQuery, metadata *scopes.SearchMetadata, reply *scopes.SearchReply, cancelled <-chan bool) error {
96 return s.AddQueryResults(reply, query.QueryString())
97}
98
99func (s *MyScope) SetScopeBase(base *scopes.ScopeBase) {
100 s.base = base
101}
102
103// RESULTS *********************************************************************
104
105func (s *MyScope) AddQueryResults(reply *scopes.SearchReply, query string) error {
106 cat := reply.RegisterCategory("category", "Category", "", searchCategoryTemplate)
107
108 result := scopes.NewCategorisedResult(cat)
109 result.SetURI("http://localhost/" + query)
110 result.SetDndURI("http://localhost_dnduri" + query)
111 result.SetTitle("TEST" + query)
112 result.SetArt("https://pbs.twimg.com/profile_images/1117820653/5ttls5.jpg.png")
113 result.Set("test_value_bool", true)
114 result.Set("test_value_string", "test_value"+query)
115 result.Set("test_value_int", 1999)
116 result.Set("test_value_float", 1.999)
117 if err := reply.Push(result); err != nil {
118 return err
119 }
120
121 result.SetURI("http://localhost2/" + query)
122 result.SetDndURI("http://localhost_dnduri2" + query)
123 result.SetTitle("TEST2")
124 result.SetArt("https://pbs.twimg.com/profile_images/1117820653/5ttls5.jpg.png")
125 result.Set("test_value_bool", false)
126 result.Set("test_value_string", "test_value2"+query)
127 result.Set("test_value_int", 2000)
128 result.Set("test_value_float", 2.100)
129
130 // add a variant map value
131 m := make(map[string]interface{})
132 m["value1"] = 1
133 m["value2"] = "string_value"
134 result.Set("test_value_map", m)
135
136 // add a variant array value
137 l := make([]interface{}, 0)
138 l = append(l, 1999)
139 l = append(l, "string_value")
140 result.Set("test_value_array", l)
141 if err := reply.Push(result); err != nil {
142 return err
143 }
144
145 return nil
146}
147
148// MAIN ************************************************************************
149
150func main() {
151 var sc MyScope
152 scope_interface = &sc
153
154 if err := scopes.Run(&MyScope{}); err != nil {
155 log.Fatalln(err)
156 }
157}
0158
=== modified file 'unityscope.go'
--- unityscope.go 2015-06-17 07:29:19 +0000
+++ unityscope.go 2015-06-17 07:29:19 +0000
@@ -41,9 +41,19 @@
41 Preview(result *Result, metadata *ActionMetadata, reply *PreviewReply, cancelled <-chan bool) error41 Preview(result *Result, metadata *ActionMetadata, reply *PreviewReply, cancelled <-chan bool) error
42}42}
4343
44// AggregatedScope defines the interface that scopes should implement to
45// aggregate sub scopes.
44type AggregatedScope interface {46type AggregatedScope interface {
45 Scope47 Scope
46 FindChildScopes() []*ChildScope48 FindChildScopes() []*ChildScope
49 GetScopeBase() *ScopeBase
50}
51
52// SearchListener is an interface that should be implemented for aggregated
53// scopes to retrieve results from their child scopes.
54type SearchListener interface {
55 FilterResult(result *CategorisedResult) bool
56 Finished()
47}57}
4858
49// Activator is an interface that should be implemented by scopes that59// Activator is an interface that should be implemented by scopes that
@@ -61,10 +71,10 @@
61}71}
6272
63//export callScopeSearch73//export callScopeSearch
64func callScopeSearch(scope Scope, queryPtr, metadataPtr unsafe.Pointer, replyData *C.uintptr_t, cancel <-chan bool) {74func callScopeSearch(scope Scope, queryPtr, metadataPtr unsafe.Pointer, replyData *C.uintptr_t, queryBase *C._SearchQueryBase, cancel <-chan bool) {
65 query := makeCannedQuery((*C._CannedQuery)(queryPtr))75 query := makeCannedQuery((*C._CannedQuery)(queryPtr))
66 metadata := makeSearchMetadata((*C._SearchMetadata)(metadataPtr))76 metadata := makeSearchMetadata((*C._SearchMetadata)(metadataPtr))
67 reply := makeSearchReply(replyData)77 reply := makeSearchReply(replyData, queryBase)
6878
69 go func() {79 go func() {
70 err := scope.Search(query, metadata, reply, cancel)80 err := scope.Search(query, metadata, reply, cancel)
@@ -72,6 +82,12 @@
72 reply.Error(err)82 reply.Error(err)
73 return83 return
74 }84 }
85 switch scope.(type) {
86 case AggregatedScope:
87 reply.waitForChildScopesFinished()
88 default:
89 // nothing
90 }
75 reply.Finished()91 reply.Finished()
76 }()92 }()
77}93}
@@ -212,7 +228,7 @@
212 var nb_scopes C.int228 var nb_scopes C.int
213 var c_array **C._ChildScope = C.list_child_scopes(b.b, &nb_scopes)229 var c_array **C._ChildScope = C.list_child_scopes(b.b, &nb_scopes)
214 defer C.free(unsafe.Pointer(c_array))230 defer C.free(unsafe.Pointer(c_array))
215 231
216 length := int(nb_scopes)232 length := int(nb_scopes)
217 // create a very big slice and then slice it to the number of scopes metadata233 // create a very big slice and then slice it to the number of scopes metadata
218 slice := (*[1 << 27]*C._ChildScope)(unsafe.Pointer(c_array))[:length:length]234 slice := (*[1 << 27]*C._ChildScope)(unsafe.Pointer(c_array))[:length:length]

Subscribers

People subscribed via source and target branches

to all changes: