Browse Source

unit test

pull/8101/head
HuangYi 3 years ago
parent
commit
fd305d30a3
No known key found for this signature in database GPG Key ID: 58776091521E8B17
2 changed files with 38 additions and 27 deletions
  1. +8
    -1
      cmd/tendermint/commands/rollback_test.go
  2. +30
    -26
      test/e2e/app/state.go

+ 8
- 1
cmd/tendermint/commands/rollback_test.go View File

@ -46,6 +46,13 @@ func TestRollbackIntegration(t *testing.T) {
height, _, err = commands.RollbackState(cfg)
require.NoError(t, err, "%d", height)
})
t.Run("Rollback agian", func(t *testing.T) {
// should be able to rollback agian.
require.NoError(t, app.Rollback())
height2, _, err := commands.RollbackState(cfg)
require.NoError(t, err, "%d", height2)
require.Equal(t, height-1, height2)
})
t.Run("Restart", func(t *testing.T) {
require.True(t, height > 0, "%d", height)
@ -68,7 +75,7 @@ func TestRollbackIntegration(t *testing.T) {
status, err := client.Status(ctx)
require.NoError(t, err)
if status.SyncInfo.LatestBlockHeight > height {
if status.SyncInfo.LatestBlockHeight > height+2 {
return
}
}


+ 30
- 26
test/e2e/app/state.go View File

@ -12,8 +12,7 @@ import (
"sync"
)
const stateFileName = "app_state.json"
const prevStateFileName = "prev_app_state.json"
const stateFileName = "app_state_%d.json"
// State is the application state.
type State struct {
@ -23,9 +22,9 @@ type State struct {
Hash []byte
// private fields aren't marshaled to disk.
currentFile string
// app saves current and previous state for rollback functionality
previousFile string
// app saves current and historical states for rollback functionality
stateFileTemplate string
persistInterval uint64
initialHeight uint64
}
@ -33,10 +32,9 @@ type State struct {
// NewState creates a new state.
func NewState(dir string, persistInterval uint64) (*State, error) {
state := &State{
Values: make(map[string]string),
currentFile: filepath.Join(dir, stateFileName),
previousFile: filepath.Join(dir, prevStateFileName),
persistInterval: persistInterval,
Values: make(map[string]string),
stateFileTemplate: filepath.Join(dir, stateFileName),
persistInterval: persistInterval,
}
state.Hash = hashItems(state.Values)
err := state.load()
@ -48,25 +46,37 @@ func NewState(dir string, persistInterval uint64) (*State, error) {
return state, nil
}
func (s *State) currentFile() string {
return s.stateFileAtVersion(s.Height)
}
func (s *State) previousFile() string {
return s.stateFileAtVersion(s.Height - 1)
}
func (s *State) stateFileAtVersion(i uint64) string {
return fmt.Sprintf(s.stateFileTemplate, i)
}
// load loads state from disk. It does not take out a lock, since it is called
// during construction.
func (s *State) load() error {
bz, err := os.ReadFile(s.currentFile)
bz, err := os.ReadFile(s.currentFile())
if err != nil {
// if the current state doesn't exist then we try recover from the previous state
if errors.Is(err, os.ErrNotExist) {
bz, err = os.ReadFile(s.previousFile)
bz, err = os.ReadFile(s.previousFile())
if err != nil {
return fmt.Errorf("failed to read both current and previous state (%q): %w",
s.previousFile, err)
s.previousFile(), err)
}
} else {
return fmt.Errorf("failed to read state from %q: %w", s.currentFile, err)
return fmt.Errorf("failed to read state from %q: %w", s.currentFile(), err)
}
}
err = json.Unmarshal(bz, s)
if err != nil {
return fmt.Errorf("invalid state data in %q: %w", s.currentFile, err)
return fmt.Errorf("invalid state data in %q: %w", s.currentFile(), err)
}
return nil
}
@ -80,19 +90,13 @@ func (s *State) save() error {
}
// We write the state to a separate file and move it to the destination, to
// make it atomic.
newFile := fmt.Sprintf("%v.new", s.currentFile)
newFile := fmt.Sprintf("%v.new", s.currentFile())
err = os.WriteFile(newFile, bz, 0644)
if err != nil {
return fmt.Errorf("failed to write state to %q: %w", s.currentFile, err)
}
// We take the current state and move it to the previous state, replacing it
if _, err := os.Stat(s.currentFile); err == nil {
if err := os.Rename(s.currentFile, s.previousFile); err != nil {
return fmt.Errorf("failed to replace previous state: %w", err)
}
return fmt.Errorf("failed to write state to %q: %w", s.currentFile(), err)
}
// Finally, we take the new state and replace the current state.
return os.Rename(newFile, s.currentFile)
return os.Rename(newFile, s.currentFile())
}
// Export exports key/value pairs as JSON, used for state sync snapshots.
@ -159,13 +163,13 @@ func (s *State) Commit() (uint64, []byte, error) {
}
func (s *State) Rollback() error {
bz, err := os.ReadFile(s.previousFile)
bz, err := os.ReadFile(s.previousFile())
if err != nil {
return fmt.Errorf("failed to read state from %q: %w", s.previousFile, err)
return fmt.Errorf("failed to read state from %q: %w", s.previousFile(), err)
}
err = json.Unmarshal(bz, s)
if err != nil {
return fmt.Errorf("invalid state data in %q: %w", s.previousFile, err)
return fmt.Errorf("invalid state data in %q: %w", s.previousFile(), err)
}
return nil
}


Loading…
Cancel
Save