github.com/digdeepmining/go-atheios@v1.5.13-0.20180902133602-d5687a2e6f43/rpc/subscription_test.go (about) 1 // Copyright 2016 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package rpc 18 19 import ( 20 "encoding/json" 21 "net" 22 "sync" 23 "testing" 24 "time" 25 26 "golang.org/x/net/context" 27 ) 28 29 type NotificationTestService struct { 30 mu sync.Mutex 31 unsubscribed bool 32 33 gotHangSubscriptionReq chan struct{} 34 unblockHangSubscription chan struct{} 35 } 36 37 func (s *NotificationTestService) Echo(i int) int { 38 return i 39 } 40 41 func (s *NotificationTestService) wasUnsubCallbackCalled() bool { 42 s.mu.Lock() 43 defer s.mu.Unlock() 44 return s.unsubscribed 45 } 46 47 func (s *NotificationTestService) Unsubscribe(subid string) { 48 s.mu.Lock() 49 s.unsubscribed = true 50 s.mu.Unlock() 51 } 52 53 func (s *NotificationTestService) SomeSubscription(ctx context.Context, n, val int) (*Subscription, error) { 54 notifier, supported := NotifierFromContext(ctx) 55 if !supported { 56 return nil, ErrNotificationsUnsupported 57 } 58 59 // by explicitly creating an subscription we make sure that the subscription id is send back to the client 60 // before the first subscription.Notify is called. Otherwise the events might be send before the response 61 // for the eth_subscribe method. 62 subscription := notifier.CreateSubscription() 63 64 go func() { 65 // test expects n events, if we begin sending event immediately some events 66 // will probably be dropped since the subscription ID might not be send to 67 // the client. 68 time.Sleep(5 * time.Second) 69 for i := 0; i < n; i++ { 70 if err := notifier.Notify(subscription.ID, val+i); err != nil { 71 return 72 } 73 } 74 75 select { 76 case <-notifier.Closed(): 77 s.mu.Lock() 78 s.unsubscribed = true 79 s.mu.Unlock() 80 case <-subscription.Err(): 81 s.mu.Lock() 82 s.unsubscribed = true 83 s.mu.Unlock() 84 } 85 }() 86 87 return subscription, nil 88 } 89 90 // HangSubscription blocks on s.unblockHangSubscription before 91 // sending anything. 92 func (s *NotificationTestService) HangSubscription(ctx context.Context, val int) (*Subscription, error) { 93 notifier, supported := NotifierFromContext(ctx) 94 if !supported { 95 return nil, ErrNotificationsUnsupported 96 } 97 98 s.gotHangSubscriptionReq <- struct{}{} 99 <-s.unblockHangSubscription 100 subscription := notifier.CreateSubscription() 101 102 go func() { 103 notifier.Notify(subscription.ID, val) 104 }() 105 return subscription, nil 106 } 107 108 func TestNotifications(t *testing.T) { 109 server := NewServer() 110 service := &NotificationTestService{} 111 112 if err := server.RegisterName("eth", service); err != nil { 113 t.Fatalf("unable to register test service %v", err) 114 } 115 116 clientConn, serverConn := net.Pipe() 117 118 go server.ServeCodec(NewJSONCodec(serverConn), OptionMethodInvocation|OptionSubscriptions) 119 120 out := json.NewEncoder(clientConn) 121 in := json.NewDecoder(clientConn) 122 123 n := 5 124 val := 12345 125 request := map[string]interface{}{ 126 "id": 1, 127 "method": "eth_subscribe", 128 "version": "2.0", 129 "params": []interface{}{"someSubscription", n, val}, 130 } 131 132 // create subscription 133 if err := out.Encode(request); err != nil { 134 t.Fatal(err) 135 } 136 137 var subid string 138 response := jsonSuccessResponse{Result: subid} 139 if err := in.Decode(&response); err != nil { 140 t.Fatal(err) 141 } 142 143 var ok bool 144 if _, ok = response.Result.(string); !ok { 145 t.Fatalf("expected subscription id, got %T", response.Result) 146 } 147 148 for i := 0; i < n; i++ { 149 var notification jsonNotification 150 if err := in.Decode(¬ification); err != nil { 151 t.Fatalf("%v", err) 152 } 153 154 if int(notification.Params.Result.(float64)) != val+i { 155 t.Fatalf("expected %d, got %d", val+i, notification.Params.Result) 156 } 157 } 158 159 clientConn.Close() // causes notification unsubscribe callback to be called 160 time.Sleep(1 * time.Second) 161 162 if !service.wasUnsubCallbackCalled() { 163 t.Error("unsubscribe callback not called after closing connection") 164 } 165 }