summaryrefslogtreecommitdiffstats
path: root/pkg/gui/recording.go
blob: 75de87334a88f8deeea36d499a39300225408deb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package gui

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"os"
	"strconv"
	"time"

	"github.com/jesseduffield/gocui"
	"github.com/jesseduffield/lazygit/pkg/utils"
)

func recordingEvents() bool {
	return recordEventsTo() != ""
}

func recordEventsTo() string {
	return os.Getenv("RECORD_EVENTS_TO")
}

func (gui *Gui) timeSinceStart() int64 {
	return time.Since(gui.StartTime).Nanoseconds() / 1e6
}

func (gui *Gui) replayRecordedEvents() {
	if os.Getenv("REPLAY_EVENTS_FROM") == "" {
		return
	}

	go utils.Safe(func() {
		time.Sleep(time.Second * 20)
		log.Fatal("20 seconds is up, lazygit recording took too long to complete")
	})

	events, err := gui.loadRecordedEvents()
	if err != nil {
		log.Fatal(err)
	}

	ticker := time.NewTicker(time.Millisecond)
	defer ticker.Stop()

	// might need to add leeway if this ends up flakey
	var leeway int64 = 0
	// humans are slow so this speeds things up.
	speed := 1
	envReplaySpeed := os.Getenv("REPLAY_SPEED")
	if envReplaySpeed != "" {
		var err error
		speed, err = strconv.Atoi(envReplaySpeed)
		if err != nil {
			log.Fatal(err)
		}
	}

	// The playback could be paused at any time because integration tests run concurrently.
	// Therefore we can't just check for a given event whether we've passed its timestamp,
	// or else we'll have an explosion of keypresses after the test is resumed.
	// We need to check if we've waited long enough since the last event was replayed.
	for i, event := range events {
		var prevEventTimestamp int64 = 0
		if i > 0 {
			prevEventTimestamp = events[i-1].Timestamp
		}
		timeToWait := (event.Timestamp - prevEventTimestamp) / int64(speed)
		if i == 0 {
			timeToWait += leeway
		}
		var timeWaited int64 = 0
	middle:
		for {
			select {
			case <-ticker.C:
				timeWaited += 1
				if gui.g != nil && timeWaited >= timeToWait {
					gui.g.ReplayedEvents <- *event.Event
					break middle
				}
			case <-gui.stopChan:
				return
			}
		}
	}

	time.Sleep(time.Second * 1)

	gui.g.Update(func(*gocui.Gui) error {
		return gocui.ErrQuit
	})

	time.Sleep(time.Second * 1)

	log.Fatal("lazygit should have already exited")
}

func (gui *Gui) loadRecordedEvents() ([]RecordedEvent, error) {
	path := os.Getenv("REPLAY_EVENTS_FROM")

	data, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}

	events := []RecordedEvent{}

	err = json.Unmarshal(data, &events)
	if err != nil {
		return nil, err
	}

	return events, nil
}

func (gui *Gui) saveRecordedEvents() error {
	if !recordingEvents() {
		return nil
	}

	jsonEvents, err := json.Marshal(gui.RecordedEvents)
	if err != nil {
		return err
	}

	path := recordEventsTo()

	return ioutil.WriteFile(path, jsonEvents, 0600)
}

func (gui *Gui) recordEvents() {
	for event := range gui.g.RecordedEvents {
		recordedEvent := RecordedEvent{
			Timestamp: gui.timeSinceStart(),
			Event:     event,
		}

		gui.RecordedEvents = append(gui.RecordedEvents, recordedEvent)
	}
}