in firebase-database/src/testUtil/java/com/google/firebase/database/core/RandomOperationGenerator.java [115:205]
public Operation nextOperation() {
if (this.writeOpForLastUpdate != null) {
return getAck();
}
if (random.nextDouble() < SERVER_OP_PROBABILITY) {
if (this.completeDataJustSentForListen != null) {
Path path = this.completeDataJustSentForListen.getPath();
QueryParams params = this.completeDataJustSentForListen.getParams();
OperationSource source;
if (!params.loadsAllData()) {
source =
OperationSource.forServerTaggedQuery(this.completeDataJustSentForListen.getParams());
} else {
source = OperationSource.SERVER;
}
completeDataJustSentForListen = null;
return new ListenComplete(source, path);
} else if (!outstandingListens.isEmpty()
&& random.nextDouble() < SERVER_LISTEN_RESPONSE_PROBABILITY) {
QuerySpec listen = outstandingListens.poll();
QueryParams params = listen.getParams();
Path path = listen.getPath();
Node currentServerNode = this.currentServerState.getChild(path);
if (!params.loadsAllData()) {
IndexedNode emptyNode = IndexedNode.from(EmptyNode.Empty(), params.getIndex());
IndexedNode node =
params
.getNodeFilter()
.updateFullNode(
emptyNode, IndexedNode.from(currentServerNode, params.getIndex()), null);
OperationSource source = OperationSource.forServerTaggedQuery(listen.getParams());
return new Overwrite(source, path, node.getNode());
} else {
return new Overwrite(OperationSource.SERVER, path, currentServerNode);
}
} else if (!outstandingUnlistens.isEmpty()
&& random.nextDouble() < SERVER_LISTEN_RESPONSE_PROBABILITY) {
throw new RuntimeException("NOT IMPL!");
} else if (outstandingWrites.size() > 0 && random.nextDouble() < SERVER_ACK_PROBABILITY) {
WriteOp op = outstandingWrites.peek();
Path path = op.operation.getPath();
boolean isEmptyPriorityError =
!path.isEmpty()
&& path.getBack().isPriorityChildName()
&& this.currentServerState.getChild(path.getParent()).isEmpty();
if (random.nextDouble() < REVERT_PROBABILITY || isEmptyPriorityError) {
writeTree.removeWrite(op.writeId);
outstandingWrites.remove();
return getAckForWrite(op.operation, /*revert=*/ true);
} else {
// TODO: maybe ignore if write equals server data
Operation serverOp = userOperationToServerOperation(op.operation);
this.currentServerState = applyOperation(serverOp, this.currentServerState);
this.writeOpForLastUpdate = op;
return serverOp;
}
} else {
if (random.nextDouble() < MERGE_PROBABILITY) {
Merge merge = getRandomMerge(OperationSource.SERVER);
this.currentServerState = applyOperation(merge, currentServerState);
return merge;
} else {
Overwrite overwrite = getRandomOverwrite(OperationSource.SERVER);
if (overwrite.getPath().getBack() != null
&& overwrite.getPath().getBack().isPriorityChildName()
&& this.currentServerState.getChild(overwrite.getPath().getParent()).isEmpty()) {
// This is a case where we would overwrite a priority on an empty node which is an
// illegal update
return nextOperation();
}
this.currentServerState = applyOperation(overwrite, currentServerState);
return overwrite;
}
}
} else {
if (random.nextDouble() < MERGE_PROBABILITY) {
Merge merge = getRandomMerge(OperationSource.USER);
WriteOp writeOp = new WriteOp(merge, currentWriteId++);
outstandingWrites.add(writeOp);
writeTree.addMerge(
merge.getPath(), CompoundWrite.fromChildMerge(getMergeMap(merge)), writeOp.writeId);
return merge;
} else {
Overwrite overwrite = getRandomOverwrite(OperationSource.USER);
WriteOp writeOp = new WriteOp(overwrite, currentWriteId++);
outstandingWrites.add(writeOp);
writeTree.addOverwrite(overwrite.getPath(), overwrite.getSnapshot(), writeOp.writeId, true);
return overwrite;
}
}
}