Ensure that clean-up code is executed even after '#Timeout' - testing

As titled
For example, I have below test cases.
class StartupTest {
def deploymentPath = "build/milestone/deployment"
void checkConnectedProducts(){
def productConnected = XXXhelper.countConnectedProducts()
//Called another function in another class to check the number of connected products, every 1 sec
while (productConnected>2){
Thread.sleep(1000)
productConnected = XXXhelper.countConnectedProducts()
}
}
int countError(){
def error = xxx.logFinder.search("(ERROR)",TimeFrames.from(LocalDateTime.now().minusSeconds(20)).to(LocalDateTime.now().plusSeconds(20)))
return error.size()
}
#Timeout(80)
def 'Start_test'() {
setup:
//do some setup here
when: 'Test_started'
//do something here
and: 'Check_something'
//check something here
then: 'Validate_something'
checkConnectedProducts()
cleanup:
//Call the function in another class to zip the file and send out message to slack
File log = new File("$deploymentPath/logs")
def error = countError()
def zipFile = "xxx.zip"
IOHelper.createZip(log,zipFile)
File zipFile = new File(zipFile)
//Pass the zip to another class
ZipUploader.Slack(zipFile,error)
}
}
Below class is used to upload zipped file and send message to slack channel
class ZipUploader {
static uploadLocation = System.getProperty("benchmark-result-server")
static proxyHost = System.getProperty("proxy-host")
static proxyPort = System.getProperty("proxy-port")
static Slack(File file, int errorCount){
//Upload file
OkHttpClient client = new OkHttpClient()
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("zip", file.name,
RequestBody.create(file, MediaType.parse(com.google.common.net.MediaType.ZIP.type()))
)
.build()
Request request = new Request.Builder()
.url("$uploadLocation/!/xxxxxxxxxxxx")
.post(requestBody)
.build()
//Check the response
Response response = client.newCall(request).execute()
if (!response.isSuccessful()) throw new Exception("File cannot be uploaded" + response)
//Slack
OkHttpClient clientSlack = new OkHttpClient().newBuilder()
.proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, proxyPort)))
.build()
MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, "{\n \"text\": \"Test has been finished \\t Error Log: $errorCount"\n}")
Request requestSlack = new Request.Builder()
.url("https://hooks.slack.com/services/xxxxxxx/xxxxxx/xxxxxx?Content-type=application/json")
.method("POST", body)
.addHeader("Content-Type", "application/json")
.build()
Response responseSlack = clientSlack.newCall(requestSlack).execute()
//Check the response
if (!responseSlack.isSuccessful()) throw new Exception("Failed to send message to slack " + responseSlack)
}
}
So if the test run within 80sec, the function in cleanup can be execute successfully, but I also want the function to execute even the test exceed timeout, I don't know how to achieve it.
It will just stopped the whole test in the middle if the test exceed timeout and return timeout exception, and the function under cleanup: will not be executed, including the function to upload zip and send slack notice.

#kriegaex, thanks you for your answer. For my error log, it returns as below.
[spock.lang.Timeout] Method 'Start_test' has not yet returned - interrupting. Next try in 0.50 seconds.
[spock.lang.Timeout] Method 'Start_test' has not yet returned - interrupting. Next try in 1.00 seconds.
[spock.lang.Timeout] Method 'Start_test' has not yet returned - interrupting. Next try in 2.00 seconds.
Method timed out after 70.00 seconds
Method timed out after 70.00 seconds
at app//xxx.checkConnectedProducts(StartupTest.groovy:36)
at xxx.StartupTest.Start_test(StartupTest.groovy:71)
xxx.StartupTest > Start_test STANDARD_OUT
[spock.lang.Timeout] Method 'Start_test' has not yet returned - interrupting. Next try in 0.50 seconds.
[spock.lang.Timeout] Method 'Start_test' has not yet returned - interrupting. Next try in 1.00 seconds.
[spock.lang.Timeout] Method 'Start_test' has not yet returned - interrupting. Next try in 2.00 seconds.
Gradle Test Executor 4 finished executing tests.
> Task :test
TRACE [startup log cleaner] StartupLogFactory - Flushing delayed logs as application terminates...
TRACE [startup log cleaner] StartupLogFactory - Caution: timestamps are those at the time of the flush!
DEBUG [startup log cleaner] Connector - Connecting to localhost:6450 with user admin
TRACE [startup log cleaner] StartupLogFactory - Finished flushing delayed logs.
xxx.StartupTest > Start_test FAILED
Method timed out after 70.00 seconds
at app//xxx.StartupTest.checkConnectedProducts(StartupTest.groovy:36)
at xxx.StartupTest.Start_test(StartupTest.groovy:71)
DEBUG [pool-10-thread-2] Connector - Connecting to 0.0.0.0:6450 with user <null>
1 test completed, 1 failed
It just stopped at the checkConnectedProducts(). And do not proceed IOHelper.createZip(log,zipFile) under cleanup: block.
I tried to add println("abc") under cleanup: block, indeed it can be executed after timeout, but not the zip function and upload zip class function.
Could you advice on how to make IOHelper.createZip and also Zipuploader under cleanup: block to run after timeout? In my case, they did not proceed. Thanks a lot!!!

Revised answer (2022-02-27)
I want to revise my previous statements with regard to
how I was emulating the situation and
how I was interpreting the results:
Because I had a similar use case and used my example from here as a starting point, I noticed that there is a fundamental difference between
JRE's Thread.sleep(long),
Groovy's sleep(long) and
Groovy's sleep(long, Closure).
See also this article.
I was using both Thread.sleep (in checkConnectedProducts()) and simple sleep (in the cleanup: block and the method called from there), which is why the result was the way it is shown in the log output.
Please note that Thread.sleep is interruptible, while sleep is not! Only an extended Groovy variant like sleep(4000), { e -> true } is also interruptable.
I.e., when the timeout occurs and Spock tries to interrupt the test, it successfully interrupts the Thread.sleep in checkConnectedProducts, but then immediately the two non-interruptible sleep 4000 variants are called from the cleanup: block, which means that they ignore Spock's repeated tries to interrupt them too, as you can see in the console log. If there we would also use an interruptible sleep or simply normal, interruptible Groovy or Java code, the cleanup: block or methods called from there would indeed also be interrupted by Spock quickly.
Now, assuming that you really need to make sure that the clean-up code is executed, even if the method gets interrupted by #Timeout, you simply move the clean-up code from the cleanup: block to a cleanup() method (which then gets executed for each feature method in the Spock specification). Look at this:
package de.scrum_master.stackoverflow.q60601740
import spock.lang.Issue
import spock.lang.Specification
import spock.lang.Timeout
import java.util.concurrent.TimeUnit
import static java.lang.System.currentTimeMillis
#Issue("https://stackoverflow.com/a/60610944/1082681")
class StartupTest extends Specification {
static final int SLEEP_MILLIS = 5000
static final int TIMEOUT_MILLIS = 3000
def startTime = currentTimeMillis()
def timeElapsed() {
currentTimeMillis() - startTime
}
void checkConnectedProducts() {
printf "%6d | checkConnectedProducts, sleeping %d ms%n", timeElapsed(), SLEEP_MILLIS
// Interruptable
Thread.sleep SLEEP_MILLIS
// Non-interruptable
// sleep SLEEP_MILLIS
// Interruptable
// sleep SLEEP_MILLIS, { true }
printf "%6d | checkConnectedProducts finished%n", timeElapsed()
}
int countError() {
printf "%6d | countError, sleeping %d ms%n", timeElapsed(), SLEEP_MILLIS
// Interruptable, but will not be interrupted by '#Timeout' when called from method 'cleanup'
Thread.sleep SLEEP_MILLIS
printf "%6d | countError finished%n", timeElapsed(), SLEEP_MILLIS
return 3
}
#Timeout(value = TIMEOUT_MILLIS, unit = TimeUnit.MILLISECONDS)
def 'Start_test'() {
expect: 'Validate_something'
checkConnectedProducts()
}
/**
* Even if a feature method (including its 'cleanup:' block) times out and gets aborted,
* the 'cleanup' method will be executed.
*/
def cleanup() {
countError()
printf "%6d | cleanup, sleeping %d ms%n", timeElapsed(), SLEEP_MILLIS
// Interruptable, but will not be interrupted by '#Timeout' within method 'cleanup'
Thread.sleep SLEEP_MILLIS
printf "%6d | cleanup finished%n", timeElapsed()
// Verify that really as much time has elapsed as we expect, i.e. 'cleanup()' has not been interrupted
assert timeElapsed() >= TIMEOUT_MILLIS + SLEEP_MILLIS + SLEEP_MILLIS
}
}
The console log says:
143 | checkConnectedProducts, sleeping 5000 ms
3138 | countError, sleeping 5000 ms
8147 | countError finished
8151 | cleanup, sleeping 5000 ms
13157 | cleanup finished
Method timed out after 3,00 seconds
at app//org.codehaus.groovy.vmplugin.v7.IndyInterface.selectMethod(IndyInterface.java:234)
at app//de.scrum_master.stackoverflow.q60601740.StartupTest.checkConnectedProducts(StartupTest.groovy:25)
at de.scrum_master.stackoverflow.q60601740.StartupTest.Start_test(StartupTest.groovy:44)
Try in in the Groovy web console.
See? The test method gets interrupted after 3 seconds, but the 5 + 5 = 10 extra seconds in cleanup() fully elapse, i.e. after cleanup we have a total of 13 elapsed seconds in the log.
If you want to speed up the test, simply change the static constants from 5000/3000 to 500/300.
BTW, you can also add a separate #Timeout to cleanup() or any other fixture method, but that is a bit out of scope here.
Original answer
I cannot reproduce your problem, given the code snippets you have provided instead of an MCVE. I replicated a simplified version of your situation like this, just replacing the operations consuming time in your example by Groovy sleep or Java Thread.sleep statements:
package de.scrum_master.stackoverflow.q60601740
import spock.lang.Specification
import spock.lang.Timeout
import static java.lang.System.currentTimeMillis
class StartupTest extends Specification {
def startTime = currentTimeMillis()
def timeElapsed() {
currentTimeMillis() - startTime
}
void checkConnectedProducts() {
for (int i = 1; i <= 5; i++) {
println "${timeElapsed()} | checkConnectedProducts #$i, sleeping 1 s"
Thread.sleep(1000)
}
}
int countError() {
println "${timeElapsed()} | countError, sleeping 4 s"
sleep 4000
return 3
}
#Timeout(3)
def 'Start_test'() {
setup:
true
when: 'Test_started'
true
and: 'Check_something'
true
then: 'Validate_something'
checkConnectedProducts()
cleanup:
countError()
println "${timeElapsed()} | cleanup, sleeping 4 s"
sleep 4000
println "${timeElapsed()} | cleanup finished"
}
}
The console log looks as expected:
64 | checkConnectedProducts #1, sleeping 1 s
1112 | checkConnectedProducts #2, sleeping 1 s
2114 | checkConnectedProducts #3, sleeping 1 s
3011 | countError, sleeping 4 s
[spock.lang.Timeout] Method 'Start_test' has not yet returned - interrupting. Next try in 0,50 seconds.
[spock.lang.Timeout] Method 'Start_test' has not yet returned - interrupting. Next try in 1,00 seconds.
[spock.lang.Timeout] Method 'Start_test' has not yet returned - interrupting. Next try in 2,00 seconds.
[spock.lang.Timeout] Method 'Start_test' has not yet returned - interrupting. Next try in 4,00 seconds.
7012 | cleanup, sleeping 4 s
[spock.lang.Timeout] Method 'Start_test' has not yet returned - interrupting. Next try in 8,00 seconds.
11012 | cleanup finished
Method timed out after 3,00 seconds
at de.scrum_master.stackoverflow.q60601740.StartupTest.checkConnectedProducts(StartupTest.groovy:18)
at de.scrum_master.stackoverflow.q60601740.StartupTest.Start_test(StartupTest.groovy:40)
As you can see, the first 3 iterations of the for loop, each expending ~1 s, are being executed before the 3 s timeout interrupts the test in the method called from the then: block. After that, the cleanup block runs completely, both the called method and the direct sleep, each spending ~4 s in addition to the 3 elapsed seconds. All in all we have 3 + 4 + 4 = 11 seconds, so everything works as expected.
Unless you can provide an MCVE reproducing your problem as Tim asked you to already, I would think there is no problem in Spock but rather something you misinterpret from your log output or another problem in your code which you did not show to us, such as a cleanup or zip operation done in an extra thread.

Related

Unit testing infinite Flux speed of generation

I have Flux, which generates event in some pace (in infinite manner). I would like to use StepVerifier that after 5 seconds there is at least 2 events generated. How can I verify this behavior using StepVerifier?
Sample flux for testing can look like this:
public fluxTest() {
final AtomicLong counter = new AtomicLong(0);
final Random rnd = new Random();
final Flux<String> randomIntervalEmitter = Flux.generate(generator -> {
try {
final long counterDivided = counter.get() % 12;
if (counterDivided > 0) {
TimeUnit.SECONDS.sleep(rnd.nextInt(1, 10));
} else {
TimeUnit.MILLISECONDS.sleep(rnd.nextInt(1, 50));
}
generator.next("asdf " + counterDivided);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
final Flux<String> regularDummyUpdate = Flux.interval(Duration.ofSeconds(5)).map(e -> "" + (88 + (System.currentTimeMillis() % 104)));
final Flux<String> stringFluxWithSomePace = randomIntervalEmitter.mergeWith(regularDummyUpdate);
stringFluxWithSomePace.subscribe(System.out::println);
}
As stated in javadoc of StepVerifier:
The verification must be triggered after the terminal expectations
(completion, error, cancellation) have been declared, by calling one
of the verify() methods.
So when I have infinite source, I need to trigger some terminal operation. Complete and Error is emitted from source (I am not changing it in any way for tests) and Cancel can be triggered by subscriber (StepVerifier). So using thenCancel() and verify() on StepVerifier, I can achieve to assert only on the beginning of the infinite stream.
final var sv = StepVerifier.create(stringFluxWithSomePace.log())
// in 23 seconds should at least some "heartbeat" come at least three times
.expectSubscription()
.expectNextCount(3)
.thenCancel()
.verify(Duration.ofSeconds(23));
Nice thing is, that after receiving any 3 items, stream is cancelled and test finished (in my case usually under a second).

foreach kotlin waiting for intervals to follow the flow

Can someone help me in the following:
I have a FOR and inside it I run some TIMERS
my intention is that initially it runs the TIMER for the first index and only after the timer is canceled does the method go to the next timer. my FOR would need to wait for the TIMER to be finalized.
for (i in 1..3) {
println("running for loop: $i")
val interval: Long = 60 * 1000 * 1 // 1 minute
val timer = Timer("timer: $i", true)
timer.schedule(100, interval) {
println("${LocalTime.now()} to loop: $i")
// some logic here do stop the timer
// if (something happens) timer.cancel()
}
}
my output
running for loop: 1
running for loop: 2
running for loop: 3
18:34:57.147132900 to loop: 2
18:34:57.147132900 to loop: 3
18:34:57.147132900 to loop: 1
18:35:57.137764800 to loop: 3
18:35:57.137764800 to loop: 2
18:35:57.137764800 to loop: 1
etc...
my output as I hope it will be
running for loop: 1
18:34:57.147132900 to loop: 1
18:35:57.147132900 to loop: 1
18:36:57.147132900 to loop: 1
18:37:57.147132900 to loop: 1
running for loop: 2
18:38:57.147132900 to loop: 2
18:39:57.147132900 to loop: 2
running for loop: 3
18:40:57.147132900 to loop: 3
18:41:57.147132900 to loop: 3
18:42:57.147132900 to loop: 3
I have a FOR and inside it I run some TIMERS
This statement is wrong, java.util.Timer is a utility class that can be used to schedule a thread to be executed at certain time in future.
timer.schedule(100, interval) {}
The above line does not block the for loop, the timer is scheduled to be run on the thread and the for loop continues to do next iteration.
You probably need a blocking solution for your purpose of executing them one by one
for (i in 1..3) {
println("running for loop: $i")
val interval: Long = 60 * 1000 * 1 // 1 minute
Thread.sleep(100)
var isRunning = true
while(isRunning) {
println("${LocalTime.now()} to loop: $i")
Thread.sleep(interval)
// some logic here do stop the timer
// if (something happens) isRunning = false
}
}
But probably it isn't the best practise since your thread is just blocked, you could use coroutines to do that without wasting your system resources by blocking threads:
fun main() = runBlocking {
launch {
for (i in 1..3) {
println("running for loop: $i")
val interval: Long = 60 * 1000 * 1 // 1 minute
delay(100)
var isRunning = true
while(isRunning) {
println("${LocalTime.now()} to loop: $i")
delay(interval)
// some logic here do stop the timer
// if (something happens) isRunning = false
}
}
}
// ...
// do sth in main without blocking, these all things run on same thread
// none of the code is blocked
}

sleep/wait function in velocity

Using apache velocity, I am performing a SNMP poll to a network device for Received packets. The aim is to get the Received packets per sec (PPS) but the network device only returns cumulative value.
So my plan was to run the snmp poll every sec and I can subtract the old valued from the new value to get PPS.
I could not find any sleep or wait function in apache velocity that can delay the poll by one sec.
Below is my script for reference.
#device($defensePro, 'type=defensePro', 'lazyConnect=true')
#param($output, 'type=string', 'direction=out')
#set($dp = $defensePro.readAllBeans('RsACCStatEntry'))
#result('text/text')
#foreach($x in $dp)
#if($x.id.toString().equals('RECEIVED'))
#set($id = $x.id)
#set($value = $x.value)
$date.get('H:m:s') Poll $velocityCount: $id $value
#end
#end
#end
Output
18:33:53 Poll 1: RECEIVED 18878707
Can someone please let me know if there is a sleep function in apache velocity or any other way of achieving the desired result
I'm not sure it the right way, but Velocity can call java methods
Add method (to your defensePro for example) as
public void sleep() { Thread.sleep(1000) }
And call it from template
$defensePro.sleep();
Found a very old answer in velocity mailing list
Thread.sleep should work. It pauses the current Thread for the given
amount of time
Thanks for the reply, below worked for me
#device($defensePro, 'type=defensePro', 'lazyConnect=true')
#param($output, 'type=string', 'direction=out')
#set($dp = $defensePro.readAllBeans('RsACCStatEntry'))
#macro(sleepX, $seconds)
#set($duration = $seconds * 1000)
#set($class = $class.inspect('java.lang.Thread'))
$class.type.sleep($duration)
#end
#foreach($i in [1..6])
#foreach($x in $dp)
#if($x.id.toString().equals('RECEIVED'))
#set($id = $x.id)
#set($value = $x.value)
$date.get('H:m:s') Poll $i: $id $value
#sleepX(1)
#end #end #end
Sample Output
17:4:10 Poll 1: RECEIVED 0
17:4:12 Poll 2: RECEIVED 0
17:4:13 Poll 3: RECEIVED 0
17:4:14 Poll 4: RECEIVED 0
17:4:15 Poll 5: RECEIVED 0
17:4:16 Poll 6: RECEIVED 0

GCD - Critical Section/Mutex

Can somebody answer with short example:
How to correctly Lock code part with condition: if this part is locked by some thread don't hold other threads just skip this part by other threads and keep going.
ok, here is the working example (credit goes to #KenThomases ...)
import Dispatch
let semaphore = DispatchSemaphore(value: 1)
let printQueue = DispatchQueue(label: "print queue")
let group = DispatchGroup()
func longRuningTask(i: Int) {
printQueue.async(group: group) {
print(i,"GREEN semaphore")
}
usleep(1000) // cca 1 milisecond
printQueue.async(group: group) {
print(i,"job done")
}
}
func shortRuningTask(i: Int) {
group.enter()
guard semaphore.wait(timeout: .now() + 0.001) == .success else { // wait for cca 1 milisecond from now
printQueue.async(group: group) {
print(i,"RED semaphore, job not done")
}
group.leave()
return
}
longRuningTask(i: i)
semaphore.signal()
group.leave()
}
printQueue.async(group: group) {
print("running")
}
DispatchQueue.concurrentPerform(iterations: 10, execute: shortRuningTask )
group.wait()
print("all done")
and its printout
running
0 GREEN semaphore
2 RED semaphore, job not done
1 RED semaphore, job not done
3 RED semaphore, job not done
0 job done
4 GREEN semaphore
5 RED semaphore, job not done
6 RED semaphore, job not done
7 RED semaphore, job not done
4 job done
8 GREEN semaphore
9 RED semaphore, job not done
8 job done
all done
Program ended with exit code: 0

how to suspend for 200 ticks while delay 400 ticks in vxworks

I'm trying to code a program in vxworks. When a task total delay is 400 ticks, it was suspended at the 100th tick for 20 ticks, then resume to delay.
My main code is like the following:
void DelaySuspend (int level)
{
int tid, suspend_start,suspend_end,i;
suspend_start = vxTicks + 100;
suspend_end = vxTicks + 120;
i = vxTicks;
/* myfunction has taskDelay(400)*/
tid = taskSpawn("tMytask",200,0,2000,(FUNCPTR)myfunction,0,0,0,0,0,0,0,0,0,0);
/* tick between vxTicks+100 and vxTicks+120,suspend tMytask*/
while (i<suspend_start)
{
i=tickGet();
}
while (i <= suspend_end &&i >= suspend_start)
{
i = tickGet();
taskSuspend(tid);
}
}
What I want is to verify total delay time(or tick) doesn't change even I suspend the task for some time. I know the answer but just try to program it to show how vxWorks does it.
I am still not 100% clear on what you are trying to do, but calling taskSuspend in a loop like that isn't going to suspend the task any more. I am guessing you want something like this:
void DelaySuspend (int level)
{
int tid, suspend_start,suspend_end,i;
suspend_start = vxTicks + 100;
suspend_end = vxTicks + 120;
i = vxTicks;
/* myfunction has taskDelay(400)*/
tid = taskSpawn("tMytask",200,0,2000,(FUNCPTR)myfunction,0,0,0,0,0,0,0,0,0,0);
/* tick between vxTicks+100 and vxTicks+120,suspend tMytask*/
while (i<suspend_start)
{
i=tickGet();
}
taskSuspend(tid);
while (i <= suspend_end &&i >= suspend_start)
{
i = tickGet();
}
}
I just pulled the taskSuspend out of the loop, maybe you also want a taskResume in there after the loop or something? I am not sure what you are attempting to accomplish.
Whatever the case, there are probably better ways to do whatever you want, in general using taskSuspend is a bad idea because you have no idea what the task is doing when you suspend it. So for example if the suspended task is doing File I/O when you suspend it, and it has the file system mutex, then you cannot do any file I/O until you resume that task...
In general it is much better to block on a taskDelay/semaphore/mutex/message queue than use taskSuspend. I understand that this is just a test, and as such doing this may be ok, but if this test becomes production code, then you are asking for problems.