mirror of
https://github.com/Orange-OpenSource/hurl.git
synced 2024-11-09 21:44:51 +03:00
Implement repeat for sequential runner.
This commit is contained in:
parent
16f902a7e1
commit
54f3bf5b1a
8
integration/hurl/tests_ok/repeat.py
Normal file
8
integration/hurl/tests_ok/repeat.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
from app import app
|
||||||
|
from flask import request
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/repeat/hello")
|
||||||
|
def repeat_hello():
|
||||||
|
name = request.args.get("name")
|
||||||
|
return f"Hello {name}!\n"
|
2
integration/hurl/tests_ok/repeat_a.hurl
Executable file
2
integration/hurl/tests_ok/repeat_a.hurl
Executable file
@ -0,0 +1,2 @@
|
|||||||
|
GET http://localhost:8000/repeat/hello?name=A
|
||||||
|
HTTP 200
|
2
integration/hurl/tests_ok/repeat_b.hurl
Executable file
2
integration/hurl/tests_ok/repeat_b.hurl
Executable file
@ -0,0 +1,2 @@
|
|||||||
|
GET http://localhost:8000/repeat/hello?name=B
|
||||||
|
HTTP 200
|
2
integration/hurl/tests_ok/repeat_c.hurl
Executable file
2
integration/hurl/tests_ok/repeat_c.hurl
Executable file
@ -0,0 +1,2 @@
|
|||||||
|
GET http://localhost:8000/repeat/hello?name=C
|
||||||
|
HTTP 200
|
14
integration/hurl/tests_ok/repeat_par.out
Executable file
14
integration/hurl/tests_ok/repeat_par.out
Executable file
@ -0,0 +1,14 @@
|
|||||||
|
TAP version 13
|
||||||
|
1..12
|
||||||
|
ok 1 - tests_ok/repeat_a.hurl
|
||||||
|
ok 2 - tests_ok/repeat_b.hurl
|
||||||
|
ok 3 - tests_ok/repeat_c.hurl
|
||||||
|
ok 4 - tests_ok/repeat_a.hurl
|
||||||
|
ok 5 - tests_ok/repeat_b.hurl
|
||||||
|
ok 6 - tests_ok/repeat_c.hurl
|
||||||
|
ok 7 - tests_ok/repeat_a.hurl
|
||||||
|
ok 8 - tests_ok/repeat_b.hurl
|
||||||
|
ok 9 - tests_ok/repeat_c.hurl
|
||||||
|
ok 10 - tests_ok/repeat_a.hurl
|
||||||
|
ok 11 - tests_ok/repeat_b.hurl
|
||||||
|
ok 12 - tests_ok/repeat_c.hurl
|
12
integration/hurl/tests_ok/repeat_par.ps1
Normal file
12
integration/hurl/tests_ok/repeat_par.ps1
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
Set-StrictMode -Version latest
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
if (Test-Path build/repeat/tap.txt) {
|
||||||
|
Remove-Item build/repeat/tap.txt
|
||||||
|
}
|
||||||
|
|
||||||
|
hurl --repeat 4 --parallel --report-tap build/repeat/tap.txt --no-output `
|
||||||
|
tests_ok/repeat_a.hurl `
|
||||||
|
tests_ok/repeat_b.hurl `
|
||||||
|
tests_ok/repeat_c.hurl
|
||||||
|
|
||||||
|
Write-Host (Get-Content build/repeat/tap.txt -Raw) -NoNewLine
|
10
integration/hurl/tests_ok/repeat_par.sh
Executable file
10
integration/hurl/tests_ok/repeat_par.sh
Executable file
@ -0,0 +1,10 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -Eeuo pipefail
|
||||||
|
rm -f build/repeat/tap.txt
|
||||||
|
|
||||||
|
hurl --repeat 4 --parallel --report-tap build/repeat/tap.txt --no-output \
|
||||||
|
tests_ok/repeat_a.hurl \
|
||||||
|
tests_ok/repeat_b.hurl \
|
||||||
|
tests_ok/repeat_c.hurl
|
||||||
|
|
||||||
|
cat build/repeat/tap.txt
|
12
integration/hurl/tests_ok/repeat_seq.out
Executable file
12
integration/hurl/tests_ok/repeat_seq.out
Executable file
@ -0,0 +1,12 @@
|
|||||||
|
Hello A!
|
||||||
|
Hello B!
|
||||||
|
Hello C!
|
||||||
|
Hello A!
|
||||||
|
Hello B!
|
||||||
|
Hello C!
|
||||||
|
Hello A!
|
||||||
|
Hello B!
|
||||||
|
Hello C!
|
||||||
|
Hello A!
|
||||||
|
Hello B!
|
||||||
|
Hello C!
|
7
integration/hurl/tests_ok/repeat_seq.ps1
Normal file
7
integration/hurl/tests_ok/repeat_seq.ps1
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
Set-StrictMode -Version latest
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
|
hurl --repeat 4 `
|
||||||
|
tests_ok/repeat_a.hurl `
|
||||||
|
tests_ok/repeat_b.hurl `
|
||||||
|
tests_ok/repeat_c.hurl
|
7
integration/hurl/tests_ok/repeat_seq.sh
Executable file
7
integration/hurl/tests_ok/repeat_seq.sh
Executable file
@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -Eeuo pipefail
|
||||||
|
|
||||||
|
hurl --repeat 4 \
|
||||||
|
tests_ok/repeat_a.hurl \
|
||||||
|
tests_ok/repeat_b.hurl \
|
||||||
|
tests_ok/repeat_c.hurl
|
@ -156,6 +156,12 @@ pub enum Repeat {
|
|||||||
Forever,
|
Forever,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for Repeat {
|
||||||
|
fn default() -> Self {
|
||||||
|
Repeat::Count(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn get_version() -> String {
|
fn get_version() -> String {
|
||||||
let libcurl_version = http::libcurl_version_info();
|
let libcurl_version = http::libcurl_version_info();
|
||||||
format!(
|
format!(
|
||||||
|
@ -128,9 +128,9 @@ impl Iterator for JobQueue<'_> {
|
|||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
if self.index >= self.jobs.len() {
|
if self.index >= self.jobs.len() {
|
||||||
|
self.repeat_index = self.repeat_index.checked_add(1).unwrap_or(0);
|
||||||
match self.repeat {
|
match self.repeat {
|
||||||
Repeat::Count(n) => {
|
Repeat::Count(n) => {
|
||||||
self.repeat_index = self.repeat_index.checked_add(1).unwrap_or(0);
|
|
||||||
if self.repeat_index >= n {
|
if self.repeat_index >= n {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
@ -149,3 +149,61 @@ impl Iterator for JobQueue<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::parallel::job::{Job, JobQueue};
|
||||||
|
use crate::parallel::runner::Repeat;
|
||||||
|
use crate::runner::{Input, RunnerOptionsBuilder};
|
||||||
|
use crate::util::logger::LoggerOptionsBuilder;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
fn new_job(file: &str, index: usize) -> Job {
|
||||||
|
let variables = HashMap::new();
|
||||||
|
let runner_options = RunnerOptionsBuilder::default().build();
|
||||||
|
let logger_options = LoggerOptionsBuilder::default().build();
|
||||||
|
Job::new(
|
||||||
|
&Input::new(file),
|
||||||
|
index,
|
||||||
|
&runner_options,
|
||||||
|
&variables,
|
||||||
|
&logger_options,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn job_queue_is_finite() {
|
||||||
|
let jobs = [
|
||||||
|
new_job("a.hurl", 0),
|
||||||
|
new_job("b.hurl", 1),
|
||||||
|
new_job("c.hurl", 2),
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut queue = JobQueue::new(&jobs, Repeat::Count(2));
|
||||||
|
|
||||||
|
assert_eq!(queue.next(), Some(new_job("a.hurl", 0)));
|
||||||
|
assert_eq!(queue.next(), Some(new_job("b.hurl", 1)));
|
||||||
|
assert_eq!(queue.next(), Some(new_job("c.hurl", 2)));
|
||||||
|
assert_eq!(queue.next(), Some(new_job("a.hurl", 3)));
|
||||||
|
assert_eq!(queue.next(), Some(new_job("b.hurl", 4)));
|
||||||
|
assert_eq!(queue.next(), Some(new_job("c.hurl", 5)));
|
||||||
|
assert_eq!(queue.next(), None);
|
||||||
|
|
||||||
|
assert_eq!(queue.jobs_count(), Some(6));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn input_queue_is_infinite() {
|
||||||
|
let jobs = [new_job("foo.hurl", 0)];
|
||||||
|
|
||||||
|
let mut queue = JobQueue::new(&jobs, Repeat::Forever);
|
||||||
|
assert_eq!(queue.next(), Some(new_job("foo.hurl", 0)));
|
||||||
|
assert_eq!(queue.next(), Some(new_job("foo.hurl", 1)));
|
||||||
|
assert_eq!(queue.next(), Some(new_job("foo.hurl", 2)));
|
||||||
|
assert_eq!(queue.next(), Some(new_job("foo.hurl", 3)));
|
||||||
|
assert_eq!(queue.next(), Some(new_job("foo.hurl", 4)));
|
||||||
|
// etc...
|
||||||
|
|
||||||
|
assert_eq!(queue.jobs_count(), None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -39,7 +39,10 @@ pub fn run_seq(
|
|||||||
) -> Result<Vec<HurlRun>, CliError> {
|
) -> Result<Vec<HurlRun>, CliError> {
|
||||||
let mut runs = vec![];
|
let mut runs = vec![];
|
||||||
|
|
||||||
for filename in files.iter() {
|
let repeat = options.repeat.unwrap_or_default();
|
||||||
|
let queue = InputQueue::new(files, repeat);
|
||||||
|
|
||||||
|
for filename in queue {
|
||||||
let content = filename.read_to_string();
|
let content = filename.read_to_string();
|
||||||
let content = match content {
|
let content = match content {
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
@ -49,8 +52,8 @@ pub fn run_seq(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
let variables = &options.variables;
|
let variables = &options.variables;
|
||||||
let runner_options = options.to_runner_options(filename, current_dir);
|
let runner_options = options.to_runner_options(&filename, current_dir);
|
||||||
let logger_options = options.to_logger_options(filename);
|
let logger_options = options.to_logger_options(&filename);
|
||||||
|
|
||||||
// Run our Hurl file now, we can only fail if there is a parsing error.
|
// Run our Hurl file now, we can only fail if there is a parsing error.
|
||||||
// The parsing error is displayed in the `execute` call, that's why we gobble the error
|
// The parsing error is displayed in the `execute` call, that's why we gobble the error
|
||||||
@ -64,7 +67,7 @@ pub fn run_seq(
|
|||||||
// representation of the full Hurl result.
|
// representation of the full Hurl result.
|
||||||
// In sequential run, we use an immediate (non-buffered) standard output.
|
// In sequential run, we use an immediate (non-buffered) standard output.
|
||||||
let mut stdout = Stdout::new(WriteMode::Immediate);
|
let mut stdout = Stdout::new(WriteMode::Immediate);
|
||||||
print_output(&hurl_result, &content, filename, options, &mut stdout)?;
|
print_output(&hurl_result, &content, &filename, options, &mut stdout)?;
|
||||||
|
|
||||||
let run = HurlRun {
|
let run = HurlRun {
|
||||||
content,
|
content,
|
||||||
@ -208,3 +211,103 @@ impl From<Repeat> for parallel::runner::Repeat {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An input queue to manage a queue of [`Input`].
|
||||||
|
///
|
||||||
|
/// The queue implements [`Iterator`] trait, and can return a new input to use each time its
|
||||||
|
/// `next` method is called. This queue can repeat its input sequence a certain number of times, or
|
||||||
|
/// can loop forever.
|
||||||
|
pub struct InputQueue<'a> {
|
||||||
|
/// The input list.
|
||||||
|
inputs: &'a [Input],
|
||||||
|
/// Current index of the input, referencing the input list.
|
||||||
|
index: usize,
|
||||||
|
/// Repeat mode of this queue (finite or infinite).
|
||||||
|
repeat: Repeat,
|
||||||
|
/// Current index of the repeat.
|
||||||
|
repeat_index: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> InputQueue<'a> {
|
||||||
|
/// Create a new queue, with a list of `inputs` and a `repeat` mode.
|
||||||
|
pub fn new(inputs: &'a [Input], repeat: Repeat) -> Self {
|
||||||
|
InputQueue {
|
||||||
|
inputs,
|
||||||
|
index: 0,
|
||||||
|
repeat,
|
||||||
|
repeat_index: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a new input at the given `index`.
|
||||||
|
fn input_at(&self, index: usize) -> Input {
|
||||||
|
self.inputs[index].clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for InputQueue<'_> {
|
||||||
|
type Item = Input;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
if self.index >= self.inputs.len() {
|
||||||
|
self.repeat_index = self.repeat_index.checked_add(1).unwrap_or(0);
|
||||||
|
match self.repeat {
|
||||||
|
Repeat::Count(n) => {
|
||||||
|
if self.repeat_index >= n {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
self.index = 1;
|
||||||
|
Some(self.input_at(0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Repeat::Forever => {
|
||||||
|
self.index = 1;
|
||||||
|
Some(self.input_at(0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.index += 1;
|
||||||
|
Some(self.input_at(self.index - 1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::cli::options::Repeat;
|
||||||
|
use crate::run::InputQueue;
|
||||||
|
use hurl::runner::Input;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn input_queue_is_finite() {
|
||||||
|
let files = [Input::new("a"), Input::new("b"), Input::new("c")];
|
||||||
|
|
||||||
|
let mut queue = InputQueue::new(&files, Repeat::Count(4));
|
||||||
|
assert_eq!(queue.next(), Some(Input::new("a")));
|
||||||
|
assert_eq!(queue.next(), Some(Input::new("b")));
|
||||||
|
assert_eq!(queue.next(), Some(Input::new("c")));
|
||||||
|
assert_eq!(queue.next(), Some(Input::new("a")));
|
||||||
|
assert_eq!(queue.next(), Some(Input::new("b")));
|
||||||
|
assert_eq!(queue.next(), Some(Input::new("c")));
|
||||||
|
assert_eq!(queue.next(), Some(Input::new("a")));
|
||||||
|
assert_eq!(queue.next(), Some(Input::new("b")));
|
||||||
|
assert_eq!(queue.next(), Some(Input::new("c")));
|
||||||
|
assert_eq!(queue.next(), Some(Input::new("a")));
|
||||||
|
assert_eq!(queue.next(), Some(Input::new("b")));
|
||||||
|
assert_eq!(queue.next(), Some(Input::new("c")));
|
||||||
|
assert_eq!(queue.next(), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn input_queue_is_infinite() {
|
||||||
|
let files = [Input::new("a")];
|
||||||
|
|
||||||
|
let mut queue = InputQueue::new(&files, Repeat::Forever);
|
||||||
|
assert_eq!(queue.next(), Some(Input::new("a")));
|
||||||
|
assert_eq!(queue.next(), Some(Input::new("a")));
|
||||||
|
assert_eq!(queue.next(), Some(Input::new("a")));
|
||||||
|
assert_eq!(queue.next(), Some(Input::new("a")));
|
||||||
|
assert_eq!(queue.next(), Some(Input::new("a")));
|
||||||
|
// etc...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user