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