Clean api package (#1055)

This commit is contained in:
Adrià Cidre 2018-06-27 10:11:45 +02:00 committed by GitHub
parent 3ca120c2aa
commit afb3ce159e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 121 additions and 75 deletions

View File

@ -5,30 +5,30 @@ import (
"strings"
)
// AppState represents if the app is in foreground, background or some other state
type AppState string
// appState represents if the app is in foreground, background or some other state
type appState string
func (a AppState) String() string {
func (a appState) String() string {
return string(a)
}
// Specific app states
// see https://facebook.github.io/react-native/docs/appstate.html
const (
AppStateForeground = AppState("active") // these constant values are kept in sync with React Native
AppStateBackground = AppState("background")
AppStateInactive = AppState("inactive")
appStateForeground = appState("active") // these constant values are kept in sync with React Native
appStateBackground = appState("background")
appStateInactive = appState("inactive")
AppStateInvalid = AppState("")
appStateInvalid = appState("")
)
// validAppStates returns an immutable set of valid states.
func validAppStates() []AppState {
return []AppState{AppStateInactive, AppStateBackground, AppStateForeground}
func validAppStates() []appState {
return []appState{appStateInactive, appStateBackground, appStateForeground}
}
// ParseAppState creates AppState from a string
func ParseAppState(stateString string) (AppState, error) {
// parseAppState creates AppState from a string
func parseAppState(stateString string) (appState, error) {
// a bit of cleaning up
stateString = strings.ToLower(strings.TrimSpace(stateString))
@ -38,5 +38,5 @@ func ParseAppState(stateString string) (AppState, error) {
}
}
return AppStateInvalid, fmt.Errorf("could not parse app state: %s", stateString)
return appStateInvalid, fmt.Errorf("could not parse app state: %s", stateString)
}

View File

@ -7,22 +7,22 @@ import (
)
func TestParseAppType(t *testing.T) {
check := func(input string, expectedState AppState, expectError bool) {
actualState, err := ParseAppState(input)
assert.Equalf(t, expectedState, actualState, "unexpected result from ParseAppState")
check := func(input string, expectedState appState, expectError bool) {
actualState, err := parseAppState(input)
assert.Equalf(t, expectedState, actualState, "unexpected result from parseAppState")
if expectError {
assert.NotNil(t, err, "error should not be nil")
}
}
check("active", AppStateForeground, false)
check("background", AppStateBackground, false)
check("inactive", AppStateInactive, false)
check(" acTIVE ", AppStateForeground, false)
check(" backGROUND ", AppStateBackground, false)
check(" INACTIVE ", AppStateInactive, false)
check("", AppStateInvalid, true)
check("back ground", AppStateInvalid, true)
check(" back ground ", AppStateInvalid, true)
check(" ", AppStateInvalid, true)
check("active", appStateForeground, false)
check("background", appStateBackground, false)
check("inactive", appStateInactive, false)
check(" acTIVE ", appStateForeground, false)
check(" backGROUND ", appStateBackground, false)
check(" INACTIVE ", appStateInactive, false)
check("", appStateInvalid, true)
check("back ground", appStateInvalid, true)
check(" back ground ", appStateInvalid, true)
check(" ", appStateInvalid, true)
}

View File

@ -47,7 +47,8 @@ type StatusBackend struct {
transactor *transactions.Transactor
jailManager jail.Manager
newNotification fcm.NotificationConstructor
connectionState ConnectionState
connectionState connectionState
appState appState
log log.Logger
}
@ -90,11 +91,6 @@ func (b *StatusBackend) JailManager() jail.Manager {
return b.jailManager
}
// PersonalAPI returns reference to personal APIs
func (b *StatusBackend) PersonalAPI() *personal.PublicAPI {
return b.personalAPI
}
// Transactor returns reference to a status transactor
func (b *StatusBackend) Transactor() *transactions.Transactor {
return b.transactor
@ -155,7 +151,7 @@ func (b *StatusBackend) startNode(config *params.NodeConfig) (err error) {
}
b.log.Info("Handlers registered")
if err = b.ReSelectAccount(); err != nil {
if err = b.reSelectAccount(); err != nil {
b.log.Error("Reselect account failed", "err", err)
return
}
@ -316,24 +312,22 @@ func (b *StatusBackend) registerHandlers() error {
return hash.Hex(), err
})
rpcClient.RegisterHandler(params.PersonalSignMethodName, b.PersonalAPI().Sign)
rpcClient.RegisterHandler(params.PersonalRecoverMethodName, b.PersonalAPI().Recover)
rpcClient.RegisterHandler(params.PersonalSignMethodName, b.personalAPI.Sign)
rpcClient.RegisterHandler(params.PersonalRecoverMethodName, b.personalAPI.Recover)
return nil
}
//
// ConnectionChange handles network state changes logic.
func (b *StatusBackend) ConnectionChange(typ string, expensive bool) {
b.mu.Lock()
defer b.mu.Unlock()
state := ConnectionState{
Type: NewConnectionType(typ),
state := connectionState{
Type: newConnectionType(typ),
Expensive: expensive,
}
if typ == "none" {
if typ == none {
state.Offline = true
}
@ -348,13 +342,14 @@ func (b *StatusBackend) ConnectionChange(typ string, expensive bool) {
// AppStateChange handles app state changes (background/foreground).
// state values: see https://facebook.github.io/react-native/docs/appstate.html
func (b *StatusBackend) AppStateChange(state string) {
appState, err := ParseAppState(state)
s, err := parseAppState(state)
if err != nil {
log.Error("AppStateChange failed, ignoring", "error", err)
return // and do nothing
}
b.log.Info("App State changed", "new-state", appState)
b.log.Info("App State changed", "new-state", s)
b.appState = s
// TODO: put node in low-power mode if the app is in background (or inactive)
// and normal mode if the app is in foreground.
@ -385,8 +380,8 @@ func (b *StatusBackend) Logout() error {
return nil
}
// ReSelectAccount selects previously selected account, often, after node restart.
func (b *StatusBackend) ReSelectAccount() error {
// reSelectAccount selects previously selected account, often, after node restart.
func (b *StatusBackend) reSelectAccount() error {
selectedAccount, err := b.AccountManager().SelectedAccount()
if selectedAccount == nil || err == account.ErrNoAccountSelected {
return nil

View File

@ -103,7 +103,7 @@ func TestBackendGettersConcurrently(t *testing.T) {
wg.Add(1)
go func() {
assert.NotNil(t, backend.PersonalAPI())
assert.NotNil(t, backend.personalAPI)
wg.Done()
}()
@ -174,7 +174,7 @@ func TestBackendAccountsConcurrently(t *testing.T) {
wg.Add(1)
go func() {
assert.NoError(t, backend.ReSelectAccount())
assert.NoError(t, backend.reSelectAccount())
wg.Done()
}()
@ -189,7 +189,7 @@ func TestBackendAccountsConcurrently(t *testing.T) {
}
func TestBackendConnectionChangesConcurrently(t *testing.T) {
connections := [...]string{"wifi", "cellular"}
connections := [...]string{wifi, cellular, unknown}
backend := NewStatusBackend()
count := 3
@ -207,6 +207,18 @@ func TestBackendConnectionChangesConcurrently(t *testing.T) {
wg.Wait()
}
func TestBackendConnectionChangesToOffline(t *testing.T) {
b := NewStatusBackend()
b.ConnectionChange(none, false)
assert.True(t, b.connectionState.Offline)
b.ConnectionChange(wifi, false)
assert.False(t, b.connectionState.Offline)
b.ConnectionChange("unknown-state", false)
assert.False(t, b.connectionState.Offline)
}
func TestBackendCallRPCConcurrently(t *testing.T) {
backend := NewStatusBackend()
config := params.NodeConfig{}
@ -245,6 +257,38 @@ func TestBackendCallRPCConcurrently(t *testing.T) {
wg.Wait()
}
func TestAppStateChange(t *testing.T) {
backend := NewStatusBackend()
var testCases = []struct {
name string
fromState appState
toState appState
expectedState appState
}{
{
name: "success",
fromState: appStateInactive,
toState: appStateBackground,
expectedState: appStateBackground,
},
{
name: "invalid state",
fromState: appStateInactive,
toState: "unexisting",
expectedState: appStateInactive,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
backend.appState = tc.fromState
backend.AppStateChange(tc.toState.String())
assert.Equal(t, tc.expectedState.String(), backend.appState.String())
})
}
}
func TestPrepareTxArgs(t *testing.T) {
var flagtests = []struct {
description string

View File

@ -4,60 +4,61 @@ import (
"fmt"
)
// ConnectionState represents device connection state and type,
// connectionState represents device connection state and type,
// as reported by mobile framework.
//
// Zero value represents default assumption about network (online and unknown type).
type ConnectionState struct {
type connectionState struct {
Offline bool `json:"offline"`
Type ConnectionType `json:"type"`
Type connectionType `json:"type"`
Expensive bool `json:"expensive"`
}
// ConnectionType represents description of available
// connectionType represents description of available
// connection types as reported by React Native (see
// https://facebook.github.io/react-native/docs/netinfo.html)
// We're interested mainly in 'wifi' and 'cellular', but
// other types are also may be used.
type ConnectionType byte
type connectionType byte
const (
offline = "offline"
wifi = "wifi"
cellular = "cellular"
unknown = "unknown"
none = "none"
)
// NewConnectionType creates new ConnectionType from string.
func NewConnectionType(s string) ConnectionType {
// newConnectionType creates new connectionType from string.
func newConnectionType(s string) connectionType {
switch s {
case cellular:
return ConnectionCellular
return connectionCellular
case wifi:
return ConnectionWifi
return connectionWifi
}
return ConnectionUnknown
return connectionUnknown
}
// ConnectionType constants
const (
ConnectionUnknown ConnectionType = iota
ConnectionCellular // cellular, LTE, 4G, 3G, EDGE, etc.
ConnectionWifi // WIFI or iOS simulator
connectionUnknown connectionType = iota
connectionCellular // cellular, LTE, 4G, 3G, EDGE, etc.
connectionWifi // WIFI or iOS simulator
)
// String formats ConnectionState for logs. Implements Stringer.
func (c ConnectionState) String() string {
// string formats ConnectionState for logs. Implements Stringer.
func (c connectionState) String() string {
if c.Offline {
return offline
}
var typ string
switch c.Type {
case ConnectionWifi:
case connectionWifi:
typ = wifi
case ConnectionCellular:
case connectionCellular:
typ = cellular
default:
typ = unknown

View File

@ -3,16 +3,16 @@ package api
import "testing"
func TestConnectionType(t *testing.T) {
c := NewConnectionType("wifi")
if c != ConnectionWifi {
c := newConnectionType("wifi")
if c != connectionWifi {
t.Fatalf("Wrong connection type: %v", c)
}
c = NewConnectionType("cellular")
if c != ConnectionCellular {
c = newConnectionType("cellular")
if c != connectionCellular {
t.Fatalf("Wrong connection type: %v", c)
}
c = NewConnectionType("bluetooth")
if c != ConnectionUnknown {
c = newConnectionType("bluetooth")
if c != connectionUnknown {
t.Fatalf("Wrong connection type: %v", c)
}
}
@ -20,34 +20,39 @@ func TestConnectionType(t *testing.T) {
func TestConnectionState(t *testing.T) {
tests := []struct {
name string
state ConnectionState
state connectionState
expected string
}{
{
"zero value",
ConnectionState{},
connectionState{},
"unknown",
},
{
"offline",
ConnectionState{Offline: true},
connectionState{Offline: true},
"offline",
},
{
"wifi",
ConnectionState{Type: ConnectionWifi},
connectionState{Type: connectionWifi},
"wifi",
},
{
"wifi tethered",
ConnectionState{Type: ConnectionWifi, Expensive: true},
connectionState{Type: connectionWifi, Expensive: true},
"wifi (expensive)",
},
{
"unknown",
ConnectionState{Type: ConnectionUnknown},
connectionState{Type: connectionUnknown},
"unknown",
},
{
"cellular",
connectionState{Type: connectionCellular},
"cellular",
},
}
for _, test := range tests {

View File

@ -1,5 +1,6 @@
package api
// RunAsync runs the specified function asynchronously.
func RunAsync(f func() error) <-chan error {
resp := make(chan error, 1)
go func() {