1package random_bugs
2
3import (
4 "math/rand"
5 "strings"
6 "time"
7
8 "github.com/MichaelMure/git-bug/bug"
9 "github.com/MichaelMure/git-bug/identity"
10 "github.com/MichaelMure/git-bug/repository"
11 "github.com/icrowley/fake"
12)
13
14type opsGenerator func(bug.Interface, identity.Interface, int64)
15
16type Options struct {
17 BugNumber int
18 PersonNumber int
19 MinOp int
20 MaxOp int
21}
22
23func DefaultOptions() Options {
24 return Options{
25 BugNumber: 15,
26 PersonNumber: 5,
27 MinOp: 3,
28 MaxOp: 20,
29 }
30}
31
32func FillRepo(repo repository.ClockedRepo, bugNumber int) {
33 FillRepoWithSeed(repo, bugNumber, time.Now().UnixNano())
34}
35
36func FillRepoWithSeed(repo repository.ClockedRepo, bugNumber int, seed int64) {
37 options := DefaultOptions()
38 options.BugNumber = bugNumber
39
40 CommitRandomBugsWithSeed(repo, options, seed)
41}
42
43func CommitRandomBugs(repo repository.ClockedRepo, opts Options) {
44 CommitRandomBugsWithSeed(repo, opts, time.Now().UnixNano())
45}
46
47func CommitRandomBugsWithSeed(repo repository.ClockedRepo, opts Options, seed int64) {
48 generateRandomPersons(repo, opts.PersonNumber)
49
50 bugs := generateRandomBugsWithSeed(opts, seed)
51
52 for _, b := range bugs {
53 err := b.Commit(repo)
54 if err != nil {
55 panic(err)
56 }
57 }
58}
59
60func generateRandomBugsWithSeed(opts Options, seed int64) []*bug.Bug {
61 rand.Seed(seed)
62 fake.Seed(seed)
63
64 // At the moment git-bug has a risk of hash collision is simple
65 // operation (like open/close) are made with the same timestamp.
66 // As a temporary workaround, we use here an strictly increasing
67 // timestamp
68 timestamp := time.Now().Unix()
69
70 opsGenerators := []opsGenerator{
71 comment,
72 comment,
73 title,
74 labels,
75 open,
76 close,
77 }
78
79 result := make([]*bug.Bug, opts.BugNumber)
80
81 for i := 0; i < opts.BugNumber; i++ {
82 addedLabels = []string{}
83
84 b, _, err := bug.Create(
85 randomPerson(),
86 time.Now().Unix(),
87 fake.Sentence(),
88 paragraphs(),
89 )
90
91 if err != nil {
92 panic(err)
93 }
94
95 nOps := opts.MinOp
96
97 if opts.MaxOp > opts.MinOp {
98 nOps += rand.Intn(opts.MaxOp - opts.MinOp)
99 }
100
101 for j := 0; j < nOps; j++ {
102 index := rand.Intn(len(opsGenerators))
103 opsGenerators[index](b, randomPerson(), timestamp)
104 timestamp++
105 }
106
107 result[i] = b
108 }
109
110 return result
111}
112
113func GenerateRandomOperationPacks(packNumber int, opNumber int) []*bug.OperationPack {
114 return GenerateRandomOperationPacksWithSeed(packNumber, opNumber, time.Now().UnixNano())
115}
116
117func GenerateRandomOperationPacksWithSeed(packNumber int, opNumber int, seed int64) []*bug.OperationPack {
118 // Note: this is a bit crude, only generate a Create + Comments
119
120 panic("this piece of code needs to be updated to make sure that the identities " +
121 "are properly commit before usage. That is, generateRandomPersons() need to be called.")
122
123 rand.Seed(seed)
124 fake.Seed(seed)
125
126 result := make([]*bug.OperationPack, packNumber)
127
128 for i := 0; i < packNumber; i++ {
129 opp := &bug.OperationPack{}
130
131 var op bug.Operation
132
133 op = bug.NewCreateOp(
134 randomPerson(),
135 time.Now().Unix(),
136 fake.Sentence(),
137 paragraphs(),
138 nil,
139 )
140
141 opp.Append(op)
142
143 for j := 0; j < opNumber-1; j++ {
144 op = bug.NewAddCommentOp(
145 randomPerson(),
146 time.Now().Unix(),
147 paragraphs(),
148 nil,
149 )
150 opp.Append(op)
151 }
152
153 result[i] = opp
154 }
155
156 return result
157}
158
159func person() identity.Interface {
160 return identity.NewIdentity(fake.FullName(), fake.EmailAddress())
161}
162
163var persons []identity.Interface
164
165func generateRandomPersons(repo repository.ClockedRepo, n int) {
166 persons = make([]identity.Interface, n)
167 for i := range persons {
168 p := person()
169 err := p.Commit(repo)
170 if err != nil {
171 panic(err)
172 }
173 persons[i] = p
174 }
175}
176
177func randomPerson() identity.Interface {
178 index := rand.Intn(len(persons))
179 return persons[index]
180}
181
182func paragraphs() string {
183 p := fake.Paragraphs()
184 return strings.Replace(p, "\t", "\n\n", -1)
185}
186
187func comment(b bug.Interface, p identity.Interface, timestamp int64) {
188 _, _ = bug.AddComment(b, p, timestamp, paragraphs())
189}
190
191func title(b bug.Interface, p identity.Interface, timestamp int64) {
192 _, _ = bug.SetTitle(b, p, timestamp, fake.Sentence())
193}
194
195func open(b bug.Interface, p identity.Interface, timestamp int64) {
196 _, _ = bug.Open(b, p, timestamp)
197}
198
199func close(b bug.Interface, p identity.Interface, timestamp int64) {
200 _, _ = bug.Close(b, p, timestamp)
201}
202
203var addedLabels []string
204
205func labels(b bug.Interface, p identity.Interface, timestamp int64) {
206 var removed []string
207 nbRemoved := rand.Intn(3)
208 for nbRemoved > 0 && len(addedLabels) > 0 {
209 index := rand.Intn(len(addedLabels))
210 removed = append(removed, addedLabels[index])
211 addedLabels[index] = addedLabels[len(addedLabels)-1]
212 addedLabels = addedLabels[:len(addedLabels)-1]
213 nbRemoved--
214 }
215
216 var added []string
217 nbAdded := rand.Intn(3)
218 for i := 0; i < nbAdded; i++ {
219 label := fake.Word()
220 added = append(added, label)
221 addedLabels = append(addedLabels, label)
222 }
223
224 // ignore error
225 // if the randomisation produce no changes, no op
226 // is added to the bug
227 _, _, _ = bug.ChangeLabels(b, p, timestamp, added, removed)
228}