Unverified Commit ee76d15e authored by Thomas Mortagne's avatar Thomas Mortagne Committed by GitHub
Browse files

Merge pull request #194 from michitux/XRENDERING-631

parents 94168509 f4947e29
......@@ -29,6 +29,7 @@
import org.xwiki.rendering.listener.MetaData;
import org.xwiki.rendering.listener.reference.ResourceReference;
import org.xwiki.rendering.syntax.Syntax;
import org.xwiki.stability.Unstable;
/**
* Indicates block element for which we are inside and previous blocks.
......@@ -79,6 +80,8 @@ public class BlockStateChainingListener extends AbstractChainingListener impleme
private Event previousEvent = Event.NONE;
private final Deque<Event> eventStack = new ArrayDeque<>();
private int inlineDepth;
private boolean isInParagraph;
......@@ -248,8 +251,31 @@ public boolean isInMacro()
return getMacroDepth() > 0;
}
/**
* @return The event that encloses the current event.
* @since 14.0RC1
*/
@Unstable
public Event getParentEvent()
{
return this.eventStack.peek();
}
// Events
/**
* {@inheritDoc}
*
* @since 14.0RC1
*/
@Override
public void beginDocument(MetaData metadata)
{
super.beginDocument(metadata);
this.eventStack.push(Event.DOCUMENT);
}
@Override
public void beginDefinitionDescription()
{
......@@ -257,6 +283,8 @@ public void beginDefinitionDescription()
++this.definitionListDepth.peek().definitionListItemIndex;
super.beginDefinitionDescription();
this.eventStack.push(Event.DEFINITION_DESCRIPTION);
}
/**
......@@ -270,6 +298,8 @@ public void beginDefinitionList(Map<String, String> parameters)
this.definitionListDepth.push(new DefinitionListState());
super.beginDefinitionList(parameters);
this.eventStack.push(Event.DEFINITION_LIST);
}
@Override
......@@ -279,6 +309,8 @@ public void beginDefinitionTerm()
++this.definitionListDepth.peek().definitionListItemIndex;
super.beginDefinitionTerm();
this.eventStack.push(Event.DEFINITION_TERM);
}
/**
......@@ -292,6 +324,8 @@ public void beginLink(ResourceReference reference, boolean freestanding, Map<Str
++this.linkDepth;
super.beginLink(reference, freestanding, parameters);
this.eventStack.push(Event.LINK);
}
@Override
......@@ -300,6 +334,8 @@ public void beginList(ListType type, Map<String, String> parameters)
this.listDepth.push(new ListState());
super.beginList(type, parameters);
this.eventStack.push(Event.LIST);
}
@Override
......@@ -309,6 +345,8 @@ public void beginListItem()
++this.listDepth.peek().listItemIndex;
super.beginListItem();
this.eventStack.push(Event.LIST_ITEM);
}
@Override
......@@ -318,6 +356,8 @@ public void beginListItem(Map<String, String> parameters)
++this.listDepth.peek().listItemIndex;
super.beginListItem(parameters);
this.eventStack.push(Event.LIST_ITEM);
}
@Override
......@@ -326,6 +366,34 @@ public void beginMacroMarker(String name, Map<String, String> parameters, String
++this.macroDepth;
super.beginMacroMarker(name, parameters, content, isInline);
this.eventStack.push(Event.MACRO_MARKER);
}
/**
* {@inheritDoc}
*
* @since 14.0RC1
*/
@Override
public void beginMetaData(MetaData metadata)
{
super.beginMetaData(metadata);
this.eventStack.push(Event.META_DATA);
}
/**
* {@inheritDoc}
*
* @since 14.0RC1
*/
@Override
public void beginGroup(Map<String, String> parameters)
{
super.beginGroup(parameters);
this.eventStack.push(Event.GROUP);
}
@Override
......@@ -335,6 +403,8 @@ public void beginParagraph(Map<String, String> parameters)
++this.inlineDepth;
super.beginParagraph(parameters);
this.eventStack.push(Event.PARAGRAPH);
}
@Override
......@@ -343,6 +413,8 @@ public void beginQuotation(Map<String, String> parameters)
++this.quotationDepth;
super.beginQuotation(parameters);
this.eventStack.push(Event.QUOTATION);
}
@Override
......@@ -353,6 +425,8 @@ public void beginQuotationLine()
++this.quotationLineIndex;
super.beginQuotationLine();
this.eventStack.push(Event.QUOTATION_LINE);
}
@Override
......@@ -362,6 +436,8 @@ public void beginHeader(HeaderLevel level, String id, Map<String, String> parame
++this.inlineDepth;
super.beginHeader(level, id, parameters);
this.eventStack.push(Event.HEADER);
}
@Override
......@@ -370,6 +446,8 @@ public void beginTable(Map<String, String> parameters)
this.isInTable = true;
super.beginTable(parameters);
this.eventStack.push(Event.TABLE);
}
@Override
......@@ -378,6 +456,8 @@ public void beginTableRow(Map<String, String> parameters)
++this.cellRow;
super.beginTableRow(parameters);
this.eventStack.push(Event.TABLE_ROW);
}
@Override
......@@ -388,6 +468,8 @@ public void beginTableCell(Map<String, String> parameters)
++this.cellCol;
super.beginTableCell(parameters);
this.eventStack.push(Event.TABLE_CELL);
}
@Override
......@@ -398,11 +480,28 @@ public void beginTableHeadCell(Map<String, String> parameters)
++this.cellCol;
super.beginTableHeadCell(parameters);
this.eventStack.push(Event.TABLE_HEAD_CELL);
}
/**
* Removes an event from the stack if it matches the passed event.
*
* @param event The event to remove.
* @since 14.0RC1
*/
private void removeEventFromStack(Event event)
{
if (this.eventStack.peek() == event) {
this.eventStack.pop();
}
}
@Override
public void endDefinitionDescription()
{
removeEventFromStack(Event.DEFINITION_DESCRIPTION);
super.endDefinitionDescription();
--this.inlineDepth;
......@@ -417,6 +516,8 @@ public void endDefinitionDescription()
@Override
public void endDefinitionList(Map<String, String> parameters)
{
removeEventFromStack(Event.DEFINITION_LIST);
super.endDefinitionList(parameters);
this.definitionListDepth.pop();
......@@ -427,6 +528,8 @@ public void endDefinitionList(Map<String, String> parameters)
@Override
public void endDefinitionTerm()
{
removeEventFromStack(Event.DEFINITION_TERM);
super.endDefinitionTerm();
--this.inlineDepth;
......@@ -441,14 +544,31 @@ public void endDefinitionTerm()
@Override
public void endDocument(MetaData metadata)
{
removeEventFromStack(Event.DOCUMENT);
super.endDocument(metadata);
this.previousEvent = Event.DOCUMENT;
}
/**
* {@inheritDoc}
*
* @since 14.0RC1
*/
@Override
public void beginFormat(Format format, Map<String, String> parameters)
{
super.beginFormat(format, parameters);
this.eventStack.push(Event.FORMAT);
}
@Override
public void endFormat(Format format, Map<String, String> parameters)
{
removeEventFromStack(Event.FORMAT);
super.endFormat(format, parameters);
this.previousEvent = Event.FORMAT;
......@@ -462,6 +582,8 @@ public void endFormat(Format format, Map<String, String> parameters)
@Override
public void endLink(ResourceReference reference, boolean freestanding, Map<String, String> parameters)
{
removeEventFromStack(Event.LINK);
super.endLink(reference, freestanding, parameters);
--this.linkDepth;
......@@ -471,6 +593,8 @@ public void endLink(ResourceReference reference, boolean freestanding, Map<Strin
@Override
public void endList(ListType type, Map<String, String> parameters)
{
removeEventFromStack(Event.LIST);
super.endList(type, parameters);
this.listDepth.pop();
......@@ -481,6 +605,8 @@ public void endList(ListType type, Map<String, String> parameters)
@Override
public void endListItem()
{
removeEventFromStack(Event.LIST_ITEM);
super.endListItem();
--this.inlineDepth;
......@@ -490,6 +616,8 @@ public void endListItem()
@Override
public void endListItem(Map<String, String> parameters)
{
removeEventFromStack(Event.LIST_ITEM);
super.endListItem(parameters);
--this.inlineDepth;
......@@ -499,6 +627,8 @@ public void endListItem(Map<String, String> parameters)
@Override
public void endMacroMarker(String name, Map<String, String> parameters, String content, boolean isInline)
{
removeEventFromStack(Event.MACRO_MARKER);
super.endMacroMarker(name, parameters, content, isInline);
this.previousEvent = Event.MACRO_MARKER;
......@@ -513,6 +643,8 @@ public void endMacroMarker(String name, Map<String, String> parameters, String c
@Override
public void endMetaData(MetaData metadata)
{
removeEventFromStack(Event.META_DATA);
super.endMetaData(metadata);
this.previousEvent = Event.META_DATA;
......@@ -526,6 +658,8 @@ public void endMetaData(MetaData metadata)
@Override
public void endGroup(Map<String, String> parameters)
{
removeEventFromStack(Event.GROUP);
super.endGroup(parameters);
this.previousEvent = Event.GROUP;
......@@ -534,6 +668,8 @@ public void endGroup(Map<String, String> parameters)
@Override
public void endParagraph(Map<String, String> parameters)
{
removeEventFromStack(Event.PARAGRAPH);
super.endParagraph(parameters);
this.isInParagraph = false;
......@@ -544,6 +680,8 @@ public void endParagraph(Map<String, String> parameters)
@Override
public void endQuotation(Map<String, String> parameters)
{
removeEventFromStack(Event.QUOTATION);
super.endQuotation(parameters);
--this.quotationDepth;
......@@ -556,6 +694,8 @@ public void endQuotation(Map<String, String> parameters)
@Override
public void endQuotationLine()
{
removeEventFromStack(Event.QUOTATION_LINE);
super.endQuotationLine();
--this.quotationLineDepth;
......@@ -563,9 +703,24 @@ public void endQuotationLine()
this.previousEvent = Event.QUOTATION_LINE;
}
/**
* {@inheritDoc}
*
* @since 14.0RC1
*/
@Override
public void beginSection(Map<String, String> parameters)
{
super.beginSection(parameters);
this.eventStack.push(Event.SECTION);
}
@Override
public void endSection(Map<String, String> parameters)
{
removeEventFromStack(Event.SECTION);
super.endSection(parameters);
this.previousEvent = Event.SECTION;
......@@ -574,6 +729,8 @@ public void endSection(Map<String, String> parameters)
@Override
public void endHeader(HeaderLevel level, String id, Map<String, String> parameters)
{
removeEventFromStack(Event.HEADER);
super.endHeader(level, id, parameters);
this.isInHeader = false;
......@@ -584,6 +741,8 @@ public void endHeader(HeaderLevel level, String id, Map<String, String> paramete
@Override
public void endTable(Map<String, String> parameters)
{
removeEventFromStack(Event.TABLE);
super.endTable(parameters);
this.isInTable = false;
......@@ -594,6 +753,8 @@ public void endTable(Map<String, String> parameters)
@Override
public void endTableCell(Map<String, String> parameters)
{
removeEventFromStack(Event.TABLE_CELL);
super.endTableCell(parameters);
this.isInTableCell = false;
......@@ -604,6 +765,8 @@ public void endTableCell(Map<String, String> parameters)
@Override
public void endTableHeadCell(Map<String, String> parameters)
{
removeEventFromStack(Event.TABLE_HEAD_CELL);
super.endTableHeadCell(parameters);
this.isInTableCell = false;
......@@ -614,15 +777,32 @@ public void endTableHeadCell(Map<String, String> parameters)
@Override
public void endTableRow(Map<String, String> parameters)
{
removeEventFromStack(Event.TABLE_ROW);
super.endTableRow(parameters);
this.previousEvent = Event.TABLE_ROW;
this.cellCol = -1;
}
/**
* {@inheritDoc}
*
* @since 14.0RC1
*/
@Override
public void beginFigure(Map<String, String> parameters)
{
super.beginFigure(parameters);
this.eventStack.push(Event.FIGURE);
}
@Override
public void endFigure(Map<String, String> parameters)
{
removeEventFromStack(Event.FIGURE);
super.endFigure(parameters);
this.previousEvent = Event.FIGURE;
......@@ -634,11 +814,15 @@ public void beginFigureCaption(Map<String, String> parameters)
++this.inlineDepth;
super.beginFigureCaption(parameters);
this.eventStack.push(Event.FIGURE_CAPTION);
}
@Override
public void endFigureCaption(Map<String, String> parameters)
{
removeEventFromStack(Event.FIGURE_CAPTION);
super.endFigureCaption(parameters);
--this.inlineDepth;
......
......@@ -27,8 +27,10 @@
import org.apache.commons.text.CaseUtils;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.TestFactory;
import org.junit.jupiter.api.Named;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.stubbing.Stubber;
import org.xwiki.rendering.listener.Format;
import org.xwiki.rendering.listener.HeaderLevel;
......@@ -37,7 +39,10 @@
import org.xwiki.rendering.listener.MetaData;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.fail;
import static org.junit.jupiter.params.provider.Arguments.arguments;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
......@@ -48,7 +53,7 @@
* @version $Id$
* @since 14.0RC1
*/
public class BlockStateChainingListenerTest
class BlockStateChainingListenerTest
{
private BlockStateChainingListener listener;
......@@ -65,107 +70,129 @@ void setUpChain()
}
/**
* Tests for all "begin/end"-methods if the previous event is correctly set, but only after the end event has been
* forwarded in the chain.
* @return A stream of {@link Arguments} consisting each all begin-methods of the {@link Listener} interface,
* each with the matching end method and suitable parameters.
*/
@TestFactory
Stream<DynamicTest> beginEndMethods()
static Stream<Arguments> beginEndMethodProvider()
{
return Arrays.stream(Listener.class.getMethods())
.filter(m -> m.getName().startsWith("begin"))
.map(beginMethod ->
DynamicTest.dynamicTest(getTestName(beginMethod),
() -> testBeginEndMethod(beginMethod)));
return Arrays.stream(Listener.class.getMethods()).filter(m -> m.getName().startsWith("begin")).map(m -> {
String endMethodName = m.getName().replace("begin", "end");
Method endMethod = null;
try {
endMethod = Listener.class.getMethod(endMethodName, m.getParameterTypes());
} catch (NoSuchMethodException e) {
fail("Expected end method " + endMethodName + " for " + m.getName() + " not found: " + e.getMessage());
}
return arguments(
Named.of(getTestName(m), m),
Named.of(getTestName(endMethod), endMethod),
getMockParameters(m)
);
});
}
/**
* Tests for all "on..." methods if the previous event is correctly set, but only after the event has been forwarded
* in the chain.
* Test all begin/end-methods.
*
* Tests for all "begin/end"-methods if they do not modify the parent event (for the next in the chain) in the
* begin-method, correctly set it afterwards and if the previous event is correctly set, but only after the end
* event has been forwarded in the chain.
*
* @param beginMethod The method to begin the container.
* @param endMethod The corresponding end method.
* @param parameters Suitable parameters for both methods.
*/
@TestFactory
Stream<DynamicTest> onMethods()
{
return Arrays.stream(Listener.class.getMethods())
.filter(m -> m.getName().startsWith("on"))
.map(beginMethod ->
DynamicTest.dynamicTest(getTestName(beginMethod),
() -> testOnMethod(beginMethod)));
}
private String getTestName(Method method)
{
return method.getName() + "(" + Arrays.stream(method.getParameterTypes()).map(Class::getName)
.collect(Collectors.joining(", ")) + ")";
}
private void testBeginEndMethod(Method beginMethod)
@ParameterizedTest(name = "{0} and {1} with {2}")
@MethodSource("beginEndMethodProvider")
void testBeginEndMethod(Method beginMethod, Method endMethod, Object[] parameters)
throws InvocationTargetException, IllegalAccessException
{
String endMethodName = beginMethod.getName().replace("begin", "end");
Class<?>[] parameterClasses = beginMethod.getParameterTypes();
boolean isListItem = beginMethod.getName().equals("beginListItem");
boolean isDefinitionItem = beginMethod.getName().equals("beginDefinitionTerm") || beginMethod.getName()
.equals("beginDefinitionDescription");
BlockStateChainingListener.Event expectedParentEvent;
if (isListItem) {
this.listener.beginList(ListType.NUMBERED, Listener.EMPTY_PARAMETERS);
expectedParentEvent = BlockStateChainingListener.Event.LIST;
} else if (isDefinitionItem) {
this.listener.