Add new post
This commit is contained in:
parent
75d5eb811e
commit
a8b379458f
|
@ -0,0 +1,96 @@
|
|||
---
|
||||
title: "Synchronize goroutines in your tests"
|
||||
created_at: 2014-08-29 13:40:36 +0200
|
||||
kind: article
|
||||
tags:
|
||||
- golang
|
||||
- go
|
||||
- goroutines
|
||||
- testing
|
||||
- synchronization
|
||||
summary: |
|
||||
In most cases testing channels is not necessary. You can easily wrap the functionality
|
||||
under test in a function and test that. Sometimes, however, you really need to validate
|
||||
that those channels behave the way you want.
|
||||
---
|
||||
I have been working on an [emulator for the MOS 6502 Microprocessor, written in Go](1). As
|
||||
part of this package I have also implemented a minimal 6551 Asynchronous Communication Interface
|
||||
Adapter. The 6551 provides serial IO and is easy to use in combination with the 6502.
|
||||
|
||||
When the microprocessor writes a byte to the 6551 it is stored in the `tx` (transmit) register
|
||||
where it's available for other hardware components to read.
|
||||
|
||||
In order to make my emulator available using websockets (more on that in a later post), I had
|
||||
to _know_ when a new byte became available. In a real hardware design I'd probably have to use
|
||||
the \CTS (Clear to Send) input pin to signal the 6551 I'm ready to read the next byte of data.
|
||||
|
||||
But, my goal is to emulate the _Microprocessor_ not a complete computer hardware design.o
|
||||
|
||||
The logical option in Go would be to use a channel that receives new bytes when they are
|
||||
written by the Microprocessor.
|
||||
|
||||
I quickly devised the following:
|
||||
|
||||
:::go
|
||||
type Acia6551 struct {
|
||||
// ... ommitted for brevity
|
||||
output chan []byte
|
||||
}
|
||||
|
||||
This would allow me to create an instance of type Acia6551 and supply it with an output
|
||||
channel.
|
||||
|
||||
The calling code would start a goroutine waiting for output on this channel.
|
||||
|
||||
:::go
|
||||
output := make(chan []byte)
|
||||
acia := &Acia6551{output: output}
|
||||
|
||||
go func() {
|
||||
for {
|
||||
for _, b := range <-output {
|
||||
// Handle output byte
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
Awesome. This worked very well in my websockets prototype.
|
||||
|
||||
The problem is: how do you test this? How do you test that bytes written by the
|
||||
microcontroller to the Acia are actually sent out to the output channel?
|
||||
|
||||
The solution was to create a goroutine in the test and use another channel to synchronize
|
||||
the writing of the byte with the output appearing on the output channel.
|
||||
|
||||
:::go
|
||||
func TestAciaOutputChannel(t *testing.T) {
|
||||
var value []byte // Used to store the output value from the channel
|
||||
|
||||
output := make(chan []byte) // The output channel
|
||||
done := make(chan bool) // Channel signaling we're done waitign
|
||||
|
||||
acia := &Acia6551{output: output}
|
||||
|
||||
// Create a goroutine, waiting for data on the output channel,
|
||||
// and sending a signal when data arrived.
|
||||
go func() {
|
||||
value = <-output // Block, waiting for []byte
|
||||
done <- true // We're done!
|
||||
}()
|
||||
|
||||
acia.WriteByte(0x00, 0x42) // Writes a single byte of data to the 6551
|
||||
|
||||
<-done // Wait for the output to be received and stored in `value`
|
||||
|
||||
if value[0] != 0x42 {
|
||||
t.Errorf("Expected output not received through output channel")
|
||||
}
|
||||
}
|
||||
|
||||
This is an example of simple synchronization using a seperate channel, in this case `chan bool`.
|
||||
It worked great in my tests to verify data was actually written to the output channel.
|
||||
|
||||
[Read more about my i6502 project](2)
|
||||
|
||||
[1]: https://github.com/ariejan/i6502
|
||||
[2]: http://ariejan.github.io/i6502/
|
Loading…
Reference in New Issue
Block a user