Skip to content

Commit d2e54c1

Browse files
committed
Auto merge of #334 - devonhollowood:auto-increment-default-name, r=pietroalbini
Auto-increment default names for PRs Fix #306 I thought that the easiest way to go about this was to adjust `default_experiment_name()`, and to just have it automatically try new names in case of a clash until it finds one that works. This makes the assumption that there won't be too too many experiments for a given PR, since experiment `N` will make N calls to the database, which is only really okay if N is small. Let me know if you'd like me to do something smarter here, or to adjust a different area of the code instead.
2 parents b8d46c2 + b763389 commit d2e54c1

File tree

1 file changed

+188
-2
lines changed

1 file changed

+188
-2
lines changed

src/server/routes/webhooks/commands.rs

Lines changed: 188 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ pub fn ping(data: &Data, issue: &Issue) -> Result<()> {
1515
}
1616

1717
pub fn run(host: &str, data: &Data, issue: &Issue, args: RunArgs) -> Result<()> {
18-
let name = get_name(&data.db, issue, args.name)?;
18+
let name = setup_run_name(&data.db, issue, args.name)?;
1919

2020
::actions::CreateExperiment {
2121
name: name.clone(),
@@ -167,12 +167,57 @@ fn default_experiment_name(db: &Database, issue: &Issue) -> Result<Option<String
167167
})
168168
}
169169

170+
/// Set up the name for a new run's experiment, including auto-incrementing generated names and
171+
/// storing experiment names in the database.
172+
fn setup_run_name(db: &Database, issue: &Issue, name: Option<String>) -> Result<String> {
173+
let name = if let Some(name) = name {
174+
name
175+
} else {
176+
generate_new_experiment_name(&db, &issue)?
177+
};
178+
store_experiment_name(&db, issue, &name)?;
179+
Ok(name)
180+
}
181+
182+
/// Automatically generate experiment name, auto-incrementing to the first one which does not
183+
/// exist. E.g. if this function is passed the an issue `12345`, and experiment `pr-12345`
184+
/// exists, then this command returns Ok("pr-12345-1"). Does not store the result in the database.
185+
fn generate_new_experiment_name(db: &Database, issue: &Issue) -> Result<String> {
186+
let mut name = format!("pr-{}", issue.number);
187+
let mut idx = 1u16;
188+
while Experiment::exists(&db, &name)? {
189+
name = format!("pr-{}-{}", issue.number, idx);
190+
idx = idx
191+
.checked_add(1)
192+
.ok_or_else::<Error, _>(|| "too many similarly-named pull requests".into())?;
193+
}
194+
Ok(name)
195+
}
196+
170197
#[cfg(test)]
171198
mod tests {
172-
use super::{default_experiment_name, store_experiment_name};
199+
use super::{
200+
default_experiment_name, generate_new_experiment_name, get_name, setup_run_name,
201+
store_experiment_name,
202+
};
173203
use db::Database;
204+
use errors::*;
174205
use server::github;
175206

207+
/// Simulate to the `run` command, and return experiment name
208+
fn dummy_run(db: &Database, issue: &github::Issue, name: Option<String>) -> Result<String> {
209+
let name = setup_run_name(db, issue, name)?;
210+
::actions::CreateExperiment::dummy(&name).apply(&db, &::config::Config::default())?;
211+
Ok(name)
212+
}
213+
214+
/// Simulate to the `edit` command, and return experiment name
215+
fn dummy_edit(db: &Database, issue: &github::Issue, name: Option<String>) -> Result<String> {
216+
let name = get_name(db, issue, name)?;
217+
::actions::EditExperiment::dummy(&name).apply(&db, &::config::Config::default())?;
218+
Ok(name)
219+
}
220+
176221
#[test]
177222
fn test_default_experiment_name() {
178223
let db = Database::temp().unwrap();
@@ -209,4 +254,145 @@ mod tests {
209254
"foo"
210255
);
211256
}
257+
258+
#[test]
259+
fn test_run() {
260+
let db = Database::temp().unwrap();
261+
262+
let pr1 = github::Issue {
263+
number: 1,
264+
url: String::new(),
265+
html_url: String::new(),
266+
labels: Vec::new(),
267+
pull_request: Some(github::PullRequest {
268+
html_url: String::new(),
269+
}),
270+
};
271+
// test with supplied name
272+
assert_eq!(
273+
dummy_run(&db, &pr1, Some("pr-1".to_owned())).expect("dummy run failed"),
274+
"pr-1"
275+
);
276+
// make sure it fails the second time
277+
assert!(dummy_run(&db, &pr1, Some("pr-1".to_owned())).is_err(),);
278+
279+
let pr2 = github::Issue {
280+
number: 2,
281+
url: String::new(),
282+
html_url: String::new(),
283+
labels: Vec::new(),
284+
pull_request: Some(github::PullRequest {
285+
html_url: String::new(),
286+
}),
287+
};
288+
// test with default-generated name
289+
assert_eq!(
290+
dummy_run(&db, &pr2, None).expect("dummy run failed"),
291+
"pr-2"
292+
);
293+
// make sure it increments correctly
294+
assert_eq!(
295+
dummy_run(&db, &pr2, None).expect("dummy run failed"),
296+
"pr-2-1"
297+
);
298+
// make sure we don't get e.g. pr-2-1-1
299+
assert_eq!(
300+
dummy_run(&db, &pr2, None).expect("dummy run failed"),
301+
"pr-2-2"
302+
);
303+
// make sure we can manually supply name and then continue incrementing
304+
assert_eq!(
305+
dummy_run(&db, &pr1, Some("pr-2-custom".to_owned())).expect("dummy run failed"),
306+
"pr-2-custom"
307+
);
308+
assert_eq!(
309+
dummy_run(&db, &pr2, None).expect("dummy run failed"),
310+
"pr-2-3"
311+
);
312+
}
313+
314+
#[test]
315+
fn test_edit() {
316+
let db = Database::temp().unwrap();
317+
318+
// test retrieval of name generated in a supplied-name run
319+
let pr1 = github::Issue {
320+
number: 1,
321+
url: String::new(),
322+
html_url: String::new(),
323+
labels: Vec::new(),
324+
pull_request: Some(github::PullRequest {
325+
html_url: String::new(),
326+
}),
327+
};
328+
assert_eq!(
329+
dummy_run(&db, &pr1, Some("pr-1-custom".to_owned())).expect("dummy run failed"),
330+
"pr-1-custom"
331+
);
332+
assert_eq!(
333+
dummy_edit(&db, &pr1, None).expect("dummy edit failed"),
334+
"pr-1-custom"
335+
);
336+
337+
// test retrieval of name generated in an auto-generated run
338+
let pr2 = github::Issue {
339+
number: 2,
340+
url: String::new(),
341+
html_url: String::new(),
342+
labels: Vec::new(),
343+
pull_request: Some(github::PullRequest {
344+
html_url: String::new(),
345+
}),
346+
};
347+
assert_eq!(
348+
dummy_run(&db, &pr2, None).expect("dummy run failed"),
349+
"pr-2"
350+
);
351+
// make sure edit doesn't change name
352+
assert_eq!(
353+
dummy_edit(&db, &pr2, None).expect("dummy edit failed"),
354+
"pr-2"
355+
);
356+
// test idempotence
357+
assert_eq!(
358+
dummy_edit(&db, &pr2, None).expect("dummy edit failed"),
359+
"pr-2"
360+
);
361+
// test that name incrementing is reflected here
362+
assert_eq!(
363+
dummy_run(&db, &pr2, None).expect("dummy run failed"),
364+
"pr-2-1"
365+
);
366+
assert_eq!(
367+
dummy_edit(&db, &pr2, None).expect("dummy edit failed"),
368+
"pr-2-1"
369+
);
370+
}
371+
372+
#[test]
373+
fn test_generate_new_experiment_name() {
374+
let db = Database::temp().unwrap();
375+
let pr = github::Issue {
376+
number: 12345,
377+
url: String::new(),
378+
html_url: String::new(),
379+
labels: Vec::new(),
380+
pull_request: Some(github::PullRequest {
381+
html_url: String::new(),
382+
}),
383+
};
384+
385+
::actions::CreateExperiment::dummy("pr-12345")
386+
.apply(&db, &::config::Config::default())
387+
.expect("could not store dummy experiment");
388+
let new_name = generate_new_experiment_name(&db, &pr).unwrap();
389+
assert_eq!(new_name, "pr-12345-1");
390+
::actions::CreateExperiment::dummy("pr-12345-1")
391+
.apply(&db, &::config::Config::default())
392+
.expect("could not store dummy experiment");;
393+
assert_eq!(
394+
&generate_new_experiment_name(&db, &pr).unwrap(),
395+
"pr-12345-2"
396+
);
397+
}
212398
}

0 commit comments

Comments
 (0)