1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

Graph: Add unit tests

This commit is contained in:
reuk 2022-08-12 11:16:33 +01:00
parent 5746bc99da
commit 44a7987322
No known key found for this signature in database
GPG key ID: 9ADCD339CFC98A11

View file

@ -1777,4 +1777,166 @@ void AudioProcessorGraph::AudioGraphIOProcessor::setParentGraph (AudioProcessorG
}
}
//==============================================================================
//==============================================================================
#if JUCE_UNIT_TESTS
class AudioProcessorGraphTests : public UnitTest
{
public:
AudioProcessorGraphTests()
: UnitTest ("AudioProcessorGraph", UnitTestCategories::audioProcessors) {}
void runTest() override
{
const auto midiChannel = AudioProcessorGraph::midiChannelIndex;
beginTest ("isConnected returns true when two nodes are connected");
{
AudioProcessorGraph graph;
const auto nodeA = graph.addNode (BasicProcessor::make ({}, MidiIn::no, MidiOut::yes))->nodeID;
const auto nodeB = graph.addNode (BasicProcessor::make ({}, MidiIn::yes, MidiOut::no))->nodeID;
expect (graph.canConnect ({ { nodeA, midiChannel }, { nodeB, midiChannel } }));
expect (! graph.canConnect ({ { nodeB, midiChannel }, { nodeA, midiChannel } }));
expect (! graph.canConnect ({ { nodeA, midiChannel }, { nodeA, midiChannel } }));
expect (! graph.canConnect ({ { nodeB, midiChannel }, { nodeB, midiChannel } }));
expect (graph.getConnections().empty());
expect (! graph.isConnected ({ { nodeA, midiChannel }, { nodeB, midiChannel } }));
expect (! graph.isConnected (nodeA, nodeB));
expect (graph.addConnection ({ { nodeA, midiChannel }, { nodeB, midiChannel } }));
expect (graph.getConnections().size() == 1);
expect (graph.isConnected ({ { nodeA, midiChannel }, { nodeB, midiChannel } }));
expect (graph.isConnected (nodeA, nodeB));
expect (graph.disconnectNode (nodeA));
expect (graph.getConnections().empty());
expect (! graph.isConnected ({ { nodeA, midiChannel }, { nodeB, midiChannel } }));
expect (! graph.isConnected (nodeA, nodeB));
}
beginTest ("graph lookups work with a large number of connections");
{
AudioProcessorGraph graph;
std::vector<AudioProcessorGraph::NodeID> nodeIDs;
constexpr auto numNodes = 100;
for (auto i = 0; i < numNodes; ++i)
{
nodeIDs.push_back (graph.addNode (BasicProcessor::make (BasicProcessor::getStereoProperties(),
MidiIn::yes,
MidiOut::yes))->nodeID);
}
for (auto it = nodeIDs.begin(); it != std::prev (nodeIDs.end()); ++it)
{
expect (graph.addConnection ({ { it[0], 0 }, { it[1], 0 } }));
expect (graph.addConnection ({ { it[0], 1 }, { it[1], 1 } }));
}
// Check whether isConnected reports correct results when called
// with both connections and nodes
for (auto it = nodeIDs.begin(); it != std::prev (nodeIDs.end()); ++it)
{
expect (graph.isConnected ({ { it[0], 0 }, { it[1], 0 } }));
expect (graph.isConnected ({ { it[0], 1 }, { it[1], 1 } }));
expect (graph.isConnected (it[0], it[1]));
}
const auto& nodes = graph.getNodes();
expect (! graph.isAnInputTo (*nodes[0], *nodes[0]));
// Check whether isAnInputTo behaves correctly for a non-cyclic graph
for (auto it = std::next (nodes.begin()); it != std::prev (nodes.end()); ++it)
{
expect (! graph.isAnInputTo (**it, **it));
expect (graph.isAnInputTo (*nodes[0], **it));
expect (! graph.isAnInputTo (**it, *nodes[0]));
expect (graph.isAnInputTo (**it, *nodes[nodes.size() - 1]));
expect (! graph.isAnInputTo (*nodes[nodes.size() - 1], **it));
}
// Make the graph cyclic
graph.addConnection ({ { nodeIDs.back(), 0 }, { nodeIDs.front(), 0 } });
graph.addConnection ({ { nodeIDs.back(), 1 }, { nodeIDs.front(), 1 } });
// Check whether isAnInputTo behaves correctly for a cyclic graph
for (const auto* node : graph.getNodes())
{
expect (graph.isAnInputTo (*node, *node));
expect (graph.isAnInputTo (*nodes[0], *node));
expect (graph.isAnInputTo (*node, *nodes[0]));
expect (graph.isAnInputTo (*node, *nodes[nodes.size() - 1]));
expect (graph.isAnInputTo (*nodes[nodes.size() - 1], *node));
}
}
}
private:
enum class MidiIn { no, yes };
enum class MidiOut { no, yes };
class BasicProcessor : public AudioProcessor
{
public:
explicit BasicProcessor (const AudioProcessor::BusesProperties& layout, MidiIn mIn, MidiOut mOut)
: AudioProcessor (layout), midiIn (mIn), midiOut (mOut) {}
const String getName() const override { return "Basic Processor"; }
double getTailLengthSeconds() const override { return {}; }
bool acceptsMidi() const override { return midiIn == MidiIn ::yes; }
bool producesMidi() const override { return midiOut == MidiOut::yes; }
AudioProcessorEditor* createEditor() override { return {}; }
bool hasEditor() const override { return {}; }
int getNumPrograms() override { return 1; }
int getCurrentProgram() override { return {}; }
void setCurrentProgram (int) override {}
const String getProgramName (int) override { return {}; }
void changeProgramName (int, const String&) override {}
void getStateInformation (juce::MemoryBlock&) override {}
void setStateInformation (const void*, int) override {}
void prepareToPlay (double, int) override {}
void releaseResources() override {}
void processBlock (AudioBuffer<float>&, MidiBuffer&) override {}
bool supportsDoublePrecisionProcessing() const override { return true; }
bool isMidiEffect() const override { return {}; }
void reset() override {}
void setNonRealtime (bool) noexcept override {}
using AudioProcessor::processBlock;
static std::unique_ptr<AudioProcessor> make (const BusesProperties& layout,
MidiIn midiIn,
MidiOut midiOut)
{
return std::make_unique<BasicProcessor> (layout, midiIn, midiOut);
}
static BusesProperties getStereoProperties()
{
return BusesProperties().withInput ("in", AudioChannelSet::stereo())
.withOutput ("out", AudioChannelSet::stereo());
}
private:
MidiIn midiIn;
MidiOut midiOut;
};
};
static AudioProcessorGraphTests audioProcessorGraphTests;
#endif
} // namespace juce