diff --git a/action/faces-config.NavData b/action/faces-config.NavData deleted file mode 100644 index 298bfc50a8..0000000000 --- a/action/faces-config.NavData +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/impl/pom.xml b/impl/pom.xml index b53ab5b804..783242d6cf 100644 --- a/impl/pom.xml +++ b/impl/pom.xml @@ -1,7 +1,7 @@ + + + + + + Configuration of an administered object. + + + + + + + + + Description of this administered object. + + + + + + + + + The name element specifies the JNDI name of the + administered object being defined. + + + + + + + + + The administered object's interface type. + + + + + + + + + The administered object's class name. + + + + + + + + + Resource adapter name. + + + + + + + + + Property of the administered object property. This may be a + vendor-specific property. + + + + + + + + + + + + + + + + Configuration of a Connector Connection Factory resource. + + + + + + + + + Description of this resource. + + + + + + + + + The name element specifies the JNDI name of the + resource being defined. + + + + + + + + + The fully qualified class name of the connection factory + interface. + + + + + + + + + Resource adapter name. + + + + + + + + + Maximum number of connections that should be concurrently + allocated for a connection pool. + + + + + + + + + Minimum number of connections that should be concurrently + allocated for a connection pool. + + + + + + + + + The level of transaction support the connection factory + needs to support. + + + + + + + + + Resource property. This may be a vendor-specific + property. + + + + + + + + + + + + + + + + Configuration of a DataSource. + + + + + + + + + Description of this DataSource. + + + + + + + + + The name element specifies the JNDI name of the + data source being defined. + + + + + + + + + DataSource, XADataSource or ConnectionPoolDataSource + implementation class. + + + + + + + + + Database server name. + + + + + + + + + Port number where a server is listening for requests. + + + + + + + + + Name of a database on a server. + + + + + + + + url property is specified + along with other standard DataSource properties + such as serverName, databaseName + and portNumber, the more specific properties will + take precedence and url will be ignored. + + ]]> + + + + + + + + User name to use for connection authentication. + + + + + + + + + Password to use for connection authentication. + + + + + + + + + JDBC DataSource property. This may be a vendor-specific + property or a less commonly used DataSource property. + + + + + + + + + Sets the maximum time in seconds that this data source + will wait while attempting to connect to a database. + + + + + + + + + Set to false if connections should not participate in + transactions. + + + + + + + + + Isolation level for connections. + + + + + + + + + Number of connections that should be created when a + connection pool is initialized. + + + + + + + + + Maximum number of connections that should be concurrently + allocated for a connection pool. + + + + + + + + + Minimum number of connections that should be concurrently + allocated for a connection pool. + + + + + + + + + The number of seconds that a physical connection should + remain unused in the pool before the connection is + closed for a connection pool. + + + + + + + + + The total number of statements that a connection pool + should keep open. + + + + + + + + + + + + + + + + The description type is used by a description element to + provide text describing the parent element. The elements + that use this type should include any information that the + Deployment Component's Deployment File file producer wants + to provide to the consumer of the Deployment Component's + Deployment File (i.e., to the Deployer). Typically, the + tools used by such a Deployment File consumer will display + the description when processing the parent element that + contains the description. + + The lang attribute defines the language that the + description is provided in. The default value is "en" (English). + + + + + + + + + + + + + + + This type defines a dewey decimal that is used + to describe versions of documents. + + + + + + + + + + + + + + + + Employee Self Service + + + The value of the xml:lang attribute is "en" (English) by default. + + ]]> + + + + + + + + + + + + + + + + EmployeeRecord + + ../products/product.jar#ProductEJB + + ]]> + + + + + + + + + + + + + + + The ejb-local-refType is used by ejb-local-ref elements for + the declaration of a reference to an enterprise bean's local + home or to the local business interface of a 3.0 bean. + The declaration consists of: + + - an optional description + - the enterprise bean's reference name used in the code of the + Deployment Component that's referencing the enterprise bean. + - the optional expected type of the referenced enterprise bean + - the optional expected local interface of the referenced + enterprise bean or the local business interface of the + referenced enterprise bean. + - the optional expected local home interface of the referenced + enterprise bean. Not applicable if this ejb-local-ref refers + to the local business interface of a 3.0 bean. + - optional ejb-link information, used to specify the + referenced enterprise bean + - optional elements to define injection of the named enterprise + bean into a component field or property. + + + + + + + + + + + + + + + + + + + + + + ejb/Payroll + + ]]> + + + + + + + + + + + + + + + The ejb-refType is used by ejb-ref elements for the + declaration of a reference to an enterprise bean's home or + to the remote business interface of a 3.0 bean. + The declaration consists of: + + - an optional description + - the enterprise bean's reference name used in the code of + the Deployment Component that's referencing the enterprise + bean. + - the optional expected type of the referenced enterprise bean + - the optional remote interface of the referenced enterprise bean + or the remote business interface of the referenced enterprise + bean + - the optional expected home interface of the referenced + enterprise bean. Not applicable if this ejb-ref + refers to the remote business interface of a 3.0 bean. + - optional ejb-link information, used to specify the + referenced enterprise bean + - optional elements to define injection of the named enterprise + bean into a component field or property + + + + + + + + + + + + + + + + + + + + + + + The ejb-ref-typeType contains the expected type of the + referenced enterprise bean. + + The ejb-ref-type designates a value + that must be one of the following: + + Entity + Session + + + + + + + + + + + + + + + + + + + This type is used to designate an empty + element when used. + + + + + + + + + + + + + + The env-entryType is used to declare an application's + environment entry. The declaration consists of an optional + description, the name of the environment entry, a type + (optional if the value is injected, otherwise required), and + an optional value. + + It also includes optional elements to define injection of + the named resource into fields or JavaBeans properties. + + If a value is not specified and injection is requested, + no injection will occur and no entry of the specified name + will be created. This allows an initial value to be + specified in the source code without being incorrectly + changed when no override has been specified. + + If a value is not specified and no injection is requested, + a value must be supplied during deployment. + + This type is used by env-entry elements. + + + + + + + + + minAmount + + ]]> + + + + + + + java.lang.Integer + + ]]> + + + + + + + 100.00 + + ]]> + + + + + + + + + + + + + + + java.lang.Boolean + java.lang.Class + com.example.Color + + ]]> + + + + + + + + + + + + + + + The elements that use this type designate the name of a + Java class or interface. The name is in the form of a + "binary name", as defined in the JLS. This is the form + of name used in Class.forName(). Tools that need the + canonical name (the name used in source code) will need + to convert this binary name to the canonical name. + + + + + + + + + + + + + + + + This type defines four different values which can designate + boolean values. This includes values yes and no which are + not designated by xsd:boolean + + + + + + + + + + + + + + + + + + + + + The icon type contains small-icon and large-icon elements + that specify the file names for small and large GIF, JPEG, + or PNG icon images used to represent the parent element in a + GUI tool. + + The xml:lang attribute defines the language that the + icon file names are provided in. Its value is "en" (English) + by default. + + + + + + + + employee-service-icon16x16.jpg + + ]]> + + + + + + + employee-service-icon32x32.jpg + + ]]> + + + + + + + + + + + + + + + + An injection target specifies a class and a name within + that class into which a resource should be injected. + + The injection target class specifies the fully qualified + class name that is the target of the injection. The + Jakarta EE specifications describe which classes can be an + injection target. + + The injection target name specifies the target within + the specified class. The target is first looked for as a + JavaBeans property name. If not found, the target is + looked for as a field name. + + The specified resource will be injected into the target + during initialization of the class by either calling the + set method for the target property or by setting a value + into the named field. + + + + + + + + + + + + + + The following transaction isolation levels are allowed + (see documentation for the java.sql.Connection interface): + TRANSACTION_READ_UNCOMMITTED + TRANSACTION_READ_COMMITTED + TRANSACTION_REPEATABLE_READ + TRANSACTION_SERIALIZABLE + + + + + + + + + + + + + + + + + + + The java-identifierType defines a Java identifier. + The users of this type should further verify that + the content does not contain Java reserved keywords. + + + + + + + + + + + + + + + + + + This is a generic type that designates a Java primitive + type or a fully qualified name of a Java interface/type, + or an array of such types. + + + + + + + + + + + + + + + + + : + + Example: + + jdbc:mysql://localhost:3307/testdb + + ]]> + + + + + + + + + + + + + + + + + Configuration of a Messaging Connection Factory. + + + + + + + + + Description of this Messaging Connection Factory. + + + + + + + + + The name element specifies the JNDI name of the + messaging connection factory being defined. + + + + + + + + + Fully-qualified name of the messaging connection factory + interface. Permitted values are jakarta.jms.ConnectionFactory, + jakarta.jms.QueueConnectionFactory, or + jakarta.jms.TopicConnectionFactory. If not specified, + jakarta.jms.ConnectionFactory will be used. + + + + + + + + + Fully-qualified name of the messaging connection factory + implementation class. Ignored if a resource adapter + is used. + + + + + + + + + Resource adapter name. If not specified, the application + server will define the default behavior, which may or may + not involve the use of a resource adapter. + + + + + + + + + User name to use for connection authentication. + + + + + + + + + Password to use for connection authentication. + + + + + + + + + Client id to use for connection. + + + + + + + + + Messaging Connection Factory property. This may be a vendor-specific + property or a less commonly used ConnectionFactory property. + + + + + + + + + Set to false if connections should not participate in + transactions. + + + + + + + + + Maximum number of connections that should be concurrently + allocated for a connection pool. + + + + + + + + + Minimum number of connections that should be concurrently + allocated for a connection pool. + + + + + + + + + + + + + + + + Configuration of a Messaging Destination. + + + + + + + + + Description of this Messaging Destination. + + + + + + + + + The name element specifies the JNDI name of the + messaging destination being defined. + + + + + + + + + Fully-qualified name of the messaging destination interface. + Permitted values are jakarta.jms.Queue and jakarta.jms.Topic + + + + + + + + + Fully-qualified name of the messaging destination implementation + class. Ignored if a resource adapter is used unless the + resource adapter defines more than one destination implementation + class for the specified interface. + + + + + + + + + Resource adapter name. If not specified, the application + server will define the default behavior, which may or may + not involve the use of a resource adapter. + + + + + + + + + Name of the queue or topic. + + + + + + + + + Messaging Destination property. This may be a vendor-specific + property or a less commonly used Destination property. + + + + + + + + + + + + + + + + The jndi-nameType type designates a JNDI name in the + Deployment Component's environment and is relative to the + java:comp/env context. A JNDI name must be unique within the + Deployment Component. + + + + + + + + + + + + + + + com.aardvark.payroll.PayrollHome + + ]]> + + + + + + + + + + + + + + + The lifecycle-callback type specifies a method on a + class to be called when a lifecycle event occurs. + Note that each class may have only one lifecycle callback + method for any given event and that the method may not + be overloaded. + + If the lifefycle-callback-class element is missing then + the class defining the callback is assumed to be the + component class in scope at the place in the descriptor + in which the callback definition appears. + + + + + + + + + + + + + + + + + The listenerType indicates the deployment properties for a web + application listener bean. + + + + + + + + + + The listener-class element declares a class in the + application must be registered as a web + application listener bean. The value is the fully + qualified classname of the listener class. + + + + + + + + + + + + + + + + The localType defines the fully-qualified name of an + enterprise bean's local interface. + + + + + + + + + + + + + + + + The local-homeType defines the fully-qualified + name of an enterprise bean's local home interface. + + + + + + + + + + + + + + + + Configuration of a Mail Session resource. + + + + + + + + + Description of this Mail Session resource. + + + + + + + + + The name element specifies the JNDI name of the + Mail Session resource being defined. + + + + + + + + + Storage protocol. + + + + + + + + + Service provider store protocol implementation class + + + + + + + + + Transport protocol. + + + + + + + + + Service provider transport protocol implementation class + + + + + + + + + Mail server host name. + + + + + + + + + Mail server user name. + + + + + + + + + Password. + + + + + + + + + Email address to indicate the message sender. + + + + + + + + + Mail server property. This may be a vendor-specific + property. + + + + + + + + + + + + + + + + This type is a general type that can be used to declare + parameter/value lists. + + + + + + + + + + The param-name element contains the name of a + parameter. + + + + + + + + + The param-value element contains the value of a + parameter. + + + + + + + + + + + + + + + + The elements that use this type designate either a relative + path or an absolute path starting with a "/". + + In elements that specify a pathname to a file within the + same Deployment File, relative filenames (i.e., those not + starting with "/") are considered relative to the root of + the Deployment File's namespace. Absolute filenames (i.e., + those starting with "/") also specify names in the root of + the Deployment File's namespace. In general, relative names + are preferred. The exception is .war files where absolute + names are preferred for consistency with the Servlet API. + + + + + + + + + + + + + + + + myPersistenceContext + + + + + myPersistenceContext + + PersistenceUnit1 + + Extended + + + ]]> + + + + + + + + + The persistence-context-ref-name element specifies + the name of a persistence context reference; its + value is the environment entry name used in + Deployment Component code. The name is a JNDI name + relative to the java:comp/env context. + + + + + + + + + The Application Assembler(or BeanProvider) may use the + following syntax to avoid the need to rename persistence + units to have unique names within a Jakarta EE application. + + The Application Assembler specifies the pathname of the + root of the persistence.xml file for the referenced + persistence unit and appends the name of the persistence + unit separated from the pathname by #. The pathname is + relative to the referencing application component jar file. + In this manner, multiple persistence units with the same + persistence unit name may be uniquely identified when the + Application Assembler cannot change persistence unit names. + + + + + + + + + + + Used to specify properties for the container or persistence + provider. Vendor-specific properties may be included in + the set of properties. Properties that are not recognized + by a vendor must be ignored. Entries that make use of the + namespace jakarta.persistence and its subnamespaces must not + be used for vendor-specific properties. The namespace + jakarta.persistence is reserved for use by the specification. + + + + + + + + + + + + + + + + + The persistence-context-synchronizationType specifies + whether a container-managed persistence context is automatically + synchronized with the current transaction. + + The value of the persistence-context-synchronization element + must be one of the following: + Synchronized + Unsynchronized + + + + + + + + + + + + + + + + + + + The persistence-context-typeType specifies the transactional + nature of a persistence context reference. + + The value of the persistence-context-type element must be + one of the following: + Transaction + Extended + + + + + + + + + + + + + + + + + + + Specifies a name/value pair. + + + + + + + + + + + + + + + + + + + + myPersistenceUnit + + + + + myPersistenceUnit + + PersistenceUnit1 + + + + ]]> + + + + + + + + + The persistence-unit-ref-name element specifies + the name of a persistence unit reference; its + value is the environment entry name used in + Deployment Component code. The name is a JNDI name + relative to the java:comp/env context. + + + + + + + + + The Application Assembler(or BeanProvider) may use the + following syntax to avoid the need to rename persistence + units to have unique names within a Jakarta EE application. + + The Application Assembler specifies the pathname of the + root of the persistence.xml file for the referenced + persistence unit and appends the name of the persistence + unit separated from the pathname by #. The pathname is + relative to the referencing application component jar file. + In this manner, multiple persistence units with the same + persistence unit name may be uniquely identified when the + Application Assembler cannot change persistence unit names. + + + + + + + + + + + + + + + + com.wombat.empl.EmployeeService + + ]]> + + + + + + + + + + + + + + + jms/StockQueue + + jakarta.jms.Queue + + + + ]]> + + + + + + + + + The resource-env-ref-name element specifies the name + of a resource environment reference; its value is + the environment entry name used in + the Deployment Component code. The name is a JNDI + name relative to the java:comp/env context and must + be unique within a Deployment Component. + + + + + + + + + The resource-env-ref-type element specifies the type + of a resource environment reference. It is the + fully qualified name of a Java language class or + interface. + + + + + + + + + + + + + + + + + jdbc/EmployeeAppDB + javax.sql.DataSource + Container + Shareable + + + ]]> + + + + + + + + + The res-ref-name element specifies the name of a + resource manager connection factory reference. + The name is a JNDI name relative to the + java:comp/env context. + The name must be unique within a Deployment File. + + + + + + + + + The res-type element specifies the type of the data + source. The type is specified by the fully qualified + Java language class or interface + expected to be implemented by the data source. + + + + + + + + + + + + + + + + + + + The res-authType specifies whether the Deployment Component + code signs on programmatically to the resource manager, or + whether the Container will sign on to the resource manager + on behalf of the Deployment Component. In the latter case, + the Container uses information that is supplied by the + Deployer. + + The value must be one of the two following: + + Application + Container + + + + + + + + + + + + + + + + + + + The res-sharing-scope type specifies whether connections + obtained through the given resource manager connection + factory reference can be shared. The value, if specified, + must be one of the two following: + + Shareable + Unshareable + + The default value is Shareable. + + + + + + + + + + + + + + + + + + + The run-asType specifies the run-as identity to be + used for the execution of a component. It contains an + optional description, and the name of a security role. + + + + + + + + + + + + + + + + + + The role-nameType designates the name of a security role. + + The name must conform to the lexical rules for a token. + + + + + + + + + + + + + + + + + This role includes all employees who are authorized + to access the employee service application. + + employee + + + ]]> + + + + + + + + + + + + + + + + + The security-role-refType contains the declaration of a + security role reference in a component's or a + Deployment Component's code. The declaration consists of an + optional description, the security role name used in the + code, and an optional link to a security role. If the + security role is not specified, the Deployer must choose an + appropriate security role. + + + + + + + + + + The value of the role-name element must be the String used + as the parameter to the + EJBContext.isCallerInRole(String roleName) method or the + HttpServletRequest.isUserInRole(String role) method. + + + + + + + + + The role-link element is a reference to a defined + security role. The role-link element must contain + the name of one of the security roles defined in the + security-role elements. + + + + + + + + + + + + + + + + This type adds an "id" attribute to xsd:QName. + + + + + + + + + + + + + + + + + + This type adds an "id" attribute to xsd:boolean. + + + + + + + + + + + + + + + + + + This type adds an "id" attribute to xsd:NMTOKEN. + + + + + + + + + + + + + + + + + + This type adds an "id" attribute to xsd:anyURI. + + + + + + + + + + + + + + + + + + This type adds an "id" attribute to xsd:integer. + + + + + + + + + + + + + + + + + + This type adds an "id" attribute to xsd:positiveInteger. + + + + + + + + + + + + + + + + + + This type adds an "id" attribute to xsd:nonNegativeInteger. + + + + + + + + + + + + + + + + + + This type adds an "id" attribute to xsd:string. + + + + + + + + + + + + + + + + + + This is a special string datatype that is defined by Jakarta EE as + a base type for defining collapsed strings. When schemas + require trailing/leading space elimination as well as + collapsing the existing whitespace, this base type may be + used. + + + + + + + + + + + + + + + + + + This simple type designates a boolean with only two + permissible values + + - true + - false + + + + + + + + + + + + + + + + + + The url-patternType contains the url pattern of the mapping. + It must follow the rules specified in Section 11.2 of the + Servlet API Specification. This pattern is assumed to be in + URL-decoded form and must not contain CR(#xD) or LF(#xA). + If it contains those characters, the container must inform + the developer with a descriptive error message. + The container must preserve all characters including whitespaces. + + + + + + + + + + + + + + + + CorporateStocks + + + + ]]> + + + + + + + + + The message-destination-name element specifies a + name for a message destination. This name must be + unique among the names of message destinations + within the Deployment File. + + + + + + + + + A product specific name that this message destination + should be mapped to. Each message-destination-ref + element that references this message destination will + define a name in the namespace of the referencing + component or in one of the other predefined namespaces. + Many application servers provide a way to map these + local names to names of resources known to the + application server. This mapped name is often a global + JNDI name, but may be a name of any form. Each of the + local names should be mapped to this same global name. + + Application servers are not required to support any + particular form or type of mapped name, nor the ability + to use mapped names. The mapped name is + product-dependent and often installation-dependent. No + use of a mapped name is portable. + + + + + + + + + The JNDI name to be looked up to resolve the message destination. + + + + + + + + + + + + + + + + jms/StockQueue + + jakarta.jms.Queue + + Consumes + + CorporateStocks + + + + ]]> + + + + + + + + + The message-destination-ref-name element specifies + the name of a message destination reference; its + value is the environment entry name used in + Deployment Component code. + + + + + + + + + + + + + + + + + + + + The message-destination-usageType specifies the use of the + message destination indicated by the reference. The value + indicates whether messages are consumed from the message + destination, produced for the destination, or both. The + Assembler makes use of this information in linking producers + of a destination with its consumers. + + The value of the message-destination-usage element must be + one of the following: + Consumes + Produces + ConsumesProduces + + + + + + + + + + + + + + + + + + + jakarta.jms.Queue + + + ]]> + + + + + + + + + + + + + + + The message-destination-linkType is used to link a message + destination reference or message-driven bean to a message + destination. + + The Assembler sets the value to reflect the flow of messages + between producers and consumers in the application. + + The value must be the message-destination-name of a message + destination in the same Deployment File or in another + Deployment File in the same Jakarta EE application unit. + + Alternatively, the value may be composed of a path name + specifying a Deployment File containing the referenced + message destination with the message-destination-name of the + destination appended and separated from the path name by + "#". The path name is relative to the Deployment File + containing Deployment Component that is referencing the + message destination. This allows multiple message + destinations with the same name to be uniquely identified. + + + + + + + + + + + + + + + + The transaction-supportType specifies the level of + transaction support provided by the resource adapter. It is + used by transaction-support elements. + + The value must be one of the following: + + NoTransaction + LocalTransaction + XATransaction + + + + + + + + + + + + + diff --git a/impl/src/main/resources/com/sun/faces/metadata/taglib/faces.composite.taglib.xml b/impl/src/main/resources/com/sun/faces/metadata/taglib/faces.composite.taglib.xml index d29c06a9a6..d811047a54 100644 --- a/impl/src/main/resources/com/sun/faces/metadata/taglib/faces.composite.taglib.xml +++ b/impl/src/main/resources/com/sun/faces/metadata/taglib/faces.composite.taglib.xml @@ -20,8 +20,8 @@ Jakarta Faces Composite Components Tag Library

diff --git a/impl/src/main/resources/com/sun/faces/metadata/taglib/faces.core.taglib.xml b/impl/src/main/resources/com/sun/faces/metadata/taglib/faces.core.taglib.xml index fc008a3046..2459297ef5 100644 --- a/impl/src/main/resources/com/sun/faces/metadata/taglib/faces.core.taglib.xml +++ b/impl/src/main/resources/com/sun/faces/metadata/taglib/faces.core.taglib.xml @@ -20,8 +20,8 @@ Jakarta Faces Core Tag Library

diff --git a/impl/src/main/resources/com/sun/faces/metadata/taglib/faces.facelets.taglib.xml b/impl/src/main/resources/com/sun/faces/metadata/taglib/faces.facelets.taglib.xml index 5fddeb7406..98363528bf 100644 --- a/impl/src/main/resources/com/sun/faces/metadata/taglib/faces.facelets.taglib.xml +++ b/impl/src/main/resources/com/sun/faces/metadata/taglib/faces.facelets.taglib.xml @@ -20,8 +20,8 @@ Jakarta Faces Facelets Tag Library

diff --git a/impl/src/main/resources/com/sun/faces/metadata/taglib/faces.html.taglib.xml b/impl/src/main/resources/com/sun/faces/metadata/taglib/faces.html.taglib.xml index 8f884f9bf3..3f59ffd374 100644 --- a/impl/src/main/resources/com/sun/faces/metadata/taglib/faces.html.taglib.xml +++ b/impl/src/main/resources/com/sun/faces/metadata/taglib/faces.html.taglib.xml @@ -20,8 +20,8 @@ Jakarta Faces HTML Tag Library

diff --git a/impl/src/main/resources/com/sun/faces/metadata/taglib/faces.passthrough.taglib.xml b/impl/src/main/resources/com/sun/faces/metadata/taglib/faces.passthrough.taglib.xml index ce8b10ee2e..f8906bf893 100644 --- a/impl/src/main/resources/com/sun/faces/metadata/taglib/faces.passthrough.taglib.xml +++ b/impl/src/main/resources/com/sun/faces/metadata/taglib/faces.passthrough.taglib.xml @@ -20,8 +20,8 @@ Jakarta Faces Passthrough Attributes Tag Library

diff --git a/impl/src/main/resources/com/sun/faces/metadata/taglib/faces.taglib.xml b/impl/src/main/resources/com/sun/faces/metadata/taglib/faces.taglib.xml index f36f7849b8..38edfbb7a5 100644 --- a/impl/src/main/resources/com/sun/faces/metadata/taglib/faces.taglib.xml +++ b/impl/src/main/resources/com/sun/faces/metadata/taglib/faces.taglib.xml @@ -20,8 +20,8 @@ Jakarta Faces Passthrough Elements Tag Library

diff --git a/impl/src/main/resources/com/sun/faces/metadata/taglib/tags.core.taglib.xml b/impl/src/main/resources/com/sun/faces/metadata/taglib/tags.core.taglib.xml index d4de234080..a6942cc353 100644 --- a/impl/src/main/resources/com/sun/faces/metadata/taglib/tags.core.taglib.xml +++ b/impl/src/main/resources/com/sun/faces/metadata/taglib/tags.core.taglib.xml @@ -20,8 +20,8 @@ Jakarta Tags Core Tag Library

diff --git a/impl/src/main/resources/com/sun/faces/metadata/taglib/tags.functions.taglib.xml b/impl/src/main/resources/com/sun/faces/metadata/taglib/tags.functions.taglib.xml index e041bc93d5..bba2172b00 100644 --- a/impl/src/main/resources/com/sun/faces/metadata/taglib/tags.functions.taglib.xml +++ b/impl/src/main/resources/com/sun/faces/metadata/taglib/tags.functions.taglib.xml @@ -20,8 +20,8 @@ Jakarta Tags Functions Tag Library

diff --git a/impl/src/main/resources/com/sun/faces/web-facelettaglibrary_4_1.xsd b/impl/src/main/resources/com/sun/faces/web-facelettaglibrary_4_1.xsd new file mode 100644 index 0000000000..8346e03dc0 --- /dev/null +++ b/impl/src/main/resources/com/sun/faces/web-facelettaglibrary_4_1.xsd @@ -0,0 +1,751 @@ + + + + + + + + Copyright (c) 2024 Contributors to Eclipse Foundation. + + This program and the accompanying materials are made available under the + terms of the Eclipse Public License v. 2.0, which is available at + http://www.eclipse.org/legal/epl-2.0. + + This Source Code may also be made available under the following Secondary + Licenses when the conditions for such availability set forth in the + Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + version 2 with the GNU Classpath Exception, which is available at + https://www.gnu.org/software/classpath/license.html. + + SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + + + + + + + The XML Schema for the Tag Libraries in the Jakarta Faces + Standard Facelets View Declaration Language (Facelets VDL) + (Version 4.1).

+ +

Jakarta Faces 4.1 Facelet Tag Libraries that wish to conform to + this schema must declare it in the following manner.

+ + <facelet-taglib xmlns="https://jakarta.ee/xml/ns/jakartaee" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-facelettaglibrary_4_1.xsd" + version="4.1"> + + ... + + </facelet-taglib> + +

The instance documents may indicate the published + version of the schema using xsi:schemaLocation attribute + for jakartaee namespace with the following location:

+ +

https://jakarta.ee/xml/ns/jakartaee/web-facelettaglibrary_4_1.xsd

+ + ]]> +
+
+ + + + + + + + + + + tag-names must be unique within a document. + +

+ ]]> +
+
+ + +
+ + + + + + Behavior IDs must be unique within a document. + +

+ ]]> +
+
+ + +
+ + + + + + Converter IDs must be unique within a document. + +

+ ]]> +
+
+ + +
+ + + + + + Validator IDs must be unique within a document. + +

+ ]]> +
+
+ + +
+
+ + + + + + + + + + The top level XML element in a facelet tag library XML file. + +

+ ]]> +
+
+ + + + + + + + + + + + An advisory short name for usages of tags from this tag library. + +

+ ]]> +
+
+
+ + + + + +
+
+ +
+ + +
+ + + + + + + + + + Extension element for facelet-taglib. It may contain + implementation specific content. + +

+ ]]> +
+
+ + + + +
+ + + + + + + + If the tag library XML + file contains individual tag declarations rather than pointing + to a library-class or a declaring a composite-library name, the + individual tags are enclosed in tag elements.

+ + ]]> +
+
+ + + + + + + + + + + + + + +
+ + + + + + + + + +

The attribute element defines an attribute for the nesting + tag. The attribute element may have several subelements + defining:

+ +
+ +
description

a description of the attribute +

+ +
name

the name of the attribute +

+ +
required

whether the attribute is required or + optional +

+ +
type

the type of the attribute +

+ +
+ +

+ ]]> +
+
+ + + + + + + Defines if the nesting attribute is required or + optional.

+ +

If not present then the default is "false", i.e + the attribute is optional.

+ + ]]> +
+
+
+ + + + + + + Defines the Java type of the attributes + value. If this element is omitted, the + expected type is assumed to be + "java.lang.Object".

+ + ]]> +
+
+
+ + + + + + Defines the method signature for a MethodExpression- + enabled attribute. The syntax of the method-signature + element is as follows (taken from the function-signature + EBNF in web-jsptaglibrary_2_1.xsd):

+ + + +

MethodSignature ::= ReturnType S MethodName S? '(' S? Parameters? S? ')'

+ +

ReturnType ::= Type

+ +

MethodName ::= Identifier

+ +

Parameters ::= Parameter | ( Parameter S? ',' S? Parameters )

+ +

Parameter ::= Type

+ +
+ +

Where:

+ +
    + +
  • Type is a basic type or a fully qualified + Java class name (including package name), as per the 'Type' + production in the Java Language Specification, Second Edition, + Chapter 18.

  • + +
  • Identifier is a Java identifier, as per the + 'Identifier' production in the Java Language Specification, + Second Edition, Chapter 18.

  • + +
+ +

Example:

+ +

java.lang.String nickName( java.lang.String, int )

+ + ]]> +
+
+
+
+
+ +
+ + + + + + + + + + Extension element for tag It may contain + implementation specific content. + +

+ ]]> +
+
+ + + + +
+ + + + + + + + + + If the tag library XML file contains individual function + declarations rather than pointing to a library-class or a + declaring a composite-library name, the individual functions are + enclosed in function elements. + +

+ ]]> +
+
+ + + + + + +
+ + + + + + + + + + Within a tag element, the behavior element encapsulates + information specific to a Faces Behavior. + +

+ ]]> +
+
+ + + + + + +
+ + + + + + + + + + Extension element for behavior. It may contain + implementation specific content. + +

+ ]]> +
+
+ + + + +
+ + + + + + + + Within a tag element, the component + element encapsulates information specific to a Faces UIComponent.

+ +
+ +

As of 3.0 of the specification, this requirement is no longer + present: This element must have exactly one of + <component-type>, <resource-id>, + or <handler-class> among its child elements.

+ +
+ + ]]> +
+
+ + + + + + + + + A valid resource identifier + as specified in the Jakarta Faces Specification Document section 2.6.1.3 "Resource Identifiers". + For example:

+ +

<resource-id>myCC/ccName.xhtml</resource-id>

+ + ]]> +
+
+
+ +
+
+ + + + + + + + + + Extension element for component It may contain + implementation specific content. + +

+ ]]> +
+
+ + + + +
+ + + + + + + + + + Within a tag element, the converter element encapsulates + information specific to a Faces Converter. + +

+ ]]> +
+
+ + + + + + +
+ + + + + + + + + + Extension element for converter It may contain + implementation specific content. + +

+ ]]> +
+
+ + + + +
+ + + + + + + + + + Within a tag element, the validator element encapsulates + information specific to a Faces Validator. + +

+ ]]> +
+
+ + + + + + +
+ + + + + + + + + + Extension element for validator It may contain + implementation specific content. + +

+ ]]> +
+
+ + + + +
+ + + + + + + This type contains the recognized versions of + facelet-taglib supported. + +

+ ]]> +
+
+ + + +
+ + + + + + + + + +

Defines the canonical name of a tag or attribute being + defined.

+ +

The name must conform to the lexical rules for an NCName

+ +

+ ]]> +
+
+ + + + + +
+ +
diff --git a/impl/src/main/resources/com/sun/faces/web-facesconfig_4_1.xsd b/impl/src/main/resources/com/sun/faces/web-facesconfig_4_1.xsd new file mode 100644 index 0000000000..d2e129cf3b --- /dev/null +++ b/impl/src/main/resources/com/sun/faces/web-facesconfig_4_1.xsd @@ -0,0 +1,3447 @@ + + + + + + + + Copyright (c) 2024 Contributors to Eclipse Foundation. + + This program and the accompanying materials are made available under the + terms of the Eclipse Public License v. 2.0, which is available at + http://www.eclipse.org/legal/epl-2.0. + + This Source Code may also be made available under the following Secondary + Licenses when the conditions for such availability set forth in the + Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + version 2 with the GNU Classpath Exception, which is available at + https://www.gnu.org/software/classpath/license.html. + + SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + + + + + + + The XML Schema for the Jakarta Faces Application + Configuration File (Version 4.1).

+ +

All Jakarta Faces configuration files must indicate + the Jakarta Faces schema by indicating the + Jakarta Faces namespace:

+ +

https://jakarta.ee/xml/ns/jakartaee

+ +

and by indicating the version of the schema by + using the version element as shown below:

+ +
<faces-config xmlns="https://jakarta.ee/xml/ns/jakartaee"
+      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+      xsi:schemaLocation="..."
+      version="4.1">
+      ...
+      </faces-config>
+ +

The instance documents may indicate the published + version of the schema using xsi:schemaLocation attribute + for jakartaee namespace with the following location:

+ +

https://jakarta.ee/xml/ns/jakartaee/web-facesconfig_4_1.xsd

+ + ]]> +
+
+ + + + + + + + The "faces-config" element is the root of the configuration + information hierarchy, and contains nested elements for all + of the other configuration settings.

+ + ]]> +
+
+ + + + Behavior IDs must be unique within a document.

+ + ]]> +
+
+ + +
+ + + + Converter IDs must be unique within a document.

+ + ]]> +
+
+ + +
+ + + + 'converter-for-class' element values must be unique + within a document.

+ + ]]> +
+
+ + +
+ + + + Validator IDs must be unique within a document.

+ + ]]> +
+
+ + +
+
+ + + + + + + + The "faces-config" element is the root of the configuration + information hierarchy, and contains nested elements for all + of the other configuration settings.

+ + ]]> +
+
+ + + + + + + + + + + + The "name" element + within the top level "faces-config" + element declares the name of this application + configuration resource. Such names are used + in the document ordering scheme specified in section + 11.3.8 "Ordering of Artifacts" of the Jakarta Faces Specification Document.

+ +

This value is taken to be the + defining document id of any <flow-definition> elements + defined in this Application Configuration Resource file. If this + element is not specified, the runtime must take the empty string + as its value.

+ + ]]> +
+
+
+ + + + + + + + +
+ + + + + The metadata-complete attribute defines whether this + Faces application is complete, or whether + the class files available to this module and packaged with + this application should be examined for annotations + that specify configuration information. + + This attribute is only inspected on the application + configuration resource file located at "WEB-INF/faces-config.xml". + The presence of this attribute on any application configuration + resource other than the one located at "WEB-INF/faces-config.xml", + including any files named using the jakarta.faces.CONFIG_FILES + attribute, must be ignored. + + If metadata-complete is set to "true", the Faces + runtime must ignore any annotations that specify configuration + information, which might be present in the class files + of the application. + + If metadata-complete is not specified or is set to + "false", the Faces runtime must examine the class + files of the application for annotations, as specified by + the specification. + + If "WEB-INF/faces-config.xml" is not present, the + Faces runtime will assume metadata-complete to be "false". + + The value of this attribute will have no impact on + runtime annotations such as @ResourceDependency or + @ListenerFor. + + + + + + +
+ + + + + + + + Extension element for faces-config. It may contain + implementation specific content.

+ + ]]> +
+
+ + + + +
+ + + + + + + + Please see section + 11.3.8 "Ordering of Artifacts" of the Jakarta Faces Specification Document + for the specification of this element.

+ + ]]> +
+
+ + + + + +
+ + + + + + + + This element contains a sequence of "id" elements, each of which + refers to an application configuration resource by the "id" + declared on its faces-config element. This element can also contain + a single "others" element which specifies that this document comes + before or after other documents within the application.

+ + ]]> +
+
+ + + + + +
+ + + + + + + + This element indicates that the ordering sub-element in which + it was placed should take special action regarding the ordering + of this application resource relative to other + application configuration resources. + See section 11.3.8 "Ordering of Artifacts" of the Jakarta Faces Specification Document + for the complete specification.

+ + ]]> +
+
+ +
+ + + + + + + + Only relevant if this is placed within the /WEB-INF/faces-config.xml. + Please see + section 11.3.8 "Ordering of Artifacts" of the Jakarta Faces Specification Document + for the specification for details.

+ + ]]> +
+
+ + + + + +
+ + + + + + + + The "application" element provides a mechanism to define the + various per-application-singleton implementation artifacts for + a particular web application that is utilizing + Jakarta Faces. For nested elements that are not specified, + the Jakarta Faces implementation must provide a suitable + default.

+ + ]]> +
+
+ + + + + The "action-listener" element contains the fully + qualified class name of the concrete + ActionListener implementation class that will be + called during the Invoke Application phase of the + request processing lifecycle.

+ + ]]> +
+
+
+ + + + The "default-render-kit-id" element allows the + application to define a renderkit to be used other + than the standard one.

+ + ]]> +
+
+
+ + + + The base name of a resource bundle representing + the message resources for this application. See + the JavaDocs for the "java.util.ResourceBundle" + class for more information on the syntax of + resource bundle names.

+ + ]]> +
+
+
+ + + + The "navigation-handler" element contains the + fully qualified class name of the concrete + NavigationHandler implementation class that will + be called during the Invoke Application phase + of the request processing lifecycle, if the + default ActionListener (provided by the Jakarta Faces + implementation) is used.

+ + ]]> +
+
+
+ + + + The "view-handler" element contains the fully + qualified class name of the concrete ViewHandler + implementation class that will be called during + the Restore View and Render Response phases of the + request processing lifecycle. The faces + implementation must provide a default + implementation of this class.

+ + ]]> +
+
+
+ + + + The "state-manager" element contains the fully + qualified class name of the concrete StateManager + implementation class that will be called during + the Restore View and Render Response phases of the + request processing lifecycle. The faces + implementation must provide a default + implementation of this class.

+ + ]]> +
+
+
+ + + + The "el-resolver" element contains the fully + qualified class name of the concrete + jakarta.el.ELResolver implementation class + that will be used during the processing of + EL expressions.

+ + ]]> +
+
+
+ + + + The "resource-handler" element contains the + fully qualified class name of the concrete + ResourceHandler implementation class that + will be used during rendering and decoding + of resource requests The standard + constructor based decorator pattern used for + other application singletons will be + honored.

+ + ]]> +
+
+
+ + + + The "resource-library-contracts" element + specifies the mappings between views in the application and resource + library contracts that, if present in the application, must be made + available for use as templates of the specified views. +

+ + ]]> +
+
+
+ + + + The "search-expression-handler" + element contains the fully qualified class name of the + concrete jakarta.faces.component.search.SearchExpressionHandler + implementation class that will be used for processing of a + search expression.

+ + ]]> +
+
+
+ + + + The "search-keyword-resolver" + element contains the fully qualified class name of the + concrete jakarta.faces.component.search.SearchKeywordResolver + implementation class that will be used during the processing + of a search expression keyword.

+ + ]]> +
+
+
+ + + + + +
+ +
+ + + + + + + + The resource-bundle element inside the application element + references a java.util.ResourceBundle instance by name + using the var element. ResourceBundles referenced in this + manner may be returned by a call to + Application.getResourceBundle() passing the current + FacesContext for this request and the value of the var + element below.

+ + ]]> +
+
+ + + + + + The fully qualified class name of the + java.util.ResourceBundle instance.

+ + ]]> +
+
+
+ + + + The name by which this ResourceBundle instance + is retrieved by a call to + Application.getResourceBundle().

+ + ]]> +
+
+
+
+ +
+ + + + + + + + The "resource-library-contracts" element + specifies the mappings between views in the application and resource + library contracts that, if present in the application, must be made + available for use as templates of the specified views. +

+ + ]]> +
+
+ + + + + + Declare a mapping between a collection + of views in the application and the list of contracts (if present in the application) + that may be used as a source for templates and resources for those views.

+ + ]]> +
+
+
+
+ +
+ + + + + + + + The "contract-mapping" element + specifies the mappings between a collection of views in the application and resource + library contracts that, if present in the application, must be made + available for use as templates of the specified views. +

+ + ]]> +
+
+ + + + + + The "url-pattern" element + specifies the collection of views in this application that + are allowed to use the corresponding contracts. +

+ + ]]> +
+
+
+ + + + The "contracts" element + is a comma separated list of resource library contracts that, + if available to the application, may be used by the views + matched by the corresponding "url-pattern" +

+ + ]]> +
+
+
+
+ +
+ + + + + + + + Extension element for application. It may contain + implementation specific content.

+ + ]]> +
+
+ + + + +
+ + + + + + + + The "factory" element provides a mechanism to define the + various Factories that comprise parts of the implementation + of Jakarta Faces. For nested elements that are not + specified, the Jakarta Faces implementation must provide a + suitable default.

+ + ]]> +
+
+ + + + + The "application-factory" element contains the + fully qualified class name of the concrete + ApplicationFactory implementation class that will + be called when + FactoryFinder.getFactory(APPLICATION_FACTORY) is + called.

+ + ]]> +
+
+
+ + + + The "exception-handler-factory" element contains the + fully qualified class name of the concrete + ExceptionHandlerFactory implementation class that will + be called when + FactoryFinder.getFactory(EXCEPTION_HANDLER_FACTORY) + is called.

+ + ]]> +
+
+
+ + + + The "external-context-factory" element contains the + fully qualified class name of the concrete + ExternalContextFactory implementation class that will + be called when + FactoryFinder.getFactory(EXTERNAL_CONTEXT_FACTORY) + is called.

+ + ]]> +
+
+
+ + + + The "faces-context-factory" element contains the + fully qualified class name of the concrete + FacesContextFactory implementation class that will + be called when + FactoryFinder.getFactory(FACES_CONTEXT_FACTORY) + is called.

+ + ]]> +
+
+
+ + + + The "facelet-cache-factory" element contains the + fully qualified class name of the concrete + FaceletCacheFactory implementation class that will + be called when + FactoryFinder.getFactory(FACELET_CACHE_FACTORY) + is called.

+ + ]]> +
+
+
+ + + + The "partial-view-context-factory" element contains the + fully qualified class name of the concrete + PartialViewContextFactory implementation class that will + be called when FactoryFinder.getFactory + (FactoryFinder.PARTIAL_VIEW_CONTEXT_FACTORY) is called.

+ + ]]> +
+
+
+ + + + The "lifecycle-factory" element contains the fully + qualified class name of the concrete LifecycleFactory + implementation class that will be called when + FactoryFinder.getFactory(LIFECYCLE_FACTORY) is called.

+ + ]]> +
+
+
+ + + + The "view-declaration-language-factory" element contains + the fully qualified class name of the concrete + ViewDeclarationLanguageFactory + implementation class that will be called when + FactoryFinder.getFactory(VIEW_DECLARATION_FACTORY) is called.

+ + ]]> +
+
+
+ + + + The "tag-handler-delegate-factory" element contains + the fully qualified class name of the concrete + ViewDeclarationLanguageFactory + implementation class that will be called when + FactoryFinder.getFactory(TAG_HANDLER_DELEGATE_FACTORY) is called.

+ + ]]> +
+
+
+ + + + The "render-kit-factory" element contains the fully + qualified class name of the concrete RenderKitFactory + implementation class that will be called when + FactoryFinder.getFactory(RENDER_KIT_FACTORY) is + called.

+ + ]]> +
+
+
+ + + + The "visit-context-factory" element contains the fully + qualified class name of the concrete VisitContextFactory + implementation class that will be called when + FactoryFinder.getFactory(VISIT_CONTEXT_FACTORY) is + called.

+ + ]]> +
+
+
+ + + + The "flash-factory" element contains the + fully qualified class name of the concrete + FaceletFactory implementation class that will + be called when + FactoryFinder.getFactory(FLASH_FACTORY) is + called.

+ + ]]> +
+
+
+ + + + The "flow-handler-factory" element contains the + fully qualified class name of the concrete + FlowHandlerFactory implementation class that will + be called when + FactoryFinder.getFactory(FLOW_HANDLER_FACTORY) is + called.

+ + ]]> +
+
+
+ + + + The "client-window-factory" element contains the fully + qualified class name of the concrete ClientWindowFactory implementation class that + will be called when FactoryFinder.getFactory(CLIENT_WINDOW_FACTORY) is called.

+ + ]]> +
+
+
+ + + + The + "search-expression-context-factory" element contains the + fully qualified class name of the concrete + SearchExpressionContextFactory implementation class that will + be called when + FactoryFinder.getFactory(SEARCH_EXPRESSION_CONTEXT_FACTORY) + is called.

+ + ]]> +
+
+
+ +
+ +
+ + + + + + + + Extension element for factory. It may contain + implementation specific content.

+ + ]]> +
+
+ + + + +
+ + + + + + + + The "attribute" element represents a named, typed, value + associated with the parent UIComponent via the generic + attributes mechanism.

+ +

Attribute names must be unique within the scope of the parent + (or related) component.

+ + ]]> +
+
+ + + + + + The "attribute-name" element represents the name under + which the corresponding value will be stored, in the + generic attributes of the UIComponent we are related + to.

+ + ]]> +
+
+
+ + + + The "attribute-class" element represents the Java type + of the value associated with this attribute name.

+ + ]]> +
+
+
+ + + +
+ +
+ + + + + + + + Extension element for attribute. It may contain + implementation specific content.

+ + ]]> +
+
+ + + + +
+ + + + + + + + The "component" element represents a concrete UIComponent + implementation class that should be registered under the + specified type identifier, along with its associated + properties and attributes. Component types must be unique + within the entire web application.

+ +

Nested "attribute" elements identify generic attributes that + are recognized by the implementation logic of this component. + Nested "property" elements identify JavaBeans properties of + the component class that may be exposed for manipulation + via tools.

+ + ]]> +
+
+ + + + + + The "component-type" element represents the name under + which the corresponding UIComponent class should be + registered.

+ + ]]> +
+
+
+ + + + The "component-class" element represents the fully + qualified class name of a concrete UIComponent + implementation class.

+ + ]]> +
+
+
+ + + + +
+ +
+ + + + + + + + Extension element for component. It may contain + implementation specific content.

+ + ]]> +
+
+ + + + +
+ + + + + + + + The "default-locale" element declares the default locale + for this application instance.

+ +

+ To facilitate BCP 47 this element first needs to be parsed by the + Locale.forLanguageTag method. If it does not return a Locale with + a language the old specification below needs to take effect. +

+ +

It must be specified as :language:[_:country:[_:variant:]] + without the colons, for example "ja_JP_SJIS". The + separators between the segments may be '-' or '_'.

+ + ]]> +
+
+ + + + + +
+ + + + + + + + The "default-value" contains the value for the property or + attribute in which this element resides. This value differs + from the "suggested-value" in that the property or attribute + must take the value, whereas in "suggested-value" taking the + value is optional.

+ + ]]> +
+
+ + + + + +
+ + + + + EL expressions present within a faces config file + must start with the character sequence of '#{' and + end with '}'.

+ + ]]> +
+
+ + + +
+ + + + + + + + Define the name and other design-time information for a facet + that is associated with a renderer or a component.

+ + ]]> +
+
+ + + + + + The "facet-name" element represents the facet name + under which a UIComponent will be added to its parent. + It must be of type "Identifier".

+ + ]]> +
+
+
+ +
+ +
+ + + + + + + + Extension element for facet. It may contain implementation + specific content.

+ + ]]> +
+
+ + + + +
+ + + + + + + + The + value of from-view-id must contain one of the following + values:

+ +
    + +
  • The exact match for a view identifier that is recognized + by the the ViewHandler implementation being used (such as + "/index.jsp" if you are using the default ViewHandler).

  • + +
  • The exact match of a flow node id + in the current flow, or a flow id of another flow.

  • + +
  • A proper prefix of a view identifier, plus a trailing + "*" character. This pattern indicates that all view + identifiers that match the portion of the pattern up to the + asterisk will match the surrounding rule. When more than one + match exists, the match with the longest pattern is selected. +

  • + +
  • An "*" character, which means that this pattern applies + to all view identifiers.

  • + +
+ + ]]> +
+
+ + + + + +
+ + + + + + + + The "from-action" element contains an action reference + expression that must have been executed (by the default + ActionListener for handling application level events) + in order to select the navigation rule. If not specified, + this rule will be relevant no matter which action reference + was executed (or if no action reference was executed).

+ + ]]> +
+
+ + + + + +
+ + + + + + + + The "if" element defines a condition that must resolve + to true in order for the navigation case on which it is + defined to be matched, with the existing match criteria + (action method and outcome) as a prerequiste, if present. + The condition is defined declaratively using a value + expression in the body of this element. The expression is + evaluated at the time the navigation case is being matched. + If the "from-outcome" is omitted and this element is + present, the navigation handler will match a null outcome + and use the condition return value to determine if the + case should be considered a match.

+ +
+ +

When used in a <switch> within a flow, if the + expresion returns true, the + <from-outcome> sibling element's outcome is used as + the id of the node in the flow graph to which control must be + passed.

+ +
+ + ]]> +
+
+ + + + + +
+ + + + + + + +

+ +
+ +
+ + ]]> +
+
+ + + + + +
+ + + + + + + + The "converter" element represents a concrete Converter + implementation class that should be registered under the + specified converter identifier. Converter identifiers must + be unique within the entire web application.

+ +

Nested "attribute" elements identify generic attributes that + may be configured on the corresponding UIComponent in order + to affect the operation of the Converter. Nested "property" + elements identify JavaBeans properties of the Converter + implementation class that may be configured to affect the + operation of the Converter. "attribute" and "property" + elements are intended to allow component developers to + more completely describe their components to tools and users. + These elements have no required runtime semantics.

+ + ]]> +
+
+ + + + + + + The "converter-id" element represents the + identifier under which the corresponding + Converter class should be registered.

+ + ]]> +
+
+
+ + + + The "converter-for-class" element represents the + fully qualified class name for which a Converter + class will be registered.

+ + ]]> +
+
+
+
+ + + + The "converter-class" element represents the fully + qualified class name of a concrete Converter + implementation class.

+ + ]]> +
+
+
+ + + + Nested "attribute" elements identify generic + attributes that may be configured on the + corresponding UIComponent in order to affect the + operation of the Converter. This attribute is + primarily for design-time tools and is not + specified to have any meaning at runtime.

+ + ]]> +
+
+
+ + + + Nested "property" elements identify JavaBeans + properties of the Converter implementation class + that may be configured to affect the operation of + the Converter. This attribute is primarily for + design-time tools and is not specified to have + any meaning at runtime.

+ + ]]> +
+
+
+ +
+ +
+ + + + + + + + Extension element for converter. It may contain + implementation specific content.

+ + ]]> +
+
+ + + + +
+ + + + + + + + The "lifecycle" element provides a mechanism to specify + modifications to the behaviour of the default Lifecycle + implementation for this web application.

+ + ]]> +
+
+ + + + + The "phase-listener" element contains the fully + qualified class name of the concrete PhaseListener + implementation class that will be registered on + the Lifecycle.

+ + ]]> +
+
+
+ +
+ +
+ + + + + + + + Extension element for lifecycle. It may contain + implementation specific content.

+ + ]]> +
+
+ + + + +
+ + + + + The localeType defines valid locale defined by ISO-639-1 + and ISO-3166.

+ + ]]> +
+
+ + + +
+ + + + + + + + The "locale-config" element allows the app developer to + declare the supported locales for this application.

+ + ]]> +
+
+ + + + + +
+ + + + + + + + The "default-validators" element allows the app developer to + register a set of validators, referenced by identifier, that + are automatically assigned to any EditableValueHolder component + in the application, unless overridden or disabled locally.

+ + ]]> +
+
+ + + + + The "validator-id" element represents the identifier + of a registered validator.

+ + ]]> +
+
+
+
+ +
+ + + + + + + + + Top level element for a flow + definition.

+ +
+ +

If there is no <start-node> element declared, it + is assumed to be <flowName>.xhtml.

+ +
+ + ]]> +
+
+ + + + + + Declare the id of the starting node in the + flow graph. The start node may be any of the node types mentioned in + the class javadocs for FlowHandler.

+ + ]]> +
+
+
+ + + + + + + + + +
+ + + + The id of this flow. The id + must be unique within the Application configuration Resource + file in which this flow is defined. The value of this attribute, + combined with the value of the <faces-config><name> element + must globally identify the flow within the application.

+ + ]]> + + + + + + + + + + + + Invoke a method, passing parameters if necessary. + The return from the method is used as the outcome for where to go next in the + flow. If the method is a void method, the default outcome is used.

+ + ]]> + + + + + + + + + A parameter to pass when calling the method + identified in the "method" element that is a sibling of this element.

+ + ]]> + + + + + + + + + + + + + + + + + + + + + + + + A parameter to pass when calling the method + identified in the "method" element that is a sibling of this element.

+ + ]]> + + + + + + + The optional "class" element within a "parameter" element + will be interpreted as the fully qualified class name for the type + of the "value" element.

+ + ]]> +
+
+ + + + + The "value" element within an "parameter" + must be a literal string or an EL Expression whose "get" will be called when the "method" + associated with this element is invoked.

+ + ]]> +
+
+
+ +
+ + + + + + + + Define a view node in a flow graph.

+ +

This element must contain exactly one + <vdl-document> element.

+ + ]]> +
+
+ + + + + + Define the path to the vdl-document for the enclosing view. +

+ + ]]> + + + + + + + + The id of this view. It must be + unique within the flow.

+ + ]]> +
+
+ +
+ + + + + + + + Define a switch node in a flow graph.

+ +
+ +

This element must contain one or more + <case> elements. When control passes to the + <switch> node, each of the cases must be considered + in order and control must past to the <from-outcome> + of the first one whose <if> expression evaluates to + true.

+ +
+ + ]]> +
+
+ + + + + Defines a case that must be + considered in the list of cases in the + <switch>.

+ + ]]> +
+
+
+ + + + Defines the default case that will + be taken if none of the other cases in the + <switch> are taken.

+ + ]]> +
+
+
+
+ + + + The id of this switch. It must be + unique within the flow.

+ + ]]> +
+
+
+
+ + + + + + + + Defines a case that will + be considered in the <switch>.

+ + ]]> +
+
+ + + + + + If this EL expression evaluates to + true, the corresponding from-outcome will + be the outcome taken by the enclosing <switch>

+ + ]]> +
+
+
+ + + + The "from-outcome" element contains a logical outcome + string returned by the execution of an application + action method selected via an "actionRef" property + (or a literal value specified by an "action" property) + of a UICommand component. If specified, this rule + will be relevant only if the outcome value matches + this element's value. If not specified, this rule + will be relevant if the outcome value is non-null + or, if the "if" element is present, will be relevant + for any outcome value, with the assumption that the + condition specified in the "if" element ultimately + determines if this rule is a match.

+ +

If used in a faces flow, this element + represents the node id to which control will be passed.

+ + ]]> +
+
+
+
+ + +
+ + + + + + + + Define a return node in a flow graph.

+ +
+ +

This element must contain exactly one <from-outcome> element.

+
+ + ]]> +
+
+ + + + + This element + represents the node id to which control will be passed.

+ + ]]> +
+
+
+
+ + + + The id of this flow-return.

+ + ]]> +
+
+
+
+ + + + + + + + Define a call node in a flow graph.

+ +
+ +

This element must contain exactly one <flow-reference> element, + which must contain exactly one <flow-id> element.

+
+ + ]]> +
+
+ + + + + The flow id of the called flow.

+ + ]]> + + + + + + + A parameter to pass when calling the flow + identified in the "flow-reference" element that is a sibling of this element.

+ + ]]> + + + + + + + + The id of this flow-return.

+ + ]]> +
+
+ +
+ + + + + + + + Identifiy the called flow.

+ +
+ +
+ + ]]> +
+
+ + + + + The document id of the called flow.

+ + ]]> +
+
+
+ + + + The id of the called flow.

+ + ]]> +
+
+
+
+
+ + + + + + + + A MethodExpression that will be invoked when the flow is entered.

+ + ]]> + + + + + + + + + + + + + + + + A MethodExpression that will be invoked when the flow is exited.

+ + ]]> + + + + + + + + + + + + + + + + A named parameter whose value will be populated + with a correspondingly named parameter within an "outbound-parameter" element.

+ + ]]> + + + + + + + The "name" element within an "inbound-parameter" + element declares the name of this parameter + to be passed into a flow. There must be + a sibling "value" element in the same parent as this element.

+ + ]]> +
+
+ + + + + The "value" element within an "inbound-parameter" + must be an EL Expression whose value will be set with the correspondingly + named "outbound-parameter" when this flow is entered, if such a + parameter exists.

+ + ]]> +
+
+
+ +
+ + + + + + + + A named parameter whose value will be + passed to a correspondingly named parameter within an "inbound-parameter" element + on the target flow.

+ + ]]> + + + + + + + The "name" element within an "outbound-parameter" element + declares the name of this parameter to be passed out of a flow. There must be + a sibling "value" element in the same parent as this element.

+ + ]]> +
+
+ + + + + The "value" element within an "outbound-parameter" + must be a literal string or an EL Expression whose "get" will be called when the "flow-call" + containing this element is traversed to go to a new flow.

+ + ]]> +
+
+
+ +
+ + + + + + + + + The + "navigation-case" element describes a particular + combination of conditions that must match for this case to + be executed, and the view id of the component tree that + should be selected next.

+ + ]]> +
+
+ + + + + + + The "from-outcome" element contains a logical outcome + string returned by the execution of an application + action method selected via an "actionRef" property + (or a literal value specified by an "action" property) + of a UICommand component. If specified, this rule + will be relevant only if the outcome value matches + this element's value. If not specified, this rule + will be relevant if the outcome value is non-null + or, if the "if" element is present, will be relevant + for any outcome value, with the assumption that the + condition specified in the "if" element ultimately + determines if this rule is a match.

+ + ]]> +
+
+
+ + + + Please see section 7.4.2 "Default NavigationHandler Algorithm" of the Jakarta Faces Specification Document + for the specification of this element.

+ + ]]> +
+
+
+ + + + The "to-view-id" element + contains the view identifier (or + flow node id, or flow id) + of the next view (or flow node or + flow) that should be displayed if this + navigation rule is matched. If the contents is a + value expression, it should be resolved by the + navigation handler to obtain the view ( + or flow node or flow) + identifier.

+ + ]]> +
+
+
+ + + + The document id of the called flow. + If this element appears in a <navigation-case> nested within + a <flow-definition>, it must be ignored because navigation + cases within flows may only navigate among the view nodes of that + flow.

+ + ]]> +
+
+
+ +
+ +
+ + + + + + + + The "navigation-rule" element represents an individual + decision rule that will be utilized by the default + NavigationHandler implementation to make decisions on + what view should be displayed next, based on the + view id being processed.

+ + ]]> +
+
+ + + + + + + +
+ + + + + + + + Extension element for navigation-rule. It may contain + implementation specific content.

+ + ]]> +
+
+ + + + +
+ + + + + + + + The "null-value" element indicates that the managed + property in which we are nested will be explicitly + set to null if our managed bean is automatically + created. This is different from omitting the managed + property element entirely, which will cause no + property setter to be called for this property.

+ +

The "null-value" element can only be used when the + associated "property-class" identifies a Java class, + not a Java primitive.

+ + ]]> +
+
+ +
+ + + + + + + + The "property" element represents a JavaBean property of the + Java class represented by our parent element.

+ +

Property names must be unique within the scope of the Java + class that is represented by the parent element, and must + correspond to property names that will be recognized when + performing introspection against that class via + java.beans.Introspector.

+ + ]]> +
+
+ + + + + + The "property-name" element represents the JavaBeans + property name under which the corresponding value + may be stored.

+ + ]]> +
+
+
+ + + + The "property-class" element represents the Java type + of the value associated with this property name. + If not specified, it can be inferred from existing + classes; however, this element should be specified if + the configuration file is going to be the source for + generating the corresponding classes.

+ + ]]> +
+
+
+ + + +
+ +
+ + + + + + + + Any view that matches any of the + url-patterns in this element may only be reached from another Jakarta Faces + view in the same web application. Because the runtime is aware of + which views are protected, any navigation from an unprotected + view to a protected view is automatically subject to + protection.

+ + ]]> +
+
+ + + +
+ + + + + + + + Extension element for property. It may contain + implementation specific content.

+ + ]]> +
+
+ + + + +
+ + + + + + + + The "redirect" element indicates that navigation to the + specified "to-view-id" should be accomplished by + performing an HTTP redirect rather than the usual + ViewHandler mechanisms.

+ + ]]> +
+
+ + + + + +
+ + + + + + + + This element was introduced due to a specification + error, and is now deprecated. The correct name for + this element is "redirect-param" and its meaning is + documented therein. The "view-param" element is + maintained to preserve backwards compatibility. + Implementations must treat this element the same as + "redirect-param".

+ + ]]> +
+
+ + + + + +
+ + + + + + + + The "redirect-param" element, only valid within + a "redirect" element, contains child "name" + and "value" elements that must be included in the + redirect url when the redirect is performed.

+ + ]]> +
+
+ + + + + +
+ + + + + + + + The "referenced-bean" element represents at design time the + promise that a Java object of the specified type will exist at + runtime in some scope, under the specified key. This can be + used by design time tools to construct user interface dialogs + based on the properties of the specified class. The presence + or absence of a referenced bean element has no impact on the + Jakarta Faces runtime environment inside a web application.

+ + ]]> +
+
+ + + + + + The "referenced-bean-name" element represents the + attribute name under which the corresponding + referenced bean may be assumed to be stored, in one + of 'request', 'session', 'view', 'application' + or a custom scope.

+ + ]]> +
+
+
+ + + + The "referenced-bean-class" element represents the + fully qualified class name of the Java class + (either abstract or concrete) or Java interface + implemented by the corresponding referenced bean.

+ + ]]> +
+
+
+
+ +
+ + + + + + + + The "render-kit" element represents a concrete RenderKit + implementation that should be registered under the specified + render-kit-id. If no render-kit-id is specified, the + identifier of the default RenderKit + (RenderKitFactory.DEFAULT_RENDER_KIT) is assumed.

+ + ]]> +
+
+ + + + + + The "render-kit-id" element represents an identifier + for the RenderKit represented by the parent + "render-kit" element.

+ + ]]> +
+
+
+ + + + The "render-kit-class" element represents the fully + qualified class name of a concrete RenderKit + implementation class.

+ + ]]> +
+
+
+ + + +
+ +
+ + + + + + + + The "client-behavior-renderer" element represents a concrete + ClientBehaviorRenderer implementation class that should be + registered under the specified behavior renderer type identifier, + in the RenderKit associated with the parent "render-kit" + element. Client Behavior renderer type must be unique within the RenderKit + associated with the parent "render-kit" element.

+ +

Nested "attribute" elements identify generic component + attributes that are recognized by this renderer.

+ + ]]> +
+
+ + + + + The "client-behavior-renderer-type" element represents a renderer type + identifier for the Client Behavior Renderer represented by the parent + "client-behavior-renderer" element.

+ + ]]> +
+
+
+ + + + The "client-behavior-renderer-class" element represents the fully + qualified class name of a concrete Client Behavior Renderer + implementation class.

+ + ]]> +
+
+
+
+ +
+ + + + + + + + The "renderer" element represents a concrete Renderer + implementation class that should be registered under the + specified component family and renderer type identifiers, + in the RenderKit associated with the parent "render-kit" + element. Combinations of component family and + renderer type must be unique within the RenderKit + associated with the parent "render-kit" element.

+ +

Nested "attribute" elements identify generic component + attributes that are recognized by this renderer.

+ + ]]> +
+
+ + + + + + The "component-family" element represents the + component family for which the Renderer represented + by the parent "renderer" element will be used.

+ + ]]> +
+
+
+ + + + The "renderer-type" element represents a renderer type + identifier for the Renderer represented by the parent + "renderer" element.

+ + ]]> +
+
+
+ + + + The "renderer-class" element represents the fully + qualified class name of a concrete Renderer + implementation class.

+ + ]]> +
+
+
+ + + +
+ +
+ + + + + + + + Extension element for renderer. It may contain implementation + specific content.

+ + ]]> +
+
+ + + + +
+ + + + + + + + Extension element for render-kit. It may contain + implementation specific content.

+ + ]]> +
+
+ + + + +
+ + + + + + + + The "suggested-value" contains the value for the property or + attribute in which this element resides. This value is + advisory only and is intended for tools to use when + populating pallettes.

+ + ]]> +
+
+ + + +
+ + + + + + + + The "supported-locale" element allows authors to declare + which locales are supported in this application instance.

+ +

+ To facilitate BCP 47 this element first needs to be parsed by the + Locale.forLanguageTag method. If it does not return a Locale with + a language the old specification below needs to take effect. +

+ +

It must be specified as :language:[_:country:[_:variant:]] + without the colons, for example "ja_JP_SJIS". The + separators between the segments may be '-' or '_'.

+ + ]]> +
+
+ + + + + +
+ + + + + + + + The "behavior" element represents a concrete Behavior + implementation class that should be registered under the + specified behavior identifier. Behavior identifiers must + be unique within the entire web application.

+ +

Nested "attribute" elements identify generic attributes that + may be configured on the corresponding UIComponent in order + to affect the operation of the Behavior. Nested "property" + elements identify JavaBeans properties of the Behavior + implementation class that may be configured to affect the + operation of the Behavior. "attribute" and "property" + elements are intended to allow component developers to + more completely describe their components to tools and users. + These elements have no required runtime semantics.

+ + ]]> +
+
+ + + + + + The "behavior-id" element represents the identifier + under which the corresponding Behavior class should + be registered.

+ + ]]> +
+
+
+ + + + The "behavior-class" element represents the fully + qualified class name of a concrete Behavior + implementation class.

+ + ]]> +
+
+
+ + + + Nested "attribute" elements identify generic + attributes that may be configured on the + corresponding UIComponent in order to affect the + operation of the Behavior. This attribute is + primarily for design-time tools and is not + specified to have any meaning at runtime.

+ + ]]> +
+
+
+ + + + Nested "property" elements identify JavaBeans + properties of the Behavior implementation class + that may be configured to affect the operation of + the Behavior. This attribute is primarily for + design-time tools and is not specified to have + any meaning at runtime.

+ + ]]> +
+
+
+ +
+ +
+ + + + + + + + Extension element for behavior. It may contain + implementation specific content.

+ + ]]> +
+
+ + + + +
+ + + + + + + + The "validator" element represents a concrete Validator + implementation class that should be registered under the + specified validator identifier. Validator identifiers must + be unique within the entire web application.

+ +

Nested "attribute" elements identify generic attributes that + may be configured on the corresponding UIComponent in order + to affect the operation of the Validator. Nested "property" + elements identify JavaBeans properties of the Validator + implementation class that may be configured to affect the + operation of the Validator. "attribute" and "property" + elements are intended to allow component developers to + more completely describe their components to tools and users. + These elements have no required runtime semantics.

+ + ]]> +
+
+ + + + + + The "validator-id" element represents the identifier + under which the corresponding Validator class should + be registered.

+ + ]]> +
+
+
+ + + + The "validator-class" element represents the fully + qualified class name of a concrete Validator + implementation class.

+ + ]]> +
+
+
+ + + + Nested "attribute" elements identify generic + attributes that may be configured on the + corresponding UIComponent in order to affect the + operation of the Validator. This attribute is + primarily for design-time tools and is not + specified to have any meaning at runtime.

+ + ]]> +
+
+
+ + + + Nested "property" elements identify JavaBeans + properties of the Validator implementation class + that may be configured to affect the operation of + the Validator. This attribute is primarily for + design-time tools and is not specified to have + any meaning at runtime.

+ + ]]> +
+
+
+ +
+ +
+ + + + + + + + Extension element for validator. It may contain + implementation specific content.

+ + ]]> +
+
+ + + + +
+ + + + + The "value" element is the String representation of + a literal value to which a scalar managed property + will be set, or a value binding expression ("#{...}") + that will be used to calculate the required value. + It will be converted as specified for the actual + property type.

+ + ]]> +
+
+ +
+ + + + + + + + The presence of this element within the "application" element in + an application configuration resource file indicates the + developer wants to add an SystemEventListener to this + application instance. Elements nested within this element allow + selecting the kinds of events that will be delivered to the + listener instance, and allow selecting the kinds of classes that + can be the source of events that are delivered to the listener + instance.

+ + ]]> +
+
+ + + + + The "system-event-listener-class" element contains + the fully qualified class name of the concrete + SystemEventListener implementation class that will be + called when events of the type specified by the + "system-event-class" are sent by the runtime.

+ + ]]> +
+
+
+ + + + The "system-event-class" element contains the fully + qualified class name of the SystemEvent subclass for + which events will be delivered to the class whose fully + qualified class name is given by the + "system-event-listener-class" element.

+ + ]]> +
+
+
+ + + + The "source-class" element, if present, contains the + fully qualified class name of the class that will be the + source for the event to be delivered to the class whose + fully qualified class name is given by the + "system-event-listener-class" element.

+ + ]]> +
+
+
+
+ +
+ + + + + This type contains the recognized versions of + faces-config supported.

+ + ]]> +
+
+ + + +
+ +
diff --git a/impl/src/main/resources/com/sun/faces/web-partialresponse_4_1.xsd b/impl/src/main/resources/com/sun/faces/web-partialresponse_4_1.xsd new file mode 100644 index 0000000000..35064f1383 --- /dev/null +++ b/impl/src/main/resources/com/sun/faces/web-partialresponse_4_1.xsd @@ -0,0 +1,403 @@ + + + + + + + + Copyright (c) 2024 Contributors to Eclipse Foundation. + + This program and the accompanying materials are made available under the + terms of the Eclipse Public License v. 2.0, which is available at + http://www.eclipse.org/legal/epl-2.0. + + This Source Code may also be made available under the following Secondary + Licenses when the conditions for such availability set forth in the + Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + version 2 with the GNU Classpath Exception, which is available at + https://www.gnu.org/software/classpath/license.html. + + SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + + + + + + + + + The XML Schema for the Jakarta Faces (Version 4.1) + Partial Response used in Jakarta Faces Ajax frameworks. + +

+ + ]]> +
+
+ + + + + + + + + + The "partial-response" element is the root of the + partial response information hierarchy, and contains + nested elements for all possible elements that can exist + in the response.

+ +

This element must have an "id" attribute whose value + is the return from calling getContainerClientId() on the + UIViewRoot to which this response pertains. + +

+ + ]]> +
+
+
+ + + + + + + + + + The "partial-response" element is the root of the + partial response information hierarchy, and contains + nested elements for all possible elements that can exist + in the response. + +

+ +

+ + ]]> +
+
+ + + + + + + + + This element must have an "id" attribute whose value + is the return from calling getContainerClientId() on the + UIViewRoot to which this response pertains.

+ + ]]> + + + + + + + + + + + + + + The "changes" element contains a collection of child elements, + each of which describes a different change to be applied to the + view in the user agent. + +

+ + ]]> +
+
+ + + + + + + + + + + The "eval" element enables this element's + contents to be executed as JavaScript. + +

+ + ]]> +
+
+
+ +
+
+ + + + + + + + + + The "update" element enables DOM elements matching the "id" + attribute to be updated with the contents of this element. + +

+ + ]]> +
+
+ + + + +
+ + + + + + + + + + The "insert" element enables content to be inserted into the DOM + before or after an existing DOM element as specified by the + nested "before" or "after" elements. The elements "before" and + "after" are mutually exclusive - one of them must be specified. + +

+ + ]]> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + The "delete" element enables DOM elements matching the "id" + attribute to be removed. + +

+ + ]]> +
+
+ +
+ + + + + + + + + + The "attributes" element enables attributes of DOM elements matching the "id" + attribute to be updated. If this element is used, then it must contain at + least one "attribute" element. + +

+ + ]]> +
+
+ + + + + + + + + + + + +
+ + + + + + + + + + The "redirect" element enables a redirect to the location as specified by the + "url" attribute. + +

+ + ]]> +
+
+ +
+ + + + + + + + + + The "error" element contains error information from the server. + +

+ + ]]> +
+
+ + + + +
+ + + + + + + + + Extension element for partial response. It may contain + implementation specific content. +

+ + ]]> +
+
+ + + + +
+ +
diff --git a/impl/src/test/java/com/sun/faces/application/FacesMessageTest.java b/impl/src/test/java/com/sun/faces/application/FacesMessageTest.java index 8b93675cf6..8ef9baef1d 100644 --- a/impl/src/test/java/com/sun/faces/application/FacesMessageTest.java +++ b/impl/src/test/java/com/sun/faces/application/FacesMessageTest.java @@ -16,7 +16,7 @@ package com.sun.faces.application; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -24,7 +24,7 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; -import org.junit.Test; +import org.junit.jupiter.api.Test; import jakarta.faces.application.FacesMessage; diff --git a/impl/src/test/java/com/sun/faces/application/resource/ResourceCacheTest.java b/impl/src/test/java/com/sun/faces/application/resource/ResourceCacheTest.java index c8223cb0ce..140a97740d 100644 --- a/impl/src/test/java/com/sun/faces/application/resource/ResourceCacheTest.java +++ b/impl/src/test/java/com/sun/faces/application/resource/ResourceCacheTest.java @@ -16,14 +16,13 @@ package com.sun.faces.application.resource; -import org.junit.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; +import org.junit.jupiter.api.Test; public class ResourceCacheTest { @@ -36,7 +35,7 @@ public void noMemoryLeakWithContracts() { // now we clear the contracts list, which was used to create the cache entry. contracts.clear(); ResourceInfo cachedResourceInfo = cache.get("bar.gif", null, null, Arrays.asList("foo", "baz")); - assertThat(cachedResourceInfo, is(resourceInfo)); + assertEquals(resourceInfo, cachedResourceInfo); } } diff --git a/impl/src/test/java/com/sun/faces/config/processor/FacesConfigNamespaceContextTest.java b/impl/src/test/java/com/sun/faces/config/processor/FacesConfigNamespaceContextTest.java index cb59988b72..b9f320d35f 100644 --- a/impl/src/test/java/com/sun/faces/config/processor/FacesConfigNamespaceContextTest.java +++ b/impl/src/test/java/com/sun/faces/config/processor/FacesConfigNamespaceContextTest.java @@ -17,8 +17,8 @@ package com.sun.faces.config.processor; import static com.sun.faces.util.Util.createLocalDocumentBuilderFactory; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -28,7 +28,7 @@ import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; diff --git a/impl/src/test/java/com/sun/faces/context/ExternalContextImplTest.java b/impl/src/test/java/com/sun/faces/context/ExternalContextImplTest.java index d83b72cd4b..e58f288399 100644 --- a/impl/src/test/java/com/sun/faces/context/ExternalContextImplTest.java +++ b/impl/src/test/java/com/sun/faces/context/ExternalContextImplTest.java @@ -16,12 +16,10 @@ package com.sun.faces.context; -import static org.easymock.EasyMock.expect; -import static org.easymock.EasyMock.replay; -import static org.easymock.EasyMock.verify; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; +import static org.mockito.Mockito.when; import java.util.HashMap; import java.util.Iterator; @@ -30,8 +28,8 @@ import java.util.function.Consumer; import java.util.function.Supplier; -import org.junit.Test; -import org.powermock.api.easymock.PowerMock; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; import jakarta.servlet.ServletContext; import jakarta.servlet.http.Cookie; @@ -48,14 +46,12 @@ public class ExternalContextImplTest { */ @Test public void testGetRequestCookieMap() { - ServletContext servletContext = PowerMock.createNiceMock(ServletContext.class); - HttpServletRequest request = PowerMock.createNiceMock(HttpServletRequest.class); - HttpServletResponse response = PowerMock.createNiceMock(HttpServletResponse.class); - replay(servletContext, request, response); - + ServletContext servletContext = Mockito.mock(ServletContext.class); + HttpServletRequest request = Mockito.mock(HttpServletRequest.class); + HttpServletResponse response = Mockito.mock(HttpServletResponse.class); + ExternalContextImpl externalContext = new ExternalContextImpl(servletContext, request, response); - verify(servletContext, request, response); - + assertNotNull(externalContext.getRequestCookieMap()); } @@ -64,19 +60,17 @@ public void testGetRequestCookieMap() { */ @Test public void testGetRequestCookieMap2() { - ServletContext servletContext = PowerMock.createNiceMock(ServletContext.class); - HttpServletRequest request = PowerMock.createNiceMock(HttpServletRequest.class); - HttpServletResponse response = PowerMock.createNiceMock(HttpServletResponse.class); + ServletContext servletContext = Mockito.mock(ServletContext.class); + HttpServletRequest request = Mockito.mock(HttpServletRequest.class); + HttpServletResponse response = Mockito.mock(HttpServletResponse.class); Cookie cookie = new Cookie("foo", "bar"); - expect(request.getCookies()).andReturn(new Cookie[]{cookie}).anyTimes(); - replay(servletContext, request, response); - + when(request.getCookies()).thenReturn(new Cookie[]{cookie}); + ExternalContextImpl externalContext = new ExternalContextImpl(servletContext, request, response); - verify(servletContext, request, response); Map requestCookieMap = externalContext.getRequestCookieMap(); assertTrue(requestCookieMap.get("foo") instanceof Cookie); Cookie value = (Cookie) requestCookieMap.get("foo"); - + assertTrue(value.getValue().equals("bar")); assertTrue(requestCookieMap.containsKey("foo")); assertTrue(requestCookieMap.containsValue(requestCookieMap.get("foo"))); @@ -93,15 +87,13 @@ public void testGetRequestCookieMap2() { */ @Test public void testGetRequestCookieMap3() { - ServletContext servletContext = PowerMock.createNiceMock(ServletContext.class); - HttpServletRequest request = PowerMock.createNiceMock(HttpServletRequest.class); - HttpServletResponse response = PowerMock.createNiceMock(HttpServletResponse.class); + ServletContext servletContext = Mockito.mock(ServletContext.class); + HttpServletRequest request = Mockito.mock(HttpServletRequest.class); + HttpServletResponse response = Mockito.mock(HttpServletResponse.class); Cookie cookie = new Cookie("foo", "bar"); - expect(request.getCookies()).andReturn(new Cookie[]{cookie}).anyTimes(); - replay(servletContext, request, response); - + when(request.getCookies()).thenReturn(new Cookie[]{cookie}); + ExternalContextImpl externalContext = new ExternalContextImpl(servletContext, request, response); - verify(servletContext, request, response); Map requestCookieMap = externalContext.getRequestCookieMap(); Iterator> entryIterator = requestCookieMap.entrySet().iterator(); @@ -153,15 +145,13 @@ public void testGetRequestCookieMap3() { @SuppressWarnings("unchecked") @Test public void testGetRequestCookieMap4() { - ServletContext servletContext = PowerMock.createNiceMock(ServletContext.class); - HttpServletRequest request = PowerMock.createNiceMock(HttpServletRequest.class); - HttpServletResponse response = PowerMock.createNiceMock(HttpServletResponse.class); + ServletContext servletContext = Mockito.mock(ServletContext.class); + HttpServletRequest request = Mockito.mock(HttpServletRequest.class); + HttpServletResponse response = Mockito.mock(HttpServletResponse.class); Cookie cookie = new Cookie("foo", "bar"); - expect(request.getCookies()).andReturn(new Cookie[]{cookie}).anyTimes(); - replay(servletContext, request, response); - + when(request.getCookies()).thenReturn(new Cookie[]{cookie}); + ExternalContextImpl externalContext = new ExternalContextImpl(servletContext, request, response); - verify(servletContext, request, response); Map requestCookieMap = externalContext.getRequestCookieMap(); boolean exceptionThrown = false; try { @@ -169,7 +159,7 @@ public void testGetRequestCookieMap4() { } catch (UnsupportedOperationException e) { exceptionThrown = true; } - + assertTrue(exceptionThrown); verifySupplier(() -> requestCookieMap.put("foot", "bar")); verifyConsumer(m -> requestCookieMap.putAll((Map) m), new HashMap<>()); @@ -184,13 +174,13 @@ public void testGetRequestCookieMap4() { */ private void verifyConsumer(Consumer consumer, Object argument) { boolean exceptionThrown = false; - + try { consumer.accept(argument); } catch (UnsupportedOperationException e) { exceptionThrown = true; } - + assertTrue(exceptionThrown); } @@ -201,13 +191,13 @@ private void verifyConsumer(Consumer consumer, Object argument) { */ private void verifySupplier(Supplier supplier) { boolean exceptionThrown = false; - + try { supplier.get(); } catch (UnsupportedOperationException e) { exceptionThrown = true; } - + assertTrue(exceptionThrown); } } diff --git a/impl/src/test/java/com/sun/faces/el/CompositeComponentAttributesELResolverTest.java b/impl/src/test/java/com/sun/faces/el/CompositeComponentAttributesELResolverTest.java index 32e1339b3c..4d0799b397 100644 --- a/impl/src/test/java/com/sun/faces/el/CompositeComponentAttributesELResolverTest.java +++ b/impl/src/test/java/com/sun/faces/el/CompositeComponentAttributesELResolverTest.java @@ -16,19 +16,17 @@ package com.sun.faces.el; -import static org.easymock.EasyMock.expect; -import static org.easymock.EasyMock.replay; -import static org.easymock.EasyMock.verify; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.when; import java.beans.BeanDescriptor; import java.lang.reflect.Field; import java.util.HashMap; import java.util.Map; -import org.easymock.EasyMock; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; import com.sun.faces.facelets.tag.composite.CompositeComponentBeanInfo; @@ -50,12 +48,12 @@ public class CompositeComponentAttributesELResolverTest { */ @Test public void testGetValue() throws Exception { - ELContext elContext1 = EasyMock.createNiceMock(ELContext.class); - FacesContext facesContext1 = EasyMock.createNiceMock(FacesContext.class); - ELContext elContext2 = EasyMock.createNiceMock(ELContext.class); - FacesContext facesContext2 = EasyMock.createNiceMock(FacesContext.class); + ELContext elContext1 = Mockito.mock(ELContext.class); + FacesContext facesContext1 = Mockito.mock(FacesContext.class); + ELContext elContext2 = Mockito.mock(ELContext.class); + FacesContext facesContext2 = Mockito.mock(FacesContext.class); - HashMap ctxAttributes1 = new HashMap<>(); + HashMap ctxAttributes1 = new HashMap(); UIPanel composite = new UIPanel(); CompositeComponentBeanInfo compositeBeanInfo = new CompositeComponentBeanInfo(); BeanDescriptor beanDescriptor = new BeanDescriptor(composite.getClass()); @@ -64,17 +62,14 @@ public void testGetValue() throws Exception { composite.getAttributes().put(UIComponent.BEANINFO_KEY, compositeBeanInfo); String property = "attrs"; - expect(elContext1.getContext(FacesContext.class)).andReturn(facesContext1); - expect(facesContext1.getAttributes()).andReturn(ctxAttributes1); - expect(elContext2.getContext(FacesContext.class)).andReturn(facesContext2); - expect(facesContext2.getAttributes()).andReturn(ctxAttributes1); - replay(elContext1, facesContext1, elContext2, facesContext2); + when(elContext1.getContext(FacesContext.class)).thenReturn(facesContext1); + when(facesContext1.getAttributes()).thenReturn(ctxAttributes1); + when(elContext2.getContext(FacesContext.class)).thenReturn(facesContext2); + when(facesContext2.getAttributes()).thenReturn(ctxAttributes1); CompositeComponentAttributesELResolver elResolver = new CompositeComponentAttributesELResolver(); - @SuppressWarnings("unchecked") Map evalMap1 = (Map) elResolver.getValue(elContext1, composite, property); assertNotNull(evalMap1); - @SuppressWarnings("unchecked") Map evalMap2 = (Map) elResolver.getValue(elContext2, composite, property); assertNotNull(evalMap2); @@ -88,7 +83,5 @@ public void testGetValue() throws Exception { assertTrue(facesContext2 == ctxField1.get(evalMap1)); assertTrue(facesContext1 != ctxField2.get(evalMap2)); assertTrue(facesContext2 == ctxField2.get(evalMap2)); - - verify(elContext1, facesContext1, elContext2, facesContext2); } } diff --git a/impl/src/test/java/com/sun/faces/el/ELUtilsTest.java b/impl/src/test/java/com/sun/faces/el/ELUtilsTest.java index 89019b5261..96993245f0 100644 --- a/impl/src/test/java/com/sun/faces/el/ELUtilsTest.java +++ b/impl/src/test/java/com/sun/faces/el/ELUtilsTest.java @@ -2,8 +2,8 @@ import java.net.URL; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import com.sun.el.ExpressionFactoryImpl; import com.sun.faces.RIConstants; @@ -25,7 +25,7 @@ public class ELUtilsTest { private ApplicationAssociate applicationAssociate; - @Before + @BeforeEach public void setUp() { CDI.setCDIProvider(new MockCDIProvider()); diff --git a/impl/src/test/java/com/sun/faces/facelets/component/UIRepeatTest.java b/impl/src/test/java/com/sun/faces/facelets/component/UIRepeatTest.java index d6c44d2952..564c220d54 100644 --- a/impl/src/test/java/com/sun/faces/facelets/component/UIRepeatTest.java +++ b/impl/src/test/java/com/sun/faces/facelets/component/UIRepeatTest.java @@ -16,22 +16,21 @@ package com.sun.faces.facelets.component; -import static org.easymock.EasyMock.expect; -import static org.easymock.EasyMock.replay; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.when; import java.lang.reflect.Method; -import junit.framework.TestCase; - -import org.easymock.EasyMock; -import org.easymock.IAnswer; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; import jakarta.faces.application.FacesMessage; import jakarta.faces.application.FacesMessage.Severity; import jakarta.faces.context.FacesContext; -public class UIRepeatTest extends TestCase { +public class UIRepeatTest { private FacesContext ctx; @@ -41,14 +40,13 @@ public class UIRepeatTest extends TestCase { @Test public void testHasErrorMessages() throws Exception { - ctx = EasyMock.createMock(FacesContext.class); - expect(ctx.getMaximumSeverity()).andAnswer(new IAnswer() { - @Override - public Severity answer() throws Throwable { - return maximumSeverity; - } - }).anyTimes(); - replay(ctx); + ctx = Mockito.mock(FacesContext.class); + when(ctx.getMaximumSeverity()).thenAnswer(new Answer() { + @Override + public Severity answer(InvocationOnMock invocation) throws Throwable { + return maximumSeverity; + } + }); maximumSeverity = FacesMessage.SEVERITY_WARN; assertEquals(false, hasErrorMessages(ctx)); diff --git a/impl/src/test/java/com/sun/faces/facelets/util/ClasspathTestCase.java b/impl/src/test/java/com/sun/faces/facelets/util/ClasspathTestCase.java index b37ead4d50..2f3761b609 100644 --- a/impl/src/test/java/com/sun/faces/facelets/util/ClasspathTestCase.java +++ b/impl/src/test/java/com/sun/faces/facelets/util/ClasspathTestCase.java @@ -16,17 +16,15 @@ package com.sun.faces.facelets.util; +import static org.junit.jupiter.api.Assertions.assertNull; + import java.util.jar.JarFile; -import junit.framework.Test; -import junit.framework.TestCase; -import junit.framework.TestSuite; -public class ClasspathTestCase extends TestCase { +import org.junit.jupiter.api.Test; - public static Test suite() { - return (new TestSuite(ClasspathTestCase.class)); - } +public class ClasspathTestCase { + @Test public void testGetAlternativeJarFile() throws Exception { String input = "rar:/scratch/fleme/fmwhome/AS11gR1SOA/soa/connectors/FileAdapter.rar!fileAdapter.jar!/META-INF/"; diff --git a/impl/src/test/java/com/sun/faces/junit/JUnitFacesTestCaseBase.java b/impl/src/test/java/com/sun/faces/junit/JUnitFacesTestCaseBase.java index 7ddb0cf1f6..ddedc7468a 100644 --- a/impl/src/test/java/com/sun/faces/junit/JUnitFacesTestCaseBase.java +++ b/impl/src/test/java/com/sun/faces/junit/JUnitFacesTestCaseBase.java @@ -20,6 +20,9 @@ import java.util.HashMap; import java.util.Map; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; + import com.sun.faces.mock.MockApplication; import com.sun.faces.mock.MockCDIProvider; import com.sun.faces.mock.MockExternalContext; @@ -36,9 +39,8 @@ import jakarta.faces.application.ApplicationFactory; import jakarta.faces.context.FacesContextFactory; import jakarta.faces.lifecycle.LifecycleFactory; -import junit.framework.TestCase; -public class JUnitFacesTestCaseBase extends TestCase { +public class JUnitFacesTestCaseBase { protected MockApplication application = null; protected MockServletConfig config = null; @@ -50,14 +52,8 @@ public class JUnitFacesTestCaseBase extends TestCase { protected MockLifecycle lifecycle = null; protected MockHttpSession session = null; - public JUnitFacesTestCaseBase(String name) { - super(name); - } - - @Override + @BeforeEach public void setUp() throws Exception { - super.setUp(); - // Set up CDI CDI.setCDIProvider(new MockCDIProvider()); @@ -104,7 +100,7 @@ public void setUp() throws Exception { } - @Override + @AfterEach public void tearDown() throws Exception { FactoryFinder.releaseFactories(); Method reInitializeFactoryManager = FactoryFinder.class.getDeclaredMethod("reInitializeFactoryManager", (Class[]) null); @@ -120,7 +116,5 @@ public void tearDown() throws Exception { response = null; servletContext = null; session = null; - - super.tearDown(); } } diff --git a/impl/src/test/java/com/sun/faces/mock/MockBeanManager.java b/impl/src/test/java/com/sun/faces/mock/MockBeanManager.java index 0b589af45a..998e88e1e0 100644 --- a/impl/src/test/java/com/sun/faces/mock/MockBeanManager.java +++ b/impl/src/test/java/com/sun/faces/mock/MockBeanManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 Contributors to Eclipse Foundation. + * Copyright (c) 2023, 2024 Contributors to Eclipse Foundation. * Copyright (c) 1997, 2021 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the @@ -263,4 +263,14 @@ public Collection getContexts(Class scopeType) { return null; } + @Override + public boolean isMatchingBean(Set beanTypes, Set beanQualifiers, Type requiredType, Set requiredQualifiers) { + return false; + } + + @Override + public boolean isMatchingEvent(Type specifiedType, Set specifiedQualifiers, Type observedEventType, Set observedEventQualifiers) { + return false; + } + } diff --git a/impl/src/test/java/com/sun/faces/mock/MockExternalContextFactory.java b/impl/src/test/java/com/sun/faces/mock/MockExternalContextFactory.java index eb61af8874..662ed31b87 100644 --- a/impl/src/test/java/com/sun/faces/mock/MockExternalContextFactory.java +++ b/impl/src/test/java/com/sun/faces/mock/MockExternalContextFactory.java @@ -16,14 +16,13 @@ package com.sun.faces.mock; -import jakarta.servlet.ServletContext; -import jakarta.servlet.ServletRequest; -import jakarta.servlet.ServletResponse; - import jakarta.faces.FacesException; import jakarta.faces.FactoryFinder; import jakarta.faces.context.ExternalContext; import jakarta.faces.context.ExternalContextFactory; +import jakarta.servlet.ServletContext; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; public class MockExternalContextFactory extends ExternalContextFactory { diff --git a/impl/src/test/java/com/sun/faces/mock/MockFacesContextFactory.java b/impl/src/test/java/com/sun/faces/mock/MockFacesContextFactory.java index 6939db8d0b..59cdc6673e 100644 --- a/impl/src/test/java/com/sun/faces/mock/MockFacesContextFactory.java +++ b/impl/src/test/java/com/sun/faces/mock/MockFacesContextFactory.java @@ -17,10 +17,6 @@ package com.sun.faces.mock; import jakarta.el.ELContext; -import jakarta.servlet.ServletContext; -import jakarta.servlet.ServletRequest; -import jakarta.servlet.ServletResponse; - import jakarta.faces.FacesException; import jakarta.faces.FactoryFinder; import jakarta.faces.application.Application; @@ -29,6 +25,9 @@ import jakarta.faces.context.FacesContext; import jakarta.faces.context.FacesContextFactory; import jakarta.faces.lifecycle.Lifecycle; +import jakarta.servlet.ServletContext; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; public class MockFacesContextFactory extends FacesContextFactory { public MockFacesContextFactory(FacesContextFactory oldImpl) { diff --git a/impl/src/test/java/com/sun/faces/mock/MockLifecycle.java b/impl/src/test/java/com/sun/faces/mock/MockLifecycle.java index a463f65ab7..8977b8c90b 100644 --- a/impl/src/test/java/com/sun/faces/mock/MockLifecycle.java +++ b/impl/src/test/java/com/sun/faces/mock/MockLifecycle.java @@ -16,13 +16,12 @@ package com.sun.faces.mock; -import jakarta.servlet.http.HttpServletResponse; - import jakarta.faces.FacesException; import jakarta.faces.context.ExternalContext; import jakarta.faces.context.FacesContext; import jakarta.faces.event.PhaseListener; import jakarta.faces.lifecycle.Lifecycle; +import jakarta.servlet.http.HttpServletResponse; public class MockLifecycle extends Lifecycle { diff --git a/impl/src/test/java/com/sun/faces/mock/MockLifecycleFactory.java b/impl/src/test/java/com/sun/faces/mock/MockLifecycleFactory.java index e70a4a414e..4405d07d0d 100644 --- a/impl/src/test/java/com/sun/faces/mock/MockLifecycleFactory.java +++ b/impl/src/test/java/com/sun/faces/mock/MockLifecycleFactory.java @@ -16,14 +16,13 @@ package com.sun.faces.mock; +import java.util.ArrayList; import java.util.Iterator; import jakarta.faces.FactoryFinder; import jakarta.faces.lifecycle.Lifecycle; import jakarta.faces.lifecycle.LifecycleFactory; -import java.util.ArrayList; - public class MockLifecycleFactory extends LifecycleFactory { public MockLifecycleFactory(LifecycleFactory oldImpl) { diff --git a/impl/src/test/java/com/sun/faces/mock/MockServlet.java b/impl/src/test/java/com/sun/faces/mock/MockServlet.java index 6fb35a3267..d5264f2abf 100644 --- a/impl/src/test/java/com/sun/faces/mock/MockServlet.java +++ b/impl/src/test/java/com/sun/faces/mock/MockServlet.java @@ -17,6 +17,7 @@ package com.sun.faces.mock; import java.io.IOException; + import jakarta.servlet.Servlet; import jakarta.servlet.ServletConfig; import jakarta.servlet.ServletException; diff --git a/impl/src/test/java/com/sun/faces/renderkit/html_basic/BodyRendererTest.java b/impl/src/test/java/com/sun/faces/renderkit/html_basic/BodyRendererTest.java index 170cc90120..5d88c84add 100644 --- a/impl/src/test/java/com/sun/faces/renderkit/html_basic/BodyRendererTest.java +++ b/impl/src/test/java/com/sun/faces/renderkit/html_basic/BodyRendererTest.java @@ -16,16 +16,13 @@ package com.sun.faces.renderkit.html_basic; -import static org.easymock.EasyMock.expect; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.when; import java.io.StringWriter; import java.util.Collections; -import org.junit.Test; -import org.powermock.api.easymock.PowerMock; - import jakarta.faces.application.Application; import jakarta.faces.application.ProjectStage; import jakarta.faces.component.Doctype; @@ -34,6 +31,9 @@ import jakarta.faces.context.FacesContext; import jakarta.faces.context.ResponseWriter; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + /** * The JUnit tests for the BodyRenderer class. */ @@ -57,23 +57,21 @@ public void testDecode() { public void testEncodeBegin() throws Exception { StringWriter writer = new StringWriter(); ResponseWriter testResponseWriter = new TestResponseWriter(writer); - FacesContext facesContext = PowerMock.createPartialMock(FacesContext.class, "getResponseWriter"); - UIViewRoot viewRoot = PowerMock.createMock(UIViewRoot.class); - Doctype doctype = PowerMock.createMock(Doctype.class); + FacesContext facesContext = Mockito.mock(FacesContext.class); + UIViewRoot viewRoot = Mockito.mock(UIViewRoot.class); + Doctype doctype = Mockito.mock(Doctype.class); BodyRenderer bodyRenderer = new BodyRenderer(); HtmlBody htmlBody = new HtmlBody(); htmlBody.getAttributes().put("styleClass", "myclass"); - expect(facesContext.getResponseWriter()).andReturn(testResponseWriter).anyTimes(); - expect(facesContext.getViewRoot()).andReturn(viewRoot).anyTimes(); - expect(viewRoot.getDoctype()).andReturn(doctype).anyTimes(); - expect(doctype.getRootElement()).andReturn("html").anyTimes(); - expect(doctype.getPublic()).andReturn(null).anyTimes(); - expect(doctype.getSystem()).andReturn(null).anyTimes(); + when(facesContext.getResponseWriter()).thenReturn(testResponseWriter); + when(facesContext.getViewRoot()).thenReturn(viewRoot); + when(viewRoot.getDoctype()).thenReturn(doctype); + when(doctype.getRootElement()).thenReturn("html"); + when(doctype.getPublic()).thenReturn(null); + when(doctype.getSystem()).thenReturn(null); - PowerMock.replay(facesContext); bodyRenderer.encodeBegin(facesContext, htmlBody); - PowerMock.verify(facesContext); String html = writer.toString(); assertTrue(html.contains("emptyList().iterator()) - .anyTimes(); - expect(facesContext.getResponseWriter()).andReturn(testResponseWriter).anyTimes(); - expect(facesContext.getViewRoot()).andReturn(viewRoot).anyTimes(); - expect(facesContext.isProjectStage(ProjectStage.Development)).andReturn(false).anyTimes(); - expect(viewRoot.getComponentResources(facesContext, "body")).andReturn(Collections.emptyList()).anyTimes(); + when(facesContext.getApplication()).thenReturn(application); + when(facesContext.getClientIdsWithMessages()).thenReturn(Collections.emptyList().iterator()); + when(facesContext.getResponseWriter()).thenReturn(testResponseWriter); + when(facesContext.getViewRoot()).thenReturn(viewRoot); + when(facesContext.isProjectStage(ProjectStage.Development)).thenReturn(false); + when(viewRoot.getComponentResources(facesContext, "body")).thenReturn(Collections.emptyList()); - PowerMock.replay(facesContext, viewRoot, application); bodyRenderer.encodeEnd(facesContext, htmlBody); - PowerMock.verify(facesContext, viewRoot, application); String html = writer.toString(); assertTrue(html.contains("")); } diff --git a/impl/src/test/java/com/sun/faces/renderkit/html_basic/HeadRendererTest.java b/impl/src/test/java/com/sun/faces/renderkit/html_basic/HeadRendererTest.java index d47004c016..ccc36072a5 100644 --- a/impl/src/test/java/com/sun/faces/renderkit/html_basic/HeadRendererTest.java +++ b/impl/src/test/java/com/sun/faces/renderkit/html_basic/HeadRendererTest.java @@ -16,14 +16,14 @@ package com.sun.faces.renderkit.html_basic; -import static org.easymock.EasyMock.expect; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.when; import java.io.StringWriter; import java.util.Collections; -import org.junit.Test; -import org.powermock.api.easymock.PowerMock; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; import jakarta.faces.component.Doctype; import jakarta.faces.component.UIViewRoot; @@ -54,22 +54,20 @@ public void testDecode() { public void testEncodeBegin() throws Exception { StringWriter writer = new StringWriter(); ResponseWriter testResponseWriter = new TestResponseWriter(writer); - FacesContext facesContext = PowerMock.createPartialMock(FacesContext.class, "getResponseWriter"); - UIViewRoot viewRoot = PowerMock.createMock(UIViewRoot.class); - Doctype doctype = PowerMock.createMock(Doctype.class); + FacesContext facesContext = Mockito.mock(FacesContext.class, "getResponseWriter"); + UIViewRoot viewRoot = Mockito.mock(UIViewRoot.class); + Doctype doctype = Mockito.mock(Doctype.class); HeadRenderer headRenderer = new HeadRenderer(); HtmlHead htmlHead = new HtmlHead(); - expect(facesContext.getResponseWriter()).andReturn(testResponseWriter).anyTimes(); - expect(facesContext.getViewRoot()).andReturn(viewRoot).anyTimes(); - expect(viewRoot.getDoctype()).andReturn(doctype).anyTimes(); - expect(doctype.getRootElement()).andReturn("html").anyTimes(); - expect(doctype.getPublic()).andReturn(null).anyTimes(); - expect(doctype.getSystem()).andReturn(null).anyTimes(); + when(facesContext.getResponseWriter()).thenReturn(testResponseWriter); + when(facesContext.getViewRoot()).thenReturn(viewRoot); + when(viewRoot.getDoctype()).thenReturn(doctype); + when(doctype.getRootElement()).thenReturn("html"); + when(doctype.getPublic()).thenReturn(null); + when(doctype.getSystem()).thenReturn(null); - PowerMock.replay(facesContext); headRenderer.encodeBegin(facesContext, htmlHead); - PowerMock.verify(facesContext); String html = writer.toString(); assertTrue(html.contains("")); } diff --git a/impl/src/test/java/com/sun/faces/renderkit/html_basic/HtmlResponseWriterTest.java b/impl/src/test/java/com/sun/faces/renderkit/html_basic/HtmlResponseWriterTest.java index 83d1d22629..a768502b0b 100644 --- a/impl/src/test/java/com/sun/faces/renderkit/html_basic/HtmlResponseWriterTest.java +++ b/impl/src/test/java/com/sun/faces/renderkit/html_basic/HtmlResponseWriterTest.java @@ -16,19 +16,21 @@ package com.sun.faces.renderkit.html_basic; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + import java.io.StringWriter; import java.io.Writer; import java.lang.reflect.Field; import java.lang.reflect.Method; -import org.junit.Test; +import org.junit.jupiter.api.Test; import jakarta.faces.component.UIComponent; import jakarta.faces.component.UIOutput; import jakarta.faces.context.FacesContext; -import static org.junit.Assert.*; - public class HtmlResponseWriterTest { /** diff --git a/impl/src/test/java/com/sun/faces/spi/ConfigurationResourceProviderFactoryTest.java b/impl/src/test/java/com/sun/faces/spi/ConfigurationResourceProviderFactoryTest.java index fba1e9ac76..9f3ec3a0d3 100644 --- a/impl/src/test/java/com/sun/faces/spi/ConfigurationResourceProviderFactoryTest.java +++ b/impl/src/test/java/com/sun/faces/spi/ConfigurationResourceProviderFactoryTest.java @@ -1,13 +1,16 @@ package com.sun.faces.spi; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + import java.io.File; import java.io.FileNotFoundException; import java.io.PrintWriter; +import org.junit.jupiter.api.Test; + import com.sun.faces.spi.ConfigurationResourceProviderFactory.ProviderType; -import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; /** * Tests {@link ConfigurationResourceProviderFactory}. @@ -52,7 +55,7 @@ public void testCreateProvidersSuccessful() throws FileNotFoundException { * @throws FileNotFoundException * Thrown when the test service file can't be written. */ - @Test(expected = IllegalStateException.class) + @Test public void testCreateProvidersWrongService() throws FileNotFoundException { ProviderType facesConfig = ProviderType.FacesConfig; @@ -65,7 +68,7 @@ public void testCreateProvidersWrongService() throws FileNotFoundException { } try { - ConfigurationResourceProviderFactory.createProviders(facesConfig); + assertThrows(IllegalStateException.class, () -> ConfigurationResourceProviderFactory.createProviders(facesConfig)); } finally { servicesFile.delete(); } diff --git a/impl/src/test/java/com/sun/faces/util/ByteArrayGuardAESCTRTest.java b/impl/src/test/java/com/sun/faces/util/ByteArrayGuardAESCTRTest.java index 50ccaf5de7..d4740f76b6 100644 --- a/impl/src/test/java/com/sun/faces/util/ByteArrayGuardAESCTRTest.java +++ b/impl/src/test/java/com/sun/faces/util/ByteArrayGuardAESCTRTest.java @@ -16,16 +16,19 @@ package com.sun.faces.util; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + import java.security.InvalidKeyException; -import jakarta.xml.bind.DatatypeConverter; -import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import org.junit.jupiter.api.Test; + +import jakarta.xml.bind.DatatypeConverter; public class ByteArrayGuardAESCTRTest { - + @Test public void testSmallerSizeBytes() throws Exception { ByteArrayGuardAESCTR guard = new ByteArrayGuardAESCTR(); @@ -34,22 +37,22 @@ public void testSmallerSizeBytes() throws Exception { String value = "1fX_2vX"; String encrypted = guard.encrypt(value); assertTrue(encrypted.length() < 33); - + String decryptedValue = guard.decrypt(encrypted); assertEquals(decryptedValue, value); - + } - - @Test(expected = InvalidKeyException.class) + + @Test public void testDecryptValueWithoutIvBytes() throws InvalidKeyException { ByteArrayGuardAESCTR sut = new ByteArrayGuardAESCTR(); - + String value = "noIV"; byte[] bytes = DatatypeConverter.parseBase64Binary(value); assertTrue(bytes.length < 16); - sut.decrypt(value); + assertThrows(InvalidKeyException.class, () -> sut.decrypt(value)); } } diff --git a/impl/src/test/java/com/sun/faces/util/FacesLoggerTest.java b/impl/src/test/java/com/sun/faces/util/FacesLoggerTest.java index b4d0830c71..badfa24957 100644 --- a/impl/src/test/java/com/sun/faces/util/FacesLoggerTest.java +++ b/impl/src/test/java/com/sun/faces/util/FacesLoggerTest.java @@ -1,14 +1,16 @@ package com.sun.faces.util; +import java.util.Locale; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + import com.sun.faces.mock.MockApplication; import com.sun.faces.mock.MockFacesContext; + import jakarta.el.ELContext; import jakarta.faces.component.UIViewRoot; -import org.easymock.EasyMock; -import org.junit.Assert; -import org.junit.Test; - -import java.util.Locale; public class FacesLoggerTest { @@ -16,14 +18,14 @@ public class FacesLoggerTest { public void unresolvedNavigationUS() { MockFacesContext facesContext = new MockFacesContext(); facesContext.setApplication(new MockApplication()); - facesContext.setELContext(EasyMock.createMock(ELContext.class)); + facesContext.setELContext(Mockito.mock(ELContext.class)); UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null); root.setLocale(Locale.US); facesContext.setViewRoot(root); final Object[] params = {"componentId1", "outcome.xhtml", "viewId.xhtml"}; Locale.setDefault(new Locale("en", "US")); final String result = FacesLogger.RENDERKIT.interpolateMessage(facesContext, "faces.outcometarget.navigation.case.not.resolved", params); - Assert.assertEquals("JSF1090: Navigation case outcome.xhtml not resolved for component componentId1 in viewId viewId.xhtml", result); + Assertions.assertEquals("JSF1090: Navigation case outcome.xhtml not resolved for component componentId1 in viewId viewId.xhtml", result); } @@ -31,13 +33,13 @@ public void unresolvedNavigationUS() { public void unresolvedNavigationGermany() { MockFacesContext facesContext = new MockFacesContext(); facesContext.setApplication(new MockApplication()); - facesContext.setELContext(EasyMock.createMock(ELContext.class)); + facesContext.setELContext(Mockito.mock(ELContext.class)); UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null); root.setLocale(Locale.GERMANY); facesContext.setViewRoot(root); final Object[] params = {"componentId1", "outcome.xhtml", "viewId.xhtml"}; Locale.setDefault(new Locale("de", "DE")); final String result = FacesLogger.RENDERKIT.interpolateMessage(facesContext, "faces.outcometarget.navigation.case.not.resolved", params); - Assert.assertEquals("JSF1090: Navigations-Fall wurde für Komponente componentId1 nicht aufgelöst.", result); + Assertions.assertEquals("JSF1090: Navigations-Fall wurde für Komponente componentId1 nicht aufgelöst.", result); } } diff --git a/impl/src/test/java/com/sun/faces/util/MostlySingletonTest.java b/impl/src/test/java/com/sun/faces/util/MostlySingletonTest.java index 26ead141bf..310ea86ed9 100644 --- a/impl/src/test/java/com/sun/faces/util/MostlySingletonTest.java +++ b/impl/src/test/java/com/sun/faces/util/MostlySingletonTest.java @@ -16,10 +16,12 @@ package com.sun.faces.util; +import static org.junit.jupiter.api.Assertions.assertTrue; + import java.util.HashSet; import java.util.Set; -import org.junit.Test; -import static org.junit.Assert.*; + +import org.junit.jupiter.api.Test; public class MostlySingletonTest { diff --git a/impl/src/test/java/com/sun/faces/util/TestHtmlUtils.java b/impl/src/test/java/com/sun/faces/util/TestHtmlUtils.java index f585413391..55255644ae 100644 --- a/impl/src/test/java/com/sun/faces/util/TestHtmlUtils.java +++ b/impl/src/test/java/com/sun/faces/util/TestHtmlUtils.java @@ -16,17 +16,20 @@ // TestHtmlUtils.java package com.sun.faces.util; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.io.StringWriter; -import junit.framework.TestCase; +import org.junit.jupiter.api.Test; /** * TestHtmlUtils is a class ... */ -public class TestHtmlUtils extends TestCase { +public class TestHtmlUtils { + @Test public void testWriteURL() throws IOException { //Test url with no params testURLEncoding("http://www.google.com", @@ -66,6 +69,7 @@ public void testWriteURL() throws IOException { "/index.jsf?greek=%CE%B5%CE%BB&cyrillic=%D0%BA%D0%B8&hebrew=%D7%99%D7%AA&arabic=%D9%8A%D8%A9%D9%8F%E2%80%8E%E2%80%8E&korean=%ED%95%9C%EA%B8%80"); } + @Test public void testControlCharacters() throws IOException { final char[] controlCharacters = new char[32]; diff --git a/impl/src/test/java/com/sun/faces/util/TestLRUMap_local.java b/impl/src/test/java/com/sun/faces/util/TestLRUMap_local.java index 4a4eaf1425..60e54e28c8 100644 --- a/impl/src/test/java/com/sun/faces/util/TestLRUMap_local.java +++ b/impl/src/test/java/com/sun/faces/util/TestLRUMap_local.java @@ -15,31 +15,24 @@ */ package com.sun.faces.util; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.Arrays; -import java.util.List; import java.util.Collections; +import java.util.List; -import junit.framework.TestCase; +import org.junit.jupiter.api.Test; /** * Validate LRU functionality of LRUMap */ -public class TestLRUMap_local extends TestCase { - - // ------------------------------------------------------------ Constructors - public TestLRUMap_local() { - super("TestLRUMap_local"); - } - - public TestLRUMap_local(String name) { - super(name); - } +public class TestLRUMap_local { // ------------------------------------------------------------ Test Methods /** * Ensure that LRUMap works as advertised. */ + @Test public void testLRUMap() { LRUMap map = new LRUMap(5); diff --git a/impl/src/test/java/com/sun/faces/util/TestUtil_local.java b/impl/src/test/java/com/sun/faces/util/TestUtil_local.java index 8e6987494e..404bad4b68 100644 --- a/impl/src/test/java/com/sun/faces/util/TestUtil_local.java +++ b/impl/src/test/java/com/sun/faces/util/TestUtil_local.java @@ -17,46 +17,23 @@ // TestUtil_local.java package com.sun.faces.util; -import junit.framework.TestCase; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.fail; import java.util.HashMap; +import org.junit.jupiter.api.Test; /** * TestUtil_local.java is a class ... - * + * * Lifetime And Scope *

* */ -public class TestUtil_local extends TestCase { - -// -// Protected Constants -// -// Class Variables -// -// -// Instance Variables -// -// Attribute Instance Variables -// Relationship Instance Variables -// -// Constructors and Initializers -// - public TestUtil_local() { - super("TestUtil_local.java"); - } +public class TestUtil_local { - public TestUtil_local(String name) { - super(name); - } - -// -// Class methods -// -// -// General Methods -// + @Test public void testGetLocaleFromString() { // positive tests assertNotNull(Util.getLocaleFromString("ps")); @@ -90,23 +67,25 @@ public void testGetLocaleFromString() { } catch (Exception exception) { } } - + + @Test public void testSplit() { String[] result = null; - + result = Util.split(new HashMap(), "fooBarKey=Zm9vQmFyVmFsdWU====", "=", 2); assertEquals(2, result.length); assertEquals(result[1], "Zm9vQmFyVmFsdWU===="); - + result = Util.split(new HashMap(), "fooBarKey=Zm9vQmFyVmFsdWU=", "=", 2); assertEquals(2, result.length); assertEquals(result[1], "Zm9vQmFyVmFsdWU="); - + result = Util.split(new HashMap(), "fooBarKey2=Zm9vQmFyVmFsdWUy", "=", 2); assertEquals(2, result.length); assertEquals(result[1], "Zm9vQmFyVmFsdWUy"); } + @Test public void testExtractFirstNumericSegment() { char separatorChar = ':'; @@ -132,5 +111,5 @@ public void testExtractFirstNumericSegment() { assertEquals("there is no numeric segment", e.getMessage()); } } - + } // end of class TestUtil_local diff --git a/impl/src/test/java/com/sun/faces/xml/ParseXMLTestCase.java b/impl/src/test/java/com/sun/faces/xml/ParseXMLTestCase.java index 1e5976421a..93a90d6f83 100644 --- a/impl/src/test/java/com/sun/faces/xml/ParseXMLTestCase.java +++ b/impl/src/test/java/com/sun/faces/xml/ParseXMLTestCase.java @@ -16,6 +16,8 @@ package com.sun.faces.xml; +import static org.junit.jupiter.api.Assertions.fail; + import java.io.File; import java.io.FileInputStream; import java.util.ArrayList; @@ -24,34 +26,19 @@ import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; +import org.junit.jupiter.api.Test; import org.xml.sax.InputSource; -import junit.framework.Test; -import junit.framework.TestCase; -import junit.framework.TestSuite; - -public class ParseXMLTestCase extends TestCase { +public class ParseXMLTestCase { List list = new ArrayList<>(); private final static String xmlDir = "/conf/share"; - public ParseXMLTestCase(String name) { - super(name); - } - - @Override - public void setUp() throws Exception { - super.setUp(); - } - - public static Test suite() { - return (new TestSuite(ParseXMLTestCase.class)); - } - // ------------------------------------------------------------ Test Methods /** * Added for issue 904. */ + @Test public void testParseXML() throws Exception { String curDir = System.getProperty("user.dir"); @@ -81,7 +68,7 @@ public void testParseXML() throws Exception { } // Process all files and directories under dir - public void visitAllDirsAndFiles(File dir) { + private void visitAllDirsAndFiles(File dir) { if (dir.isFile()) { if (isXML(dir)) { @@ -96,7 +83,7 @@ public void visitAllDirsAndFiles(File dir) { } } - public boolean isXML(File file) { + private boolean isXML(File file) { String name = file.getName(); return name.endsWith(".xml"); } diff --git a/impl/src/test/java/com/sun/faces/xml/XHTMLResolvingHandler.java b/impl/src/test/java/com/sun/faces/xml/XHTMLResolvingHandler.java index 766ca44be3..3fde265a21 100644 --- a/impl/src/test/java/com/sun/faces/xml/XHTMLResolvingHandler.java +++ b/impl/src/test/java/com/sun/faces/xml/XHTMLResolvingHandler.java @@ -24,6 +24,7 @@ import java.io.UnsupportedEncodingException; import java.util.Locale; import java.util.ResourceBundle; + import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; diff --git a/impl/src/test/java/jakarta/faces/FacesConfigOrderingTestCase.java b/impl/src/test/java/jakarta/faces/FacesConfigOrderingTestCase.java new file mode 100644 index 0000000000..32a0eab13a --- /dev/null +++ b/impl/src/test/java/jakarta/faces/FacesConfigOrderingTestCase.java @@ -0,0 +1,382 @@ +/* + * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces; + +import static com.sun.faces.util.Util.createLocalDocumentBuilderFactory; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import com.sun.faces.config.manager.documents.DocumentInfo; +import com.sun.faces.config.manager.documents.DocumentOrderingWrapper; + +import jakarta.faces.context.FacesContext; + +public class FacesConfigOrderingTestCase { + + // ---------------------------------------------------- Overall Test Methods + // Set up instance variables required by this test case. + @BeforeEach + public void setUp() throws Exception { + Method method = FacesContext.class.getDeclaredMethod( + "setCurrentInstance", FacesContext.class); + method.setAccessible(true); + method.invoke(null, new Object[] { null }); + + } + + // ------------------------------------------------- Individual Test Methods + /** + *

+ * verify that the overrides specified in the faces-config.xml in the user's + * webapp take precedence. + *

+ * + * @throws java.lang.Exception + */ + @Test + public void testNoOrderingStartWithCab() throws Exception { + DocumentInfo docC = createDocument("C", null, null); + DocumentInfo doca = createDocument("a", null, null); + DocumentInfo docb = createDocument(null, null, null); + + List documents = new ArrayList(); + // J- + Collections.addAll(documents, new DocumentOrderingWrapper(docC), + new DocumentOrderingWrapper(doca), new DocumentOrderingWrapper( + docb)); + // J+ + + DocumentOrderingWrapper[] wrappers = documents + .toArray(new DocumentOrderingWrapper[documents.size()]); + String[] originalOrder = extractNames(wrappers); + DocumentOrderingWrapper.sort(wrappers); + + String[] orderedNames = extractNames(wrappers); + + // a solution: + // ['C', 'a', ''] + List original = Arrays.asList(originalOrder); + List actually = Arrays.asList(orderedNames); + + List possibility1 = Arrays.asList("C", "a", ""); + + boolean assertion = (actually.equals(possibility1)); + String message = "\n original: " + original + "\n expected: " + + possibility1 + "\n actually: " + actually + "\n"; + assertTrue(assertion, message); + System.out.println("testNoOrderingStartWithCab: Passed" + message); + } + + @Test + public void testCafteraStartWithCab() throws Exception { + List docCAfterIds = new ArrayList(); + Collections.addAll(docCAfterIds, "a"); + + DocumentInfo docC = createDocument("C", null, docCAfterIds); + DocumentInfo doca = createDocument("a", null, null); + DocumentInfo docb = createDocument(null, null, null); + + List documents = new ArrayList(); + //J- + Collections.addAll(documents, + new DocumentOrderingWrapper(docC), + new DocumentOrderingWrapper(doca), + new DocumentOrderingWrapper(docb) + ); + //J+ + + DocumentOrderingWrapper[] wrappers = documents.toArray(new DocumentOrderingWrapper[documents.size()]); + String[] originalOrder = extractNames(wrappers); + DocumentOrderingWrapper.sort(wrappers); + + String[] orderedNames = extractNames(wrappers); + + // a solution: + // ['a', '', 'C'] + List original = Arrays.asList(originalOrder); + List actually = Arrays.asList(orderedNames); + + List possibility1 = Arrays.asList("a", "", "C"); + + boolean assertion = (actually.equals(possibility1)); + String message = "\n original: " + original + "\n expected: " + possibility1 + + "\n actually: " + actually + "\n"; + assertTrue(assertion, message); + System.out.println("testCafteraStartWithCab: Passed" + message); + } + + @Test + public void testAafterD_BafterCbeforeOthers_CafterDbeforeB_startWithABCD() throws Exception { + List docAAfterIds = new ArrayList(); + Collections.addAll(docAAfterIds, "D"); + + // C should before B, hence B needs to be after C + List docBAfterIds = new ArrayList(); + Collections.addAll(docBAfterIds, "C"); + + List docBBeforeIds = new ArrayList(); + Collections.addAll(docBBeforeIds, "@others"); + + List docCAfterIds = new ArrayList(); + Collections.addAll(docCAfterIds, "D"); + + List docCBeforeIds = new ArrayList(); + Collections.addAll(docCBeforeIds, "B"); + + DocumentInfo docA = createDocument("A", null, docAAfterIds); + DocumentInfo docB = createDocument("B", docBBeforeIds, docBAfterIds); + DocumentInfo docC = createDocument("C", docCBeforeIds, docCAfterIds); + DocumentInfo docD = createDocument("D", null, null); + + List documents = new ArrayList(); + //J- + Collections.addAll(documents, + new DocumentOrderingWrapper(docA), + new DocumentOrderingWrapper(docB), + new DocumentOrderingWrapper(docC), + new DocumentOrderingWrapper(docD) + ); + //J+ + + DocumentOrderingWrapper[] wrappers = documents.toArray(new DocumentOrderingWrapper[documents.size()]); + String[] originalOrder = extractNames(wrappers); + DocumentOrderingWrapper.sort(wrappers); + + String[] orderedNames = extractNames(wrappers); + + // a solution: + // ['D', 'C', 'B', 'A'] + List original = Arrays.asList(originalOrder); + List actually = Arrays.asList(orderedNames); + + List possibility1 = Arrays.asList("D", "C", "B", "A"); + + boolean assertion = (actually.equals(possibility1) + ); + String message = "\n original: " + original + "\n expected: " + possibility1 + + "\n actually: " + actually + "\n"; + assertTrue(assertion, message); + System.out.println("testAafterD_BafterCbeforeOthers_CafterDbeforeB_startWithABCD: Passed" + message); + } + + @Test + public void testAafterD_BafterCbeforeOthers_CafterDbeforeB_startWithADBC() throws Exception { + + List docAAfterIds = new ArrayList(); + Collections.addAll(docAAfterIds, "D"); + + // C should before B, hence B needs to be after C + List docBAfterIds = new ArrayList(); + Collections.addAll(docBAfterIds, "C"); + + List docBBeforeIds = new ArrayList(); + Collections.addAll(docBBeforeIds, "@others"); + + List docCAfterIds = new ArrayList(); + Collections.addAll(docCAfterIds, "D"); + + List docCBeforeIds = new ArrayList(); + Collections.addAll(docCBeforeIds, "B"); + + DocumentInfo docA = createDocument("A", null, docAAfterIds); + DocumentInfo docB = createDocument("B", docBBeforeIds, docBAfterIds); + DocumentInfo docC = createDocument("C", docCBeforeIds, docCAfterIds); + DocumentInfo docD = createDocument("D", null, null); + + List documents = new ArrayList(); + //J- + Collections.addAll(documents, + new DocumentOrderingWrapper(docA), + new DocumentOrderingWrapper(docD), + new DocumentOrderingWrapper(docB), + new DocumentOrderingWrapper(docC) + ); + //J+ + + DocumentOrderingWrapper[] wrappers = documents.toArray(new DocumentOrderingWrapper[documents.size()]); + String[] originalOrder = extractNames(wrappers); + DocumentOrderingWrapper.sort(wrappers); + + String[] orderedNames = extractNames(wrappers); + + // a solution: + // ['D', 'C', 'B', 'A'] + List original = Arrays.asList(originalOrder); + List actually = Arrays.asList(orderedNames); + + List possibility1 = Arrays.asList("D", "C", "B", "A"); + + boolean assertion = (actually.equals(possibility1) + ); + String message = "\n original: " + original + "\n expected: " + possibility1 + + "\n actually: " + actually + "\n"; + assertTrue(assertion, message); + System.out.println("testAafterD_BafterCbeforeOthers_CafterDbeforeB_startWithADBC: Passed" + message); + + } + + @Test + public void testAafterD_BafterCbeforeOthers_CafterDbeforeB_shuffle() throws Exception { + + List docAAfterIds = new ArrayList(); + Collections.addAll(docAAfterIds, "D"); + + // C should before B, hence B needs to be after C + List docBAfterIds = new ArrayList(); + Collections.addAll(docBAfterIds, "C"); + + List docBBeforeIds = new ArrayList(); + Collections.addAll(docBBeforeIds, "@others"); + + List docCAfterIds = new ArrayList(); + Collections.addAll(docCAfterIds, "D"); + + List docCBeforeIds = new ArrayList(); + Collections.addAll(docCBeforeIds, "B"); + + DocumentInfo docA = createDocument("A", null, docAAfterIds); + DocumentInfo docB = createDocument("B", docBBeforeIds, docBAfterIds); + DocumentInfo docC = createDocument("C", docCBeforeIds, docCAfterIds); + DocumentInfo docD = createDocument("D", null, null); + + List documents = new ArrayList(); + //J- + Collections.addAll(documents, + new DocumentOrderingWrapper(docA), + new DocumentOrderingWrapper(docB), + new DocumentOrderingWrapper(docC), + new DocumentOrderingWrapper(docD) + ); + //J+ + + int number = 100; + for (int i = 0; i < number; i++) { + + Collections.shuffle(documents); + + DocumentOrderingWrapper[] wrappers = documents.toArray(new DocumentOrderingWrapper[documents.size()]); + String[] originalOrder = extractNames(wrappers); + DocumentOrderingWrapper.sort(wrappers); + + String[] orderedNames = extractNames(wrappers); + + // some solutions: + // [D, C, B, A] + // [D, A, C, B] + List original = Arrays.asList(originalOrder); + List actually = Arrays.asList(orderedNames); + + List possibility1 = Arrays.asList("D", "C", "B", "A"); + List possibility2 = Arrays.asList("D", "A", "C", "B"); + + boolean assertion = (actually.equals(possibility1) + || actually.equals(possibility2) + ); + String message = "\n original: " + original + + "\n expected: " + possibility1 + + "\n or: " + possibility2 + + "\n actually: " + actually + "\n"; + assertTrue(assertion, message); + + } + + System.out.println("testAafterD_BafterCbeforeOthers_CafterDbeforeB_shuffle: " + number + " shuffles passed."); + + } + + private DocumentInfo createDocument(String documentId, + List beforeIds, List afterIds) throws Exception { + + String ns = "http://java.sun.com/xml/ns/javaee"; + Document document = newDocument(); + Element root = document.createElementNS(ns, "faces-config"); + if (documentId != null) { + Element nameElement = document.createElementNS(ns, "name"); + nameElement.setTextContent(documentId); + root.appendChild(nameElement); + } + document.appendChild(root); + boolean hasBefore = (beforeIds != null && !beforeIds.isEmpty()); + boolean hasAfter = (afterIds != null && !afterIds.isEmpty()); + boolean createOrdering = (hasBefore || hasAfter); + if (createOrdering) { + Element ordering = document.createElementNS(ns, "ordering"); + root.appendChild(ordering); + if (hasBefore) { + populateIds("before", beforeIds, ns, document, ordering); + } + if (hasAfter) { + populateIds("after", afterIds, ns, document, ordering); + } + } + + return new DocumentInfo(document, null); + + } + + public static String[] extractNames(DocumentOrderingWrapper[] documents) { + String[] extractedNames = new String[documents.length]; + int i = 0; + + for (DocumentOrderingWrapper w : documents) { + extractedNames[i] = w.getDocumentId(); + i++; + } + + return extractedNames; + } + + private void populateIds(String elementName, List ids, String ns, + Document document, Element ordering) { + + Element element = document.createElementNS(ns, elementName); + ordering.appendChild(element); + for (String id : ids) { + Element append; + if ("@others".equals(id)) { + append = document.createElementNS(ns, "others"); + } else { + append = document.createElementNS(ns, "name"); + append.setTextContent(id); + } + element.appendChild(append); + } + + } + + private Document newDocument() throws ParserConfigurationException { + + DocumentBuilderFactory factory = createLocalDocumentBuilderFactory(); + factory.setValidating(false); + factory.setNamespaceAware(true); + return factory.newDocumentBuilder().newDocument(); + + } +} diff --git a/impl/src/test/java/jakarta/faces/FacesWrapperTestCase.java b/impl/src/test/java/jakarta/faces/FacesWrapperTestCase.java new file mode 100644 index 0000000000..f8c6c76daa --- /dev/null +++ b/impl/src/test/java/jakarta/faces/FacesWrapperTestCase.java @@ -0,0 +1,224 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.File; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + *

+ * A unit test to make sure all classes implementing {@link FacesWrapper} are + * actually wrapping all public and protected methods of the wrapped class. This + * should help to keep the wrapper classes in synch with the wrapped classes. + *

+ */ +public class FacesWrapperTestCase { + + private static List> wrapperClasses; + private static List> noWrapperClasses; + private static List methodsToIgnore; + private static final String JAVAX_FACES_PKG = "jakarta.faces."; + + /** + * Perform class-level initialization for test - lookup for classes + * implementing FacesWrapper. + * @throws java.lang.Exception + */ + @BeforeEach + public void setUp() throws Exception { + if (wrapperClasses == null) { + loadWrapperClasses(); + methodsToIgnore = new ArrayList(); + methodsToIgnore.add(Object.class.getMethod("toString", new Class[0])); + } + } + + /** + * Unit test to assert wrapperClasses list was loaded (see {@link #setUp()}. + */ + @Test + public void testWrapperClassesLoaded() { + assertNotNull(wrapperClasses); + assertTrue(!wrapperClasses.isEmpty()); + } + + /** + * Unit test to assert there are no *Wrapper classes not implementing + * FacesWrapper. + */ + @Test + public void testWrapperClassesImplementFacesWrapper() { + assertNotNull(noWrapperClasses); + if (noWrapperClasses.size() > 0) { + System.out.println("Wrapper classes not implementing jakarta.faces.FacesWrapper:"); + System.out.println(noWrapperClasses.toString()); + } + assertTrue(noWrapperClasses + .isEmpty()); + } + + /** + * The main goal of this TestSuite: unit test to assert all classes + * implementing FacesWrapper do wrap all public and protected methods of the + * wrapped class. + */ + @Test + public void testWrapperClassWrapsPublicAndProtectedMethods() { + for (Class wrapper : wrapperClasses) { + if (wrapper.isInterface()) { + continue; + } + List wrapperMethods = getPublicAndProtectedMethods(wrapper); + List methodsToWrap = getPublicAndProtectedMethods(wrapper.getSuperclass()); + + System.out.println("verify " + wrapper.getName() + " is wrapping " + + wrapper.getSuperclass().getName() + " well"); + String msg = wrapper.getCanonicalName() + " does not wrap method: "; + for (Method m : methodsToWrap) { + if (isMethodContained(m, methodsToIgnore)) { + continue; + } + assertTrue(isMethodContained(m, wrapperMethods), msg + m.toString()); + } + } + } + + // private methods + /** + * Returns true it the passed method is contained in the also passed list of + * methods by also comparing matching parameters. + * + * @param m the method (from the wrapped class) to compare against. + * @param wrapperMethods the list of methods of the wrapper class. + */ + private boolean isMethodContained(Method m, List wrapperMethods) { + String name = m.getName(); + Class[] paramTypes = m.getParameterTypes(); + Class returnType = m.getReturnType(); + for (Method wm : wrapperMethods) { + if (name.equals(wm.getName()) && Arrays.equals(paramTypes, wm.getParameterTypes()) + && returnType == wm.getReturnType()) { + return true; + } + } + + return false; + } + + /** + * Collect public and protected methods of a class. + * + * @param wrapper the class to find its methods. + * @return list of found methods. + */ + private List getPublicAndProtectedMethods(Class wrapper) { + List mList = new ArrayList(); + if (Object.class == wrapper) { + return mList; + } + + Method[] methods = wrapper.getDeclaredMethods(); + for (Method m : methods) { + int mod = m.getModifiers(); + if (!Modifier.isStatic(mod) && (Modifier.isPublic(mod) || Modifier.isProtected(mod))) { + mList.add(m); + } + } + return mList; + } + + /** + * Collect the wrapper classes. + */ + private void loadWrapperClasses() { + wrapperClasses = new ArrayList<>(); + noWrapperClasses = new ArrayList<>(); + + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + try { + collectWrapperClasses( + classLoader, + JAVAX_FACES_PKG, + new File(classLoader.getResource("jakarta/faces/Messages.properties").getFile()) + .getParentFile()); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * Walk package tree for collecting wrapper classes. + * + * @param classLoader the ClassLoader. + * @param pkg package name. + * @param jakartaFacesFolder current File (directory or file) + * @throws Exception might throw ClassNotFoundException from class loading. + */ + private void collectWrapperClasses(ClassLoader classLoader, String pkg, File jakartaFacesFolder) throws Exception { + for (File file : jakartaFacesFolder.listFiles()) { + if (file.isDirectory()) { + collectWrapperClasses(classLoader, pkg + file.getName() + ".", file); + } else { + addWrapperClassToWrapperClassesList(classLoader, pkg, file); + } + } + } + + /** + * Add classes that are assignable to FacesWrapper class to the + * wrapperClasses list - and also add classes with a name ending on + * "Wrapper" but being not assignable to FacesWrapper to the + * noWrapperClasses list. + * + * @param cl the ClasslOader used to load the class. + * @param pkg the name of the package working in. + * @param f the File to analyse. + * @throws Exception ClassLoader exceptions. + */ + private void addWrapperClassToWrapperClassesList(ClassLoader cl, String pkg, File f) + throws Exception { + String name = f.getName(); + if (!name.endsWith(".class")) { + return; + } + String className = pkg + name.substring(0, name.length() - 6); + Class c = cl.loadClass(className); + Class wrappedClass = c.getSuperclass(); + if (wrappedClass != null) { + // we are not interested in interfaces extending FacesWrapper interface. + // also skip classes implementing FacesWrapper but extend from Object (e.g. factories). + if (FacesWrapper.class.isAssignableFrom(wrappedClass) || wrappedClass == Object.class) { + return; + } + } + if (FacesWrapper.class.isAssignableFrom(c)) { + wrapperClasses.add(c); + } else if (c != FacesWrapper.class && c.getName().endsWith("Wrapper")) { + noWrapperClasses.add(c); + } + } +} diff --git a/impl/src/test/java/jakarta/faces/FactoryFinderTestCase.java b/impl/src/test/java/jakarta/faces/FactoryFinderTestCase.java new file mode 100644 index 0000000000..0738dfd342 --- /dev/null +++ b/impl/src/test/java/jakarta/faces/FactoryFinderTestCase.java @@ -0,0 +1,284 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces; + +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.File; +import java.io.PrintWriter; +import java.lang.reflect.Method; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.sun.faces.mock.MockHttpServletRequest; +import com.sun.faces.mock.MockHttpServletResponse; +import com.sun.faces.mock.MockServletContext; + +import jakarta.faces.application.Application; +import jakarta.faces.application.ApplicationFactory; +import jakarta.faces.context.FacesContext; +import jakarta.faces.context.FacesContextFactory; +import jakarta.faces.lifecycle.Lifecycle; +import jakarta.faces.lifecycle.LifecycleFactory; + +public class FactoryFinderTestCase { + + public static String FACTORIES[][] = { + {FactoryFinder.APPLICATION_FACTORY, + "com.sun.faces.mock.MockApplicationFactory" + }, + {FactoryFinder.EXTERNAL_CONTEXT_FACTORY, + "com.sun.faces.mock.MockExternalContextFactory" + }, + {FactoryFinder.FACES_CONTEXT_FACTORY, + "com.sun.faces.mock.MockFacesContextFactory" + }, + {FactoryFinder.LIFECYCLE_FACTORY, + "com.sun.faces.mock.MockLifecycleFactory" + }, + {FactoryFinder.RENDER_KIT_FACTORY, + "com.sun.faces.mock.MockRenderKitFactory" + } + }; + + // ---------------------------------------------------- Overall Test Methods + // Set up instance variables required by this test case. + @BeforeEach + public void setUp() throws Exception { + Method method = FacesContext.class.getDeclaredMethod("setCurrentInstance", FacesContext.class); + method.setAccessible(true); + method.invoke(null, new Object[]{null}); + + for (int i = 0, len = FACTORIES.length; i < len; i++) { + System.getProperties().remove(FACTORIES[i][0]); + } + } + + // Tear down instance variables required by ths test case + @AfterEach + public void tearDown() throws Exception { + FactoryFinder.releaseFactories(); + for (int i = 0, len = FACTORIES.length; i < len; i++) { + System.getProperties().remove(FACTORIES[i][0]); + } + } + + // ------------------------------------------------- Individual Test Methods + /** + *

+ * verify that the overrides specified in the faces-config.xml in the user's + * webapp take precedence.

+ * + * @throws java.lang.Exception + */ + @Test + public void testFacesConfigCase() throws Exception { + Object factory = null; + Class clazz = null; + + FactoryFinder.releaseFactories(); + int len, i = 0; + + // simulate the "faces implementation specific" part + for (i = 0, len = FACTORIES.length; i < len; i++) { + FactoryFinder.setFactory(FACTORIES[i][0], + FACTORIES[i][1]); + } + + // simulate the "WEB-INF/services" part + // this is done by the build.xml file + File servicesDir = new File(System.getProperty("basedir"), "target/classes/META-INF/services"); + servicesDir.mkdirs(); + + File servicesFile = new File(servicesDir, "jakarta.faces.context.FacesContextFactory"); + + if (servicesFile.exists()) { + servicesFile.delete(); + } + PrintWriter writer = new PrintWriter(servicesFile); + writer.println("jakarta.faces.mock.MockFacesContextFactoryExtender"); + writer.flush(); + writer.close(); + + File cServicesDir = new File(System.getProperty("basedir"), "target/generated-classes/cobertura/META-INF/services"); + cServicesDir.mkdirs(); + + File cServicesFile = new File(cServicesDir, "jakarta.faces.context.FacesContextFactory"); + + if (cServicesFile.exists()) { + cServicesFile.delete(); + } + PrintWriter cWriter = new PrintWriter(cServicesFile); + cWriter.println("jakarta.faces.mock.MockFacesContextFactoryExtender"); + cWriter.flush(); + cWriter.close(); + + // simulate the "webapp faces-config.xml" part + FactoryFinder.setFactory(FactoryFinder.FACES_CONTEXT_FACTORY, + "jakarta.faces.mock.MockFacesContextFactoryExtender2"); + + for (i = 0, len = FACTORIES.length; i < len; i++) { + clazz = Class.forName(FACTORIES[i][0]); + factory = FactoryFinder.getFactory(FACTORIES[i][0]); + assertTrue( + clazz.isAssignableFrom(factory.getClass()), "Factory for " + clazz.getName() + + " not of expected type."); + clazz = Class.forName(FACTORIES[i][1]); + assertTrue( + clazz.isAssignableFrom(factory.getClass()), "Factory " + FACTORIES[i][1] + " not of expected type"); + + } + // verify that the delegation works + assertTrue(System.getProperty(FACTORIES[2][0]).equals("jakarta.faces.mock.MockFacesContextFactoryExtender2")); + assertTrue(System.getProperty("oldImpl").equals("jakarta.faces.mock.MockFacesContextFactoryExtender")); + + // Verify IllegalStateException when factory not found + FactoryFinder.releaseFactories(); + FactoryFinder.setFactory(FACTORIES[0][0], FACTORIES[0][1]); + FactoryFinder.setFactory(FACTORIES[1][0], FACTORIES[1][1]); + FactoryFinder.setFactory(FACTORIES[2][0], FACTORIES[2][1]); + FactoryFinder.setFactory(FACTORIES[4][0], FACTORIES[4][1]); + boolean exceptionThrown = false; + try { + factory = FactoryFinder.getFactory(FACTORIES[3][0]); + } catch (IllegalStateException ise) { + exceptionThrown = true; + } + assertTrue(exceptionThrown); + + servicesFile.delete(); + cServicesFile.delete(); + } + + // TODO re-enable + @Test + public void testNoFacesContext() throws Exception { +// assertNull(FacesContext.getCurrentInstance()); +// Object result = FactoryFinder.getFactory(FactoryFinder.FACES_CONTEXT_FACTORY); +// assertNotNull(result); +// assertTrue(result instanceof FacesContextFactory); + } + + /** + *

+ * In the absence of webapp faces-config.xml, verify that the overrides + * specified in the META-INF/services take precedence.

+ * + * @throws java.lang.Exception + */ + @Test + public void testServicesCase() throws Exception { + Object factory = null; + Class clazz = null; + + FactoryFinder.releaseFactories(); + int len, i = 0; + + // simulate the "faces implementation specific" part + for (i = 0, len = FACTORIES.length; i < len; i++) { + FactoryFinder.setFactory(FACTORIES[i][0], + FACTORIES[i][1]); + } + + // simulate the "WEB-INF/services" part + // this is done by the build.xml file + File servicesDir = new File(System.getProperty("basedir"), "target/classes/META-INF/services"); + servicesDir.mkdirs(); + + File servicesFile = new File(servicesDir, "jakarta.faces.context.FacesContextFactory"); + + if (servicesFile.exists()) { + servicesFile.delete(); + } + + PrintWriter writer = new PrintWriter(servicesFile); + writer.println("jakarta.faces.mock.MockFacesContextFactoryExtender"); + writer.flush(); + writer.close(); + + File cServicesDir = new File(System.getProperty("basedir"), "target/generated-classes/cobertura/META-INF/services"); + cServicesDir.mkdirs(); + + File cServicesFile = new File(cServicesDir, "jakarta.faces.context.FacesContextFactory"); + + if (cServicesFile.exists()) { + cServicesFile.delete(); + } + + PrintWriter cWriter = new PrintWriter(cServicesFile); + cWriter.println("jakarta.faces.mock.MockFacesContextFactoryExtender"); + cWriter.flush(); + cWriter.close(); + + // this testcase omits the "webapp faces-config.xml" simulation + for (i = 0, len = FACTORIES.length; i < len; i++) { + clazz = Class.forName(FACTORIES[i][0]); + factory = FactoryFinder.getFactory(FACTORIES[i][0]); + assertTrue( + clazz.isAssignableFrom(factory.getClass()), "Factory for " + clazz.getName() + + " not of expected type."); + clazz = Class.forName(FACTORIES[i][1]); + assertTrue( + clazz.isAssignableFrom(factory.getClass()), "Factory " + FACTORIES[i][1] + " not of expected type"); + + } + // verify that the delegation works + assertTrue(System.getProperty(FACTORIES[2][0]).equals("jakarta.faces.mock.MockFacesContextFactoryExtender")); + assertTrue(System.getProperty("oldImpl").equals("com.sun.faces.mock.MockFacesContextFactory")); + + servicesFile.delete(); + cServicesFile.delete(); + } + + @Test + public void testNoFacesContextInitially() throws Exception { + assertNull(FacesContext.getCurrentInstance()); + + FactoryFinder.releaseFactories(); + FactoryFinder.setFactory(FACTORIES[0][0], FACTORIES[0][1]); + FactoryFinder.setFactory(FACTORIES[1][0], FACTORIES[1][1]); + FactoryFinder.setFactory(FACTORIES[2][0], FACTORIES[2][1]); + FactoryFinder.setFactory(FACTORIES[3][0], FACTORIES[3][1]); + FactoryFinder.setFactory(FACTORIES[4][0], FACTORIES[4][1]); + + FacesContextFactory fcFactory = (FacesContextFactory) FactoryFinder.getFactory(FactoryFinder.FACES_CONTEXT_FACTORY); + LifecycleFactory lFactory = (LifecycleFactory) FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY); + Object request = new MockHttpServletRequest(); + Object response = new MockHttpServletResponse(); + Object containerContext = new MockServletContext(); + Lifecycle l = lFactory.getLifecycle(LifecycleFactory.DEFAULT_LIFECYCLE); + @SuppressWarnings("unused") + FacesContext context = fcFactory.getFacesContext(containerContext, request, response, l); + + ApplicationFactory aFactory = (ApplicationFactory) FactoryFinder.getFactory(FactoryFinder.APPLICATION_FACTORY); + @SuppressWarnings("unused") + Application app = aFactory.getApplication(); + FactoryFinder.releaseFactories(); + } + + // ------------------------------------------- helpers + public static void printRelevantSystemProperties() { + System.out.println("++++++Relevant System Properties: "); + for (int i = 0, len = FACTORIES.length; i < len; i++) { + System.out.println(FACTORIES[i][0] + ": " + + System.getProperty(FACTORIES[i][0])); + } + } +} diff --git a/impl/src/test/java/jakarta/faces/FactoryFinderTestCase2.java b/impl/src/test/java/jakarta/faces/FactoryFinderTestCase2.java new file mode 100644 index 0000000000..8b91e1f928 --- /dev/null +++ b/impl/src/test/java/jakarta/faces/FactoryFinderTestCase2.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class FactoryFinderTestCase2 { + + public static String FACTORIES[][] = { + { FactoryFinder.APPLICATION_FACTORY, + "com.sun.faces.mock.MockApplicationFactory" + }, + { FactoryFinder.EXTERNAL_CONTEXT_FACTORY, + "com.sun.faces.mock.MockExternalContextFactory" + }, + { FactoryFinder.FACES_CONTEXT_FACTORY, + "com.sun.faces.mock.MockFacesContextFactory" + }, + { FactoryFinder.LIFECYCLE_FACTORY, + "com.sun.faces.mock.MockLifecycleFactory" + }, + { FactoryFinder.RENDER_KIT_FACTORY, + "com.sun.faces.mock.MockRenderKitFactory" + } + }; + + // ---------------------------------------------------- Overall Test Methods + // Set up instance variables required by this test case. + @BeforeEach + public void setUp() throws Exception { + for (int i = 0, len = FactoryFinderTestCase2.FACTORIES.length; i < len; i++) { + System.getProperties().remove(FactoryFinderTestCase2.FACTORIES[i][0]); + } + } + + // Tear down instance variables required by ths test case + @AfterEach + public void tearDown() throws Exception { + FactoryFinder.releaseFactories(); + for (int i = 0, len = FactoryFinderTestCase2.FACTORIES.length; i < len; i++) { + System.getProperties().remove(FactoryFinderTestCase2.FACTORIES[i][0]); + } + } + + // ------------------------------------------------- Individual Test Methods + /** + *

+ * In the absence of webapp faces-config.xml and META-INF/services, verify + * that the overrides specified in the implementation faces-config.xml take + * precedence.

+ * @throws java.lang.Exception + */ + @Test + public void testJSFImplCase() throws Exception { + Object factory = null; + Class clazz = null; + + FactoryFinder.releaseFactories(); + int len, i = 0; + + // this testcase only simulates the "faces implementation + // specific" part + for (i = 0, len = FactoryFinderTestCase2.FACTORIES.length; i < len; i++) { + FactoryFinder.setFactory(FactoryFinderTestCase2.FACTORIES[i][0], + FactoryFinderTestCase2.FACTORIES[i][1]); + } + + for (i = 0, len = FactoryFinderTestCase2.FACTORIES.length; i < len; i++) { + clazz = Class.forName(FactoryFinderTestCase2.FACTORIES[i][0]); + factory = FactoryFinder.getFactory(FactoryFinderTestCase2.FACTORIES[i][0]); + assertTrue( + clazz.isAssignableFrom(factory.getClass()), "Factory for " + clazz.getName() + + " not of expected type."); + clazz = Class.forName(FactoryFinderTestCase2.FACTORIES[i][1]); + assertTrue( + clazz.isAssignableFrom(factory.getClass()), "Factory " + FactoryFinderTestCase2.FACTORIES[i][1] + " not of expected type"); + } + } +} diff --git a/impl/src/test/java/jakarta/faces/component/NamingContainerTestCase.java b/impl/src/test/java/jakarta/faces/component/NamingContainerTestCase.java new file mode 100644 index 0000000000..716212a50c --- /dev/null +++ b/impl/src/test/java/jakarta/faces/component/NamingContainerTestCase.java @@ -0,0 +1,516 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces.component; + +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.sun.faces.junit.JUnitFacesTestCaseBase; +import com.sun.faces.mock.MockRenderKit; + +import jakarta.faces.FactoryFinder; +import jakarta.faces.render.RenderKit; +import jakarta.faces.render.RenderKitFactory; + +/** + *

+ * Unit tests for the {@link NamingContainer} functionality of all the standard component classes. + *

+ */ +public class NamingContainerTestCase extends JUnitFacesTestCaseBase { + + // ------------------------------------------------------ Instance Variables + // The root of the component tree to be tested + private UIViewRoot root = null; + + // ---------------------------------------------------- Overall Test Methods + // Set up instance variables required by this test case. + @Override + @BeforeEach + public void setUp() throws Exception { + super.setUp(); + + root = new UIViewRoot(); + + root.setViewId("/viewId"); + root.setRenderKitId(RenderKitFactory.HTML_BASIC_RENDER_KIT); + facesContext.setViewRoot(root); + RenderKitFactory renderKitFactory = (RenderKitFactory) FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY); + RenderKit renderKit = new MockRenderKit(); + try { + renderKitFactory.addRenderKit(RenderKitFactory.HTML_BASIC_RENDER_KIT, renderKit); + } catch (IllegalArgumentException e) { + } + Map map = new HashMap<>(); + externalContext.setRequestParameterMap(map); + + } + + // Tear down instance variables required by this test case. + @Override + @AfterEach + public void tearDown() throws Exception { + + root = null; + super.tearDown(); + } + + // ------------------------------------------------- Individual Test Methods + // Test nested NamingContainer callbacks + @Test + public void testNested() { + + NamingContainerTestImpl a = new NamingContainerTestImpl(); + a.setId("a"); + NamingContainerTestImpl b = new NamingContainerTestImpl(); + b.setId("b"); + NamingContainerTestImpl d = new NamingContainerTestImpl(); + d.setId("d"); + UIPanel e = new UIPanel(); + e.setId("e"); + UIPanel g = new UIPanel(); + g.setId("g"); + a.getChildren().add(b); + b.getChildren().add(d); + b.getChildren().add(g); + d.getChildren().add(e); + + NamingContainerTestImpl.trace(null); + assertTrue(a == a.findComponent("a")); + + NamingContainerTestImpl.trace(null); + assertTrue(a == a.findComponent(":a")); + + NamingContainerTestImpl.trace(null); + assertTrue(b == a.findComponent("b")); + + NamingContainerTestImpl.trace(null); + assertTrue(b == a.findComponent(":b")); + + NamingContainerTestImpl.trace(null); + assertTrue(d == a.findComponent("b:d")); + + NamingContainerTestImpl.trace(null); + assertTrue(d == a.findComponent(":b:d")); + + NamingContainerTestImpl.trace(null); + assertTrue(e == a.findComponent("b:d:e")); + + NamingContainerTestImpl.trace(null); + assertTrue(e == a.findComponent(":b:d:e")); + + NamingContainerTestImpl.trace(null); + assertTrue(g == a.findComponent("b:g")); + + NamingContainerTestImpl.trace(null); + assertTrue(g == a.findComponent(":b:g")); + + } + + // Test nested NamingContainer callbacks + @Test + public void testNested2() { + + NamingContainerTestImpl a = new NamingContainerTestImpl(); + a.setId("a"); + NamingContainerTestImpl b = new NamingContainerTestImpl(); + b.setId("b"); + NamingContainerTestImpl d = new NamingContainerTestImpl(); + d.setId("b"); + UIPanel e = new UIPanel(); + e.setId("e"); + UIPanel g = new UIPanel(); + g.setId("g"); + a.getChildren().add(b); + b.getChildren().add(d); + b.getChildren().add(g); + d.getChildren().add(e); + + NamingContainerTestImpl.trace(null); + assertTrue(a == a.findComponent("a")); + + NamingContainerTestImpl.trace(null); + assertTrue(a == a.findComponent(":a")); + + NamingContainerTestImpl.trace(null); + assertTrue(b == a.findComponent("b")); + + NamingContainerTestImpl.trace(null); + assertTrue(b == a.findComponent(":b")); + + NamingContainerTestImpl.trace(null); + assertTrue(d == a.findComponent("b:b")); + + NamingContainerTestImpl.trace(null); + assertTrue(d == a.findComponent(":b:b")); + + NamingContainerTestImpl.trace(null); + assertTrue(e == a.findComponent("b:b:e")); + + NamingContainerTestImpl.trace(null); + assertTrue(e == a.findComponent(":b:b:e")); + + NamingContainerTestImpl.trace(null); + assertTrue(g == a.findComponent("b:g")); + + NamingContainerTestImpl.trace(null); + assertTrue(g == a.findComponent(":b:g")); + + } + + // Test standard NamingContainer functionality + @Test + public void testStandard() { + + // Set up a component hierarchy as follows (component ids in quotes): + // "a" - UIViewRoot at head of hierarchy + // "a" has children "b" and "c" + // "b" has children "d" and "g" + // "d" has children "e" and "f" + // "c" has children "h" and "i" + // Components "b" and "d" implement NamingContainer + UIViewRoot a = root; + a.setId("a"); + UIForm b = new UIForm(); + b.setId("b"); + UIPanel c = new UIPanel(); + c.setId("c"); + UINamingContainer d = new UINamingContainer(); + d.setId("d"); + UIPanel e = new UIPanel(); + e.setId("e"); + UIPanel f = new UIPanel(); + f.setId("f"); + UIPanel g = new UIPanel(); + g.setId("g"); + UIPanel h = new UIPanel(); + h.setId("h"); + UIPanel i = new UIPanel(); + i.setId("i"); + a.getChildren().add(b); + a.getChildren().add(c); + b.getChildren().add(d); + b.getChildren().add(g); + c.getChildren().add(h); + c.getChildren().add(i); + d.getChildren().add(e); + d.getChildren().add(f); + + // Positive relative searches from "a" + assertTrue(a == a.findComponent("a")); + assertTrue(b == a.findComponent("b")); + assertTrue(c == a.findComponent("c")); + assertTrue(d == a.findComponent("b:d")); + assertTrue(e == a.findComponent("b:d:e")); + assertTrue(f == a.findComponent("b:d:f")); + assertTrue(g == a.findComponent("b:g")); + assertTrue(h == a.findComponent("h")); + assertTrue(i == a.findComponent("i")); + + // Negative relative searches from "a" + assertNull(a.findComponent("d")); + assertNull(a.findComponent("e")); + assertNull(a.findComponent("f")); + assertNull(a.findComponent("g")); + + // Positive relative searches from "b" + assertTrue(b == b.findComponent("b")); + assertTrue(d == b.findComponent("d")); + assertTrue(e == b.findComponent("d:e")); + assertTrue(f == b.findComponent("d:f")); + assertTrue(g == b.findComponent("g")); + + // Negative relative searches from "b" + assertNull(b.findComponent("a")); + assertNull(b.findComponent("c")); + assertNull(b.findComponent("e")); + assertNull(b.findComponent("f")); + assertNull(b.findComponent("h")); + assertNull(b.findComponent("i")); + + // Positive relative searches from "c" + assertTrue(a == c.findComponent("a")); + assertTrue(b == c.findComponent("b")); + assertTrue(c == c.findComponent("c")); + assertTrue(d == c.findComponent("b:d")); + assertTrue(e == c.findComponent("b:d:e")); + assertTrue(f == c.findComponent("b:d:f")); + assertTrue(g == c.findComponent("b:g")); + assertTrue(h == c.findComponent("h")); + assertTrue(i == c.findComponent("i")); + + // Negative relative searches from "c" + assertNull(c.findComponent("d")); + assertNull(c.findComponent("e")); + assertNull(c.findComponent("f")); + assertNull(c.findComponent("g")); + + // Positive relative searches from "d" + assertTrue(d == d.findComponent("d")); + assertTrue(e == d.findComponent("e")); + assertTrue(f == d.findComponent("f")); + + // Negative relative searches from "d" + assertNull(d.findComponent("a")); + assertNull(d.findComponent("b")); + assertNull(d.findComponent("c")); + assertNull(d.findComponent("g")); + assertNull(d.findComponent("h")); + assertNull(d.findComponent("i")); + + // Positive relative searches from "e" + assertTrue(d == e.findComponent("d")); + assertTrue(e == e.findComponent("e")); + assertTrue(f == e.findComponent("f")); + + // Negative relative searches from "e" + assertNull(e.findComponent("a")); + assertNull(e.findComponent("b")); + assertNull(e.findComponent("c")); + assertNull(e.findComponent("g")); + assertNull(e.findComponent("h")); + assertNull(e.findComponent("i")); + + // Positive relative searches from "f" + assertTrue(d == f.findComponent("d")); + assertTrue(e == f.findComponent("e")); + assertTrue(f == f.findComponent("f")); + + // Negative relative searches from "f" + assertNull(f.findComponent("a")); + assertNull(f.findComponent("b")); + assertNull(f.findComponent("c")); + assertNull(f.findComponent("g")); + assertNull(f.findComponent("h")); + assertNull(f.findComponent("i")); + + // Positive relative searches from "g" + assertTrue(b == g.findComponent("b")); + assertTrue(d == g.findComponent("d")); + assertTrue(e == g.findComponent("d:e")); + assertTrue(f == g.findComponent("d:f")); + assertTrue(g == g.findComponent("g")); + + // Negative relative searches from "g" + assertNull(g.findComponent("a")); + assertNull(g.findComponent("c")); + assertNull(g.findComponent("e")); + assertNull(g.findComponent("f")); + assertNull(g.findComponent("h")); + assertNull(g.findComponent("i")); + + // Positive relative searches from "h" + assertTrue(a == h.findComponent("a")); + assertTrue(b == h.findComponent("b")); + assertTrue(c == h.findComponent("c")); + assertTrue(d == h.findComponent("b:d")); + assertTrue(e == h.findComponent("b:d:e")); + assertTrue(f == h.findComponent("b:d:f")); + assertTrue(g == h.findComponent("b:g")); + assertTrue(h == h.findComponent("h")); + assertTrue(i == h.findComponent("i")); + + // Negative relative searches from "h" + assertNull(h.findComponent("d")); + assertNull(h.findComponent("e")); + assertNull(h.findComponent("f")); + assertNull(h.findComponent("g")); + + // Positive relative searches from "i" + assertTrue(a == i.findComponent("a")); + assertTrue(b == i.findComponent("b")); + assertTrue(c == i.findComponent("c")); + assertTrue(d == i.findComponent("b:d")); + assertTrue(e == i.findComponent("b:d:e")); + assertTrue(f == i.findComponent("b:d:f")); + assertTrue(g == i.findComponent("b:g")); + assertTrue(h == i.findComponent("h")); + assertTrue(i == i.findComponent("i")); + + // Negative relative searches from "i" + assertNull(i.findComponent("d")); + assertNull(i.findComponent("e")); + assertNull(i.findComponent("f")); + assertNull(i.findComponent("g")); + + // Absolute searches from "a" + assertTrue(a == a.findComponent(":a")); + assertTrue(b == a.findComponent(":b")); + assertTrue(c == a.findComponent(":c")); + assertTrue(d == a.findComponent(":b:d")); + assertTrue(e == a.findComponent(":b:d:e")); + assertTrue(f == a.findComponent(":b:d:f")); + assertTrue(g == a.findComponent(":b:g")); + assertTrue(h == a.findComponent(":h")); + assertTrue(i == a.findComponent(":i")); + + // Absolute searches from "b" + assertTrue(a == b.findComponent(":a")); + assertTrue(b == b.findComponent(":b")); + assertTrue(c == b.findComponent(":c")); + assertTrue(d == b.findComponent(":b:d")); + assertTrue(e == b.findComponent(":b:d:e")); + assertTrue(f == b.findComponent(":b:d:f")); + assertTrue(g == b.findComponent(":b:g")); + assertTrue(h == b.findComponent(":h")); + assertTrue(i == b.findComponent(":i")); + + // Absolute searches from "c" + assertTrue(a == c.findComponent(":a")); + assertTrue(b == c.findComponent(":b")); + assertTrue(c == c.findComponent(":c")); + assertTrue(d == c.findComponent(":b:d")); + assertTrue(e == c.findComponent(":b:d:e")); + assertTrue(f == c.findComponent(":b:d:f")); + assertTrue(g == c.findComponent(":b:g")); + assertTrue(h == c.findComponent(":h")); + assertTrue(i == c.findComponent(":i")); + + // Absolute searches from "d" + assertTrue(a == d.findComponent(":a")); + assertTrue(b == d.findComponent(":b")); + assertTrue(c == d.findComponent(":c")); + assertTrue(d == d.findComponent(":b:d")); + assertTrue(e == d.findComponent(":b:d:e")); + assertTrue(f == d.findComponent(":b:d:f")); + assertTrue(g == d.findComponent(":b:g")); + assertTrue(h == d.findComponent(":h")); + assertTrue(i == d.findComponent(":i")); + + // Absolute searches from "e" + assertTrue(a == e.findComponent(":a")); + assertTrue(b == e.findComponent(":b")); + assertTrue(c == e.findComponent(":c")); + assertTrue(d == e.findComponent(":b:d")); + assertTrue(e == e.findComponent(":b:d:e")); + assertTrue(f == e.findComponent(":b:d:f")); + assertTrue(g == e.findComponent(":b:g")); + assertTrue(h == e.findComponent(":h")); + assertTrue(i == e.findComponent(":i")); + + // Absolute searches from "f" + assertTrue(a == f.findComponent(":a")); + assertTrue(b == f.findComponent(":b")); + assertTrue(c == f.findComponent(":c")); + assertTrue(d == f.findComponent(":b:d")); + assertTrue(e == f.findComponent(":b:d:e")); + assertTrue(f == f.findComponent(":b:d:f")); + assertTrue(g == f.findComponent(":b:g")); + assertTrue(h == f.findComponent(":h")); + assertTrue(i == f.findComponent(":i")); + + // Absolute searches from "g" + assertTrue(a == g.findComponent(":a")); + assertTrue(b == g.findComponent(":b")); + assertTrue(c == g.findComponent(":c")); + assertTrue(d == g.findComponent(":b:d")); + assertTrue(e == g.findComponent(":b:d:e")); + assertTrue(f == g.findComponent(":b:d:f")); + assertTrue(g == g.findComponent(":b:g")); + assertTrue(h == g.findComponent(":h")); + assertTrue(i == g.findComponent(":i")); + + // Absolute searches from "h" + assertTrue(a == h.findComponent(":a")); + assertTrue(b == h.findComponent(":b")); + assertTrue(c == h.findComponent(":c")); + assertTrue(d == h.findComponent(":b:d")); + assertTrue(e == h.findComponent(":b:d:e")); + assertTrue(f == h.findComponent(":b:d:f")); + assertTrue(g == h.findComponent(":b:g")); + assertTrue(h == h.findComponent(":h")); + assertTrue(i == h.findComponent(":i")); + + // Absolute searches from "i" + assertTrue(a == i.findComponent(":a")); + assertTrue(b == i.findComponent(":b")); + assertTrue(c == i.findComponent(":c")); + assertTrue(d == i.findComponent(":b:d")); + assertTrue(e == i.findComponent(":b:d:e")); + assertTrue(f == i.findComponent(":b:d:f")); + assertTrue(g == i.findComponent(":b:g")); + assertTrue(h == i.findComponent(":h")); + assertTrue(i == i.findComponent(":i")); + + // Cases that should throw exceptions + try { + a.findComponent(null); + fail("Should have thrown NullPointerException"); + } catch (NullPointerException ex) { + // Expected result + } + try { + a.findComponent("a:c:h"); + fail("Should have thrown IllegalArgumentException"); + } catch (IllegalArgumentException ex) { + // Expected result + } + try { + a.findComponent("a:c:i"); + fail("Should have thrown IllegalArgumentException"); + } catch (IllegalArgumentException ex) { + // Expected result + } + try { + a.findComponent(":a:c:h"); + fail("Should have thrown IllegalArgumentException"); + } catch (IllegalArgumentException ex) { + // Expected result + } + try { + a.findComponent(":a:c:i"); + fail("Should have thrown IllegalArgumentException"); + } catch (IllegalArgumentException ex) { + // Expected result + } + try { + a.findComponent("c:h"); + fail("Should have thrown IllegalArgumentException"); + } catch (IllegalArgumentException ex) { + // Expected result + } + try { + a.findComponent("c:i"); + fail("Should have thrown IllegalArgumentException"); + } catch (IllegalArgumentException ex) { + // Expected result + } + try { + a.findComponent(":c:h"); + fail("Should have thrown IllegalArgumentException"); + } catch (IllegalArgumentException ex) { + // Expected result + } + try { + a.findComponent(":c:i"); + fail("Should have thrown IllegalArgumentException"); + } catch (IllegalArgumentException ex) { + // Expected result + } + + } + + // --------------------------------------------------------- Support Methods +} diff --git a/impl/src/test/java/jakarta/faces/component/SearchExpressionHandlerTest.java b/impl/src/test/java/jakarta/faces/component/SearchExpressionHandlerTest.java new file mode 100644 index 0000000000..85062251bc --- /dev/null +++ b/impl/src/test/java/jakarta/faces/component/SearchExpressionHandlerTest.java @@ -0,0 +1,1061 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces.component; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.sun.faces.component.search.CompositeSearchKeywordResolver; +import com.sun.faces.component.search.SearchExpressionContextFactoryImpl; +import com.sun.faces.component.search.SearchExpressionHandlerImpl; +import com.sun.faces.component.search.SearchKeywordResolverImplAll; +import com.sun.faces.component.search.SearchKeywordResolverImplChild; +import com.sun.faces.component.search.SearchKeywordResolverImplComposite; +import com.sun.faces.component.search.SearchKeywordResolverImplForm; +import com.sun.faces.component.search.SearchKeywordResolverImplId; +import com.sun.faces.component.search.SearchKeywordResolverImplNamingContainer; +import com.sun.faces.component.search.SearchKeywordResolverImplNext; +import com.sun.faces.component.search.SearchKeywordResolverImplNone; +import com.sun.faces.component.search.SearchKeywordResolverImplParent; +import com.sun.faces.component.search.SearchKeywordResolverImplPrevious; +import com.sun.faces.component.search.SearchKeywordResolverImplRoot; +import com.sun.faces.component.search.SearchKeywordResolverImplThis; +import com.sun.faces.component.visit.VisitContextFactoryImpl; +import com.sun.faces.junit.JUnitFacesTestCaseBase; +import com.sun.faces.mock.MockRenderKit; + +import jakarta.faces.FacesException; +import jakarta.faces.FactoryFinder; +import jakarta.faces.component.search.ComponentNotFoundException; +import jakarta.faces.component.search.SearchExpressionContext; +import jakarta.faces.component.search.SearchExpressionHandler; +import jakarta.faces.component.search.SearchExpressionHint; +import jakarta.faces.component.search.SearchKeywordContext; +import jakarta.faces.component.search.SearchKeywordResolver; +import jakarta.faces.context.FacesContext; +import jakarta.faces.render.RenderKit; +import jakarta.faces.render.RenderKitFactory; + +public class SearchExpressionHandlerTest extends JUnitFacesTestCaseBase { + + @Override + @BeforeEach + public void setUp() throws Exception { + super.setUp(); + + UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null); + root.setViewId("/viewId"); + facesContext.setViewRoot(root); + + RenderKitFactory renderKitFactory = (RenderKitFactory) FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY); + RenderKit renderKit = new MockRenderKit(); + try { + renderKitFactory.addRenderKit(RenderKitFactory.HTML_BASIC_RENDER_KIT, renderKit); + } catch (IllegalArgumentException e) { + } + + FactoryFinder.setFactory(FactoryFinder.SEARCH_EXPRESSION_CONTEXT_FACTORY, SearchExpressionContextFactoryImpl.class.getName()); + FactoryFinder.setFactory(FactoryFinder.VISIT_CONTEXT_FACTORY, VisitContextFactoryImpl.class.getName()); + + SearchExpressionHandlerImpl expressionHandlerImpl = new SearchExpressionHandlerImpl(); + application.setSearchExpressionHandler(expressionHandlerImpl); + + CompositeSearchKeywordResolver searchKeywordResolvers = new CompositeSearchKeywordResolver(); + searchKeywordResolvers.add(new SearchKeywordResolverImplThis()); + searchKeywordResolvers.add(new SearchKeywordResolverImplParent()); + searchKeywordResolvers.add(new SearchKeywordResolverImplForm()); + searchKeywordResolvers.add(new SearchKeywordResolverImplComposite()); + searchKeywordResolvers.add(new SearchKeywordResolverImplNext()); + searchKeywordResolvers.add(new SearchKeywordResolverImplPrevious()); + searchKeywordResolvers.add(new SearchKeywordResolverImplNone()); + searchKeywordResolvers.add(new SearchKeywordResolverImplNamingContainer()); + searchKeywordResolvers.add(new SearchKeywordResolverImplRoot()); + searchKeywordResolvers.add(new SearchKeywordResolverImplId()); + searchKeywordResolvers.add(new SearchKeywordResolverImplChild()); + searchKeywordResolvers.add(new SearchKeywordResolverImplAll()); + application.setSearchKeywordResolver(searchKeywordResolvers); + } + + @Override + @AfterEach + public void tearDown() throws Exception { + super.tearDown(); + } + + private UIComponent resolveComponent(UIComponent source, String expression, SearchExpressionHint... hints) { + + SearchExpressionContext searchContext = SearchExpressionContext.createSearchExpressionContext(facesContext, source, new HashSet<>(Arrays.asList(hints)), + null); + + ResolveComponentCallback callback = new ResolveComponentCallback(); + + SearchExpressionHandler handler = FacesContext.getCurrentInstance().getApplication().getSearchExpressionHandler(); + + handler.resolveComponent(searchContext, expression, callback); + + return callback.component; + } + + private static class ResolveComponentCallback implements ContextCallback { + public UIComponent component; + + @Override + public void invokeContextCallback(FacesContext context, UIComponent target) { + component = target; + } + } + + private String resolveClientId(UIComponent source, String expression) { + SearchExpressionContext searchContext = SearchExpressionContext.createSearchExpressionContext(facesContext, source); + SearchExpressionHandler handler = FacesContext.getCurrentInstance().getApplication().getSearchExpressionHandler(); + + return handler.resolveClientId(searchContext, expression); + } + + private List resolveComponents(UIComponent source, String expression) { + SearchExpressionContext searchContext = SearchExpressionContext.createSearchExpressionContext(facesContext, source); + SearchExpressionHandler handler = FacesContext.getCurrentInstance().getApplication().getSearchExpressionHandler(); + + ResolveComponentsCallback callback = new ResolveComponentsCallback(); + + handler.resolveComponents(searchContext, expression, callback); + + return callback.components; + } + + private static class ResolveComponentsCallback implements ContextCallback { + public List components = new ArrayList<>(); + + @Override + public void invokeContextCallback(FacesContext context, UIComponent target) { + components.add(target); + } + } + + @Test + public void test_ResolveComponent_Parent() { + + UIComponent root = new UIPanel(); + + UIForm form = new UIForm(); + root.getChildren().add(form); + + UINamingContainer outerContainer = new UINamingContainer(); + form.getChildren().add(outerContainer); + + UINamingContainer innerContainer = new UINamingContainer(); + outerContainer.getChildren().add(innerContainer); + + UIComponent component = new UIOutput(); + innerContainer.getChildren().add(component); + + UIComponent source = new UICommand(); + innerContainer.getChildren().add(source); + + assertSame(innerContainer, resolveComponent(source, "@parent")); + } + + @Test + public void test_ResolveComponent_ParentParent() { + + UIComponent root = new UIPanel(); + + UIForm form = new UIForm(); + root.getChildren().add(form); + + UINamingContainer outerContainer = new UINamingContainer(); + form.getChildren().add(outerContainer); + + UINamingContainer innerContainer = new UINamingContainer(); + outerContainer.getChildren().add(innerContainer); + + UIComponent component = new UIOutput(); + innerContainer.getChildren().add(component); + + UIComponent source = new UICommand(); + innerContainer.getChildren().add(source); + + assertSame(outerContainer, resolveComponent(source, "@parent:@parent")); + } + + @Test + public void test_ResolveComponent_Form() { + + UIComponent root = new UIPanel(); + + UIForm form = new UIForm(); + root.getChildren().add(form); + + UINamingContainer outerContainer = new UINamingContainer(); + form.getChildren().add(outerContainer); + + UINamingContainer innerContainer = new UINamingContainer(); + outerContainer.getChildren().add(innerContainer); + + UIComponent component = new UIOutput(); + innerContainer.getChildren().add(component); + + UIComponent source = new UICommand(); + innerContainer.getChildren().add(source); + + assertSame(form, resolveComponent(source, "@form")); + } + + @Test + public void test_ResolveComponent_FormParent() { + + UIComponent root = new UIPanel(); + + UIForm form = new UIForm(); + root.getChildren().add(form); + + UINamingContainer outerContainer = new UINamingContainer(); + form.getChildren().add(outerContainer); + + UINamingContainer innerContainer = new UINamingContainer(); + outerContainer.getChildren().add(innerContainer); + + UIComponent component = new UIOutput(); + innerContainer.getChildren().add(component); + + UIComponent source = new UICommand(); + innerContainer.getChildren().add(source); + + assertSame(root, resolveComponent(source, "@form:@parent")); + } + + @Test + public void test_ResolveComponent_All() { + + UIComponent root = new UIPanel(); + + UIForm form = new UIForm(); + root.getChildren().add(form); + + UINamingContainer outerContainer = new UINamingContainer(); + form.getChildren().add(outerContainer); + + UINamingContainer innerContainer = new UINamingContainer(); + outerContainer.getChildren().add(innerContainer); + + UIComponent component = new UIOutput(); + innerContainer.getChildren().add(component); + + UIComponent source = new UICommand(); + innerContainer.getChildren().add(source); + + assertSame(root, resolveComponent(source, "@all")); + } + + @Test + public void test_ResolveComponent_This() { + + UIComponent root = new UIPanel(); + + UIForm form = new UIForm(); + root.getChildren().add(form); + + UINamingContainer outerContainer = new UINamingContainer(); + form.getChildren().add(outerContainer); + + UINamingContainer innerContainer = new UINamingContainer(); + outerContainer.getChildren().add(innerContainer); + + UIComponent component = new UIOutput(); + innerContainer.getChildren().add(component); + + UIComponent source = new UICommand(); + innerContainer.getChildren().add(source); + + assertSame(source, resolveComponent(source, "@this")); + } + + @Test + public void test_ResolveComponent_ThisParent() { + + UIComponent root = new UIPanel(); + + UIForm form = new UIForm(); + root.getChildren().add(form); + + UINamingContainer outerContainer = new UINamingContainer(); + form.getChildren().add(outerContainer); + + UINamingContainer innerContainer = new UINamingContainer(); + outerContainer.getChildren().add(innerContainer); + + UIComponent component = new UIOutput(); + innerContainer.getChildren().add(component); + + UIComponent source = new UICommand(); + innerContainer.getChildren().add(source); + + assertSame(innerContainer, resolveComponent(source, "@this:@parent")); + } + + @Test + public void test_ResolveComponent_Namingcontainer() { + + UIComponent root = new UIPanel(); + + UIForm form = new UIForm(); + root.getChildren().add(form); + + UINamingContainer outerContainer = new UINamingContainer(); + form.getChildren().add(outerContainer); + + UINamingContainer innerContainer = new UINamingContainer(); + outerContainer.getChildren().add(innerContainer); + + UIComponent component = new UIOutput(); + innerContainer.getChildren().add(component); + + UIComponent source = new UICommand(); + innerContainer.getChildren().add(source); + + assertSame(innerContainer, resolveComponent(source, "@namingcontainer")); + } + + @Test + public void test_ResolveComponent_Absolute() { + + UIComponent root = new UIPanel(); + FacesContext.getCurrentInstance().getViewRoot().getChildren().add(root); + + UIForm form = new UIForm(); + form.setId("form"); + root.getChildren().add(form); + + UINamingContainer outerContainer = new UINamingContainer(); + outerContainer.setId("outerContainer"); + form.getChildren().add(outerContainer); + + UINamingContainer innerContainer = new UINamingContainer(); + innerContainer.setId("innerContainer"); + outerContainer.getChildren().add(innerContainer); + + UIComponent component = new UIOutput(); + innerContainer.getChildren().add(component); + + UIComponent source = new UICommand(); + source.setId("source"); + innerContainer.getChildren().add(source); + + assertSame(source, resolveComponent(source, " :form:outerContainer:innerContainer:source ")); + } + + @Test + public void test_ResolveComponent_Relative() { + + UIComponent root = new UIPanel(); + + UIForm form = new UIForm(); + form.setId("form"); + root.getChildren().add(form); + + UINamingContainer outerContainer = new UINamingContainer(); + outerContainer.setId("outerContainer"); + form.getChildren().add(outerContainer); + + UINamingContainer innerContainer = new UINamingContainer(); + innerContainer.setId("innerContainer"); + outerContainer.getChildren().add(innerContainer); + + UIComponent component = new UIOutput(); + component.setId("other"); + innerContainer.getChildren().add(component); + + UIComponent source = new UICommand(); + source.setId("source"); + innerContainer.getChildren().add(source); + + assertSame(component, resolveComponent(source, " other ")); + } + + @Test + public void test_ResolveComponent_AbsoluteForm() { + + UIComponent root = new UIPanel(); + root.setId("root"); + + UIForm form = new UIForm(); + form.setId("form"); + root.getChildren().add(form); + + UINamingContainer outerContainer = new UINamingContainer(); + outerContainer.setId("outerContainer"); + form.getChildren().add(outerContainer); + + UINamingContainer innerContainer = new UINamingContainer(); + innerContainer.setId("innerContainer"); + outerContainer.getChildren().add(innerContainer); + + UIComponent component = new UIOutput(); + component.setId("other"); + innerContainer.getChildren().add(component); + + UIComponent source = new UICommand(); + source.setId("source"); + innerContainer.getChildren().add(source); + + assertSame(root, resolveComponent(source, " :form:@parent ")); + } + + @Test + public void test_ResolveComponent_ParentChild() { + + UIComponent root = new UIPanel(); + root.setId("root"); + + UIForm form = new UIForm(); + form.setId("form"); + root.getChildren().add(form); + + UINamingContainer outerContainer = new UINamingContainer(); + outerContainer.setId("outerContainer"); + form.getChildren().add(outerContainer); + + UINamingContainer innerContainer = new UINamingContainer(); + innerContainer.setId("innerContainer"); + outerContainer.getChildren().add(innerContainer); + + UIComponent component = new UIOutput(); + component.setId("other"); + innerContainer.getChildren().add(component); + + UIComponent source = new UICommand(); + source.setId("source"); + innerContainer.getChildren().add(source); + + assertSame(component, resolveComponent(source, " @parent:@child(0) ")); + assertSame(source, resolveComponent(source, " @parent:@child(1) ")); + } + + @Test + public void test_ResolveComponent_AbsoluteNamingcontainer() { + + UIComponent root = new UIPanel(); + root.setId("root"); + + UIForm form = new UIForm(); + form.setId("form"); + root.getChildren().add(form); + + UINamingContainer outerContainer = new UINamingContainer(); + outerContainer.setId("outerContainer"); + form.getChildren().add(outerContainer); + + UINamingContainer innerContainer = new UINamingContainer(); + innerContainer.setId("innerContainer"); + outerContainer.getChildren().add(innerContainer); + + UIComponent component = new UIOutput(); + component.setId("other"); + innerContainer.getChildren().add(component); + + UIComponent source = new UICommand(); + source.setId("source"); + innerContainer.getChildren().add(source); + + assertSame(form, resolveComponent(source, " :form:outerContainer:@namingcontainer ")); + } + + @Test + public void test_ResolveClientId_This() { + + UIComponent root = new UIPanel(); + + UIForm form = new UIForm(); + form.setId("form"); + root.getChildren().add(form); + + UINamingContainer outerContainer = new UINamingContainer(); + outerContainer.setId("outerContainer"); + form.getChildren().add(outerContainer); + + UINamingContainer innerContainer = new UINamingContainer(); + innerContainer.setId("innerContainer"); + outerContainer.getChildren().add(innerContainer); + + UIComponent component = new UIOutput(); + innerContainer.getChildren().add(component); + + UIComponent source = new UICommand(); + source.setId("source"); + innerContainer.getChildren().add(source); + + assertEquals("form:outerContainer:innerContainer:source", resolveClientId(source, " @this ")); + } + + @Test + public void test_ResolveClientId_Form() { + + UIComponent root = new UIPanel(); + + UIForm form = new UIForm(); + form.setId("form"); + root.getChildren().add(form); + + UINamingContainer outerContainer = new UINamingContainer(); + outerContainer.setId("outerContainer"); + form.getChildren().add(outerContainer); + + UINamingContainer innerContainer = new UINamingContainer(); + innerContainer.setId("innerContainer"); + outerContainer.getChildren().add(innerContainer); + + UIComponent component = new UIOutput(); + innerContainer.getChildren().add(component); + + UIComponent source = new UICommand(); + source.setId("source"); + innerContainer.getChildren().add(source); + + assertEquals("form", resolveClientId(source, " @form ")); + } + + @Test + public void test_ResolveClientId_AbsoluteId() { + + UIComponent root = new UIPanel(); + FacesContext.getCurrentInstance().getViewRoot().getChildren().add(root); + + UIForm form = new UIForm(); + form.setId("form"); + root.getChildren().add(form); + + UINamingContainer outerContainer = new UINamingContainer(); + outerContainer.setId("outerContainer"); + form.getChildren().add(outerContainer); + + UINamingContainer innerContainer = new UINamingContainer(); + innerContainer.setId("innerContainer"); + outerContainer.getChildren().add(innerContainer); + + UIComponent component = new UIOutput(); + innerContainer.getChildren().add(component); + + UIComponent source = new UICommand(); + innerContainer.getChildren().add(source); + + assertEquals("form", resolveClientId(source, " :form ")); + } + + @Test + public void test_ResolveClientId_Relative() { + + UIComponent root = new UIPanel(); + + UIForm form = new UIForm(); + form.setId("form"); + root.getChildren().add(form); + + UINamingContainer outerContainer = new UINamingContainer(); + outerContainer.setId("outerContainer"); + form.getChildren().add(outerContainer); + + UINamingContainer innerContainer = new UINamingContainer(); + innerContainer.setId("innerContainer"); + outerContainer.getChildren().add(innerContainer); + + UIComponent component = new UIOutput(); + component.setId("other"); + innerContainer.getChildren().add(component); + + UIComponent source = new UICommand(); + source.setId("source"); + innerContainer.getChildren().add(source); + + assertEquals("form:outerContainer:innerContainer:other", resolveClientId(source, " other ")); + } + + @Test + public void test_ResolveComponents_RelativeAndParentParent() { + + UIComponent root = new UIPanel(); + + UIForm form = new UIForm(); + form.setId("form"); + root.getChildren().add(form); + + UINamingContainer outerContainer = new UINamingContainer(); + outerContainer.setId("outerContainer"); + form.getChildren().add(outerContainer); + + UINamingContainer innerContainer = new UINamingContainer(); + innerContainer.setId("innerContainer"); + outerContainer.getChildren().add(innerContainer); + + UIComponent component = new UIOutput(); + component.setId("other"); + innerContainer.getChildren().add(component); + + UIComponent source = new UICommand(); + source.setId("source"); + innerContainer.getChildren().add(source); + + List resolvedComponents = resolveComponents(source, " other @parent:@parent "); + assertTrue(resolvedComponents.contains(component)); + assertTrue(resolvedComponents.contains(outerContainer)); + assertEquals(2, resolvedComponents.size()); + } + + @Test + public void test_ResolveComponents_RelativeAndThisParent() { + + UIComponent root = new UIPanel(); + + UIForm form = new UIForm(); + form.setId("form"); + root.getChildren().add(form); + + UINamingContainer outerContainer = new UINamingContainer(); + outerContainer.setId("outerContainer"); + form.getChildren().add(outerContainer); + + UINamingContainer innerContainer = new UINamingContainer(); + innerContainer.setId("innerContainer"); + outerContainer.getChildren().add(innerContainer); + + UIComponent component = new UIOutput(); + component.setId("other"); + innerContainer.getChildren().add(component); + + UIComponent source = new UICommand(); + source.setId("source"); + innerContainer.getChildren().add(source); + + List resolvedComponents = resolveComponents(source, " other,@this:@parent @none "); + assertTrue(resolvedComponents.contains(component)); + assertTrue(resolvedComponents.contains(innerContainer)); + assertEquals(2, resolvedComponents.size()); + } + + @Test + public void test_ResolveComponent_Next() { + + UIComponent root = new UIPanel(); + root.setId("root"); + + UIComponent command1 = new UICommand(); + command1.setId("command1"); + root.getChildren().add(command1); + + UIComponent command2 = new UICommand(); + command2.setId("command2"); + root.getChildren().add(command2); + + UIComponent command3 = new UICommand(); + command3.setId("command3"); + root.getChildren().add(command3); + + assertSame(command2, resolveComponent(command1, " @next ")); + assertSame(command3, resolveComponent(command2, " @next ")); + + try { + resolveComponent(command3, " @next"); + fail("This should actually raise an exception"); + } catch (Exception e) { + assertEquals(ComponentNotFoundException.class, e.getClass()); + } + } + + @Test + public void test_ResolveComponent_NextNext() { + + UIComponent root = new UIPanel(); + root.setId("root"); + + UIComponent command1 = new UICommand(); + command1.setId("command1"); + root.getChildren().add(command1); + + UIComponent command2 = new UICommand(); + command2.setId("command2"); + root.getChildren().add(command2); + + UIComponent command3 = new UICommand(); + command3.setId("command3"); + root.getChildren().add(command3); + + assertSame(command3, resolveComponent(command1, " @next:@next ")); + + try { + resolveComponent(command2, " @next:@next"); + fail("This should actually raise an exception"); + } catch (Exception e) { + assertEquals(ComponentNotFoundException.class, e.getClass()); + } + + try { + resolveComponent(command3, " @next:@next"); + fail("This should actually raise an exception"); + } catch (Exception e) { + assertEquals(ComponentNotFoundException.class, e.getClass()); + } + } + + @Test + public void test_ResolveComponent_Previous() { + + UIComponent root = new UIPanel(); + root.setId("root"); + + UIComponent command1 = new UICommand(); + command1.setId("command1"); + root.getChildren().add(command1); + + UIComponent command2 = new UICommand(); + command2.setId("command2"); + root.getChildren().add(command2); + + UIComponent command3 = new UICommand(); + command3.setId("command3"); + root.getChildren().add(command3); + + assertSame(command1, resolveComponent(command2, " @previous ")); + assertSame(command2, resolveComponent(command3, " @previous ")); + + try { + resolveComponent(command1, " @previous"); + fail("This should actually raise an exception"); + } catch (Exception e) { + assertEquals(ComponentNotFoundException.class, e.getClass()); + } + } + + @Test + public void test_ResolveComponent_Root() { + + UIComponent root = new UIPanel(); + root.setId("root"); + + UIComponent command1 = new UICommand(); + command1.setId("command1"); + root.getChildren().add(command1); + + UIComponent command2 = new UICommand(); + command2.setId("command2"); + root.getChildren().add(command2); + + UIComponent command3 = new UICommand(); + command3.setId("command3"); + root.getChildren().add(command3); + + assertSame(facesContext.getViewRoot(), resolveComponent(command2, " @root ")); + } + + @Test + public void test_ResolveComponent_FormChildNextNext() { + + UIForm root = new UIForm(); + root.setId("form"); + + UIComponent command1 = new UICommand(); + command1.setId("command1"); + root.getChildren().add(command1); + + UIComponent command2 = new UICommand(); + command2.setId("command2"); + root.getChildren().add(command2); + + UIComponent command3 = new UICommand(); + command3.setId("command3"); + root.getChildren().add(command3); + + assertSame(command3, resolveComponent(command1, " @form:@child(0):@next:@next ")); + } + + @Test + public void test_ResolveComponent_IgnoreNoResult() { + UIForm root = new UIForm(); + root.setId("form"); + + UIComponent command1 = new UICommand(); + command1.setId("command1"); + root.getChildren().add(command1); + + UIComponent command2 = new UICommand(); + command2.setId("command2"); + root.getChildren().add(command2); + + assertSame(null, resolveComponent(command1, " command3 ", SearchExpressionHint.IGNORE_NO_RESULT)); + } + + @Test + public void test_ResolveClientId_AbsoluteWithFormPrependIdFalse() { + + UIComponent root = new UIPanel(); + FacesContext.getCurrentInstance().getViewRoot().getChildren().add(root); + + UIForm form = new UIForm(); + form.setId("form"); + form.setPrependId(false); + root.getChildren().add(form); + + UINamingContainer outerContainer = new UINamingContainer(); + outerContainer.setId("outerContainer"); + form.getChildren().add(outerContainer); + + UINamingContainer innerContainer = new UINamingContainer(); + innerContainer.setId("innerContainer"); + outerContainer.getChildren().add(innerContainer); + + UIComponent component = new UIOutput(); + innerContainer.getChildren().add(component); + + UIComponent source = new UICommand(); + source.setId("source"); + innerContainer.getChildren().add(source); + + assertEquals("outerContainer:innerContainer:source", resolveClientId(source, " :form:outerContainer:innerContainer:source ")); + } + + @Test + public void test_ResolveClientId_AbsoluteWithFormPrependIdFalse_InvokeOnComponent() { + + UIComponent root = new UIPanel(); + FacesContext.getCurrentInstance().getViewRoot().getChildren().add(root); + + UIForm form = new UIForm(); + form.setId("form"); + form.setPrependId(false); + root.getChildren().add(form); + + UINamingContainer outerContainer = new UINamingContainer(); + outerContainer.setId("outerContainer"); + form.getChildren().add(outerContainer); + + UINamingContainer innerContainer = new UINamingContainer(); + innerContainer.setId("innerContainer"); + outerContainer.getChildren().add(innerContainer); + + UIComponent component = new UIOutput(); + innerContainer.getChildren().add(component); + + UIComponent source = new UICommand(); + source.setId("source"); + innerContainer.getChildren().add(source); + + assertEquals("outerContainer:innerContainer:source", resolveClientId(source, " outerContainer:innerContainer:source ")); + } + + @Test + public void test_Passthrough() { + SearchExpressionHandler handler = facesContext.getApplication().getSearchExpressionHandler(); + + SearchExpressionContext searchExpressionContext = SearchExpressionContext.createSearchExpressionContext(facesContext, null); + + Assertions.assertFalse(handler.isPassthroughExpression(searchExpressionContext, "mainForm:showName")); + Assertions.assertFalse(handler.isPassthroughExpression(searchExpressionContext, "mainForm:table:3:nested:1:nestedText")); + Assertions.assertFalse(handler.isPassthroughExpression(searchExpressionContext, "mainForm:table:3:baseText")); + Assertions.assertFalse(handler.isPassthroughExpression(searchExpressionContext, "mainForm:table:0:baseText")); + Assertions.assertFalse(handler.isPassthroughExpression(searchExpressionContext, "mainForm:table:3:nested:0:nestedText")); + Assertions.assertFalse(handler.isPassthroughExpression(searchExpressionContext, "mainForm:table:3:nested")); + Assertions.assertFalse(handler.isPassthroughExpression(searchExpressionContext, "mainForm:table:1:nested:0:nestedText")); + + Assertions.assertFalse(handler.isPassthroughExpression(searchExpressionContext, "@this")); + Assertions.assertFalse(handler.isPassthroughExpression(searchExpressionContext, "@this:@parent:showName")); + Assertions.assertFalse(handler.isPassthroughExpression(searchExpressionContext, "@parent:showName:@parent:showName")); + Assertions.assertFalse(handler.isPassthroughExpression(searchExpressionContext, "@form")); + Assertions.assertFalse(handler.isPassthroughExpression(searchExpressionContext, "@form:showName")); + Assertions.assertFalse(handler.isPassthroughExpression(searchExpressionContext, "@namingcontainer:showName")); + Assertions.assertFalse(handler.isPassthroughExpression(searchExpressionContext, "@previous")); + Assertions.assertFalse(handler.isPassthroughExpression(searchExpressionContext, "@next")); + Assertions.assertFalse(handler.isPassthroughExpression(searchExpressionContext, "@parent:@id(msgName)")); + + Assertions.assertFalse(handler.isPassthroughExpression(searchExpressionContext, "@whoNows")); + Assertions.assertFalse(handler.isPassthroughExpression(searchExpressionContext, "@parent:@whoNows")); + Assertions.assertFalse(handler.isPassthroughExpression(searchExpressionContext, "mainForm:@whoNows")); + Assertions.assertFalse(handler.isPassthroughExpression(searchExpressionContext, "!whoNows")); + + Set hints = new HashSet<>(); + hints.add(SearchExpressionHint.RESOLVE_CLIENT_SIDE); + searchExpressionContext = SearchExpressionContext.createSearchExpressionContext(facesContext, null, hints, null); + + Assertions.assertTrue(handler.isPassthroughExpression(searchExpressionContext, "@form")); + Assertions.assertFalse(handler.isPassthroughExpression(searchExpressionContext, "@form:showName")); + Assertions.assertFalse(handler.isPassthroughExpression(searchExpressionContext, "@form:@child(0)")); + } + + @Test + public void test_Valid() { + SearchExpressionHandler handler = facesContext.getApplication().getSearchExpressionHandler(); + + SearchExpressionContext searchExpressionContext = SearchExpressionContext.createSearchExpressionContext(facesContext, null); + + Assertions.assertTrue(handler.isValidExpression(searchExpressionContext, "mainForm:showName")); + Assertions.assertTrue(handler.isValidExpression(searchExpressionContext, "mainForm:table:3:nested:1:nestedText")); + Assertions.assertTrue(handler.isValidExpression(searchExpressionContext, "mainForm:table:3:baseText")); + Assertions.assertTrue(handler.isValidExpression(searchExpressionContext, "mainForm:table:0:baseText")); + Assertions.assertTrue(handler.isValidExpression(searchExpressionContext, "mainForm:table:3:nested:0:nestedText")); + Assertions.assertTrue(handler.isValidExpression(searchExpressionContext, "mainForm:table:3:nested")); + Assertions.assertTrue(handler.isValidExpression(searchExpressionContext, "mainForm:table:1:nested:0:nestedText")); + + Assertions.assertTrue(handler.isValidExpression(searchExpressionContext, "@this")); + Assertions.assertTrue(handler.isValidExpression(searchExpressionContext, "@this:@parent:showName")); + Assertions.assertTrue(handler.isValidExpression(searchExpressionContext, "@parent:showName:@parent:showName")); + Assertions.assertTrue(handler.isValidExpression(searchExpressionContext, "@form:showName")); + Assertions.assertTrue(handler.isValidExpression(searchExpressionContext, "@namingcontainer:showName")); + Assertions.assertTrue(handler.isValidExpression(searchExpressionContext, "@previous")); + Assertions.assertTrue(handler.isValidExpression(searchExpressionContext, "@next")); + Assertions.assertTrue(handler.isValidExpression(searchExpressionContext, "@parent:@id(msgName)")); + + Assertions.assertFalse(handler.isValidExpression(searchExpressionContext, "@whoNows")); + Assertions.assertFalse(handler.isValidExpression(searchExpressionContext, "@parent:@whoNows")); + Assertions.assertFalse(handler.isValidExpression(searchExpressionContext, "mainForm:@whoNows")); + + Assertions.assertFalse(handler.isValidExpression(searchExpressionContext, "@none:@parent")); + Assertions.assertFalse(handler.isValidExpression(searchExpressionContext, "@all:@parent")); + } + + @Test + public void test_ResolveComponents_Id() { + UIComponent root = new UIPanel(); + FacesContext.getCurrentInstance().getViewRoot().getChildren().add(root); + + UINamingContainer outerContainer = new UINamingContainer(); + outerContainer.setId("myContainer"); + root.getChildren().add(outerContainer); + + UIForm form = new UIForm(); + form.setId("form"); + root.getChildren().add(form); + + UINamingContainer innerContainer = new UINamingContainer(); + innerContainer.setId("myContainer"); + form.getChildren().add(innerContainer); + + UINamingContainer innerContainer2 = new UINamingContainer(); + innerContainer2.setId("myContainer2"); + form.getChildren().add(innerContainer2); + + UINamingContainer innerContainer3 = new UINamingContainer(); + innerContainer3.setId("myContainer3-test"); + form.getChildren().add(innerContainer3); + + List result = resolveComponents(form, " @id(myContainer) "); + assertTrue(result.size() == 1); + assertTrue(result.contains(innerContainer)); + + result = resolveComponents(form, " @id(myContainer3-test) "); + assertTrue(result.size() == 1); + assertTrue(result.contains(innerContainer3)); + } + + /** + * The SearchExpression API was inspired by PrimeFaces. This test only tests, if PFS (PrimeFaces Selectors -> jQuery + * like selectors; like @(#myId > .myStyle)) can be correctly handled by the API+IMPL as a passthrough expression. + */ + @Test + public void test_PFS() { + CompositeSearchKeywordResolver s = (CompositeSearchKeywordResolver) application.getSearchKeywordResolver(); + s.add(new SearchKeywordResolver() { + + @Override + public void resolve(SearchKeywordContext searchKeywordContext, UIComponent previous, String keyword) { + + } + + @Override + public boolean isResolverForKeyword(SearchExpressionContext searchExpressionContext, String keyword) { + return keyword.startsWith("(") && keyword.endsWith(")"); + } + + @Override + public boolean isPassthrough(SearchExpressionContext searchExpressionContext, String keyword) { + return true; + } + + @Override + public boolean isLeaf(SearchExpressionContext searchExpressionContext, String keyword) { + return true; + } + }); + + UIComponent root = new UIPanel(); + FacesContext.getCurrentInstance().getViewRoot().getChildren().add(root); + + assertEquals("@(.myPanel #id)", resolveClientId(root, " @(.myPanel #id)")); + + SearchExpressionHandler handler = facesContext.getApplication().getSearchExpressionHandler(); + SearchExpressionContext searchExpressionContext = SearchExpressionContext.createSearchExpressionContext(facesContext, null); + Assertions.assertTrue(handler.isValidExpression(searchExpressionContext, "@(.myPanel #id)")); + Assertions.assertFalse(handler.isValidExpression(searchExpressionContext, "@(.myPanel #id):test")); + } + + @Test + public void test_chainOfResponsability() { + CompositeSearchKeywordResolver s = (CompositeSearchKeywordResolver) application.getSearchKeywordResolver(); + s.add(new CustomSearchKeywordResolverImplForm()); // drop in new @form resolver + + UIComponent root = new UIPanel(); + + UIForm form = new UIForm(); + root.getChildren().add(form); + + UINamingContainer outerContainer = new UINamingContainer(); + form.getChildren().add(outerContainer); + + UINamingContainer innerContainer = new UINamingContainer(); + outerContainer.getChildren().add(innerContainer); + + UIComponent component = new UIOutput(); + innerContainer.getChildren().add(component); + + UIComponent source = new UICommand(); + innerContainer.getChildren().add(source); + + assertSame(source, resolveComponent(source, "@form")); + assertNotSame(form, resolveComponent(source, "@form")); + } + + class CustomSearchKeywordResolverImplForm extends SearchKeywordResolverImplForm { + @Override + public void resolve(SearchKeywordContext searchKeywordContext, UIComponent current, String keyword) { + searchKeywordContext.invokeContextCallback(current); + } + } + + @Test + public void test_ResolveComponent_LeafErrorHandling() { + + UIComponent root = new UIPanel(); + root.setId("root"); + + try { + resolveComponent(root, " @none:myId"); + fail("This should actually raise an exception"); + } catch (Exception e) { + assertEquals(FacesException.class, e.getClass()); + } + } +} diff --git a/impl/src/test/java/jakarta/faces/component/StateHolderSaverTestCase.java b/impl/src/test/java/jakarta/faces/component/StateHolderSaverTestCase.java new file mode 100644 index 0000000000..721dce80c4 --- /dev/null +++ b/impl/src/test/java/jakarta/faces/component/StateHolderSaverTestCase.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces.component; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +import jakarta.faces.convert.IntegerConverter; + +public class StateHolderSaverTestCase extends UIComponentBaseTestCase { + + // ------------------------------------------------- Individual Test Methods + @Override + @Test + public void testLifecycleManagement() { + } + + @Override + @Test + public void testChildrenRecursive() { + } + + @Override + @Test + public void testComponentReconnect() { + } + + @Override + @Test + public void testComponentRemoval() { + } + + @Test + public void testImplementsStateHolder() throws Exception { + StateHolderSaver saver = null; + UIInput postSave, preSave = new UIInput(); + preSave.setId("id1"); + preSave.setRendererType(null); + + saver = new StateHolderSaver(facesContext, preSave); + postSave = (UIInput) saver.restore(facesContext); + assertEquals(postSave.getId(), preSave.getId()); + } + + @Test + public void testImplementsSerializable() throws Exception { + StateHolderSaver saver = null; + String preSave = "hello"; + String postSave = null; + + saver = new StateHolderSaver(facesContext, preSave); + postSave = (String) saver.restore(facesContext); + assertTrue(preSave.equals(postSave)); + } + + @Test + public void testImplementsNeither() throws Exception { + StateHolderSaver saver = null; + IntegerConverter preSave = new IntegerConverter(), postSave = null; + + saver = new StateHolderSaver(facesContext, preSave); + postSave = (IntegerConverter) saver.restore(facesContext); + assertTrue(postSave != null); // lack of ClassCastException + } +} diff --git a/impl/src/test/java/jakarta/faces/component/UIColumnTestCase.java b/impl/src/test/java/jakarta/faces/component/UIColumnTestCase.java new file mode 100644 index 0000000000..e17feeb7cf --- /dev/null +++ b/impl/src/test/java/jakarta/faces/component/UIColumnTestCase.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces.component; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + *

+ * Unit tests for {@link UIColumn}. + *

+ */ +public class UIColumnTestCase extends UIComponentBaseTestCase { + + // ---------------------------------------------------- Overall Test Methods + // Set up instance variables required by this test case. + @Override + @BeforeEach + public void setUp() throws Exception { + super.setUp(); + component = new UIColumn(); + expectedFamily = UIColumn.COMPONENT_FAMILY; + expectedId = null; + expectedRendererType = null; + } + + // Tear down instance variables required by ths test case + @Override + @AfterEach + public void tearDown() throws Exception { + super.tearDown(); + } + + // ------------------------------------------------- Individual Test Methods + // Suppress lifecycle tests since we do not have a renderer + @Override + @Test + public void testLifecycleManagement() { + } + + // --------------------------------------------------------- Support Methods + // Create a pristine component of the type to be used in state holder tests + @Override + protected UIComponent createComponent() { + UIComponent component = new UIColumn(); + component.setRendererType(null); + return component; + } +} diff --git a/impl/src/test/java/jakarta/faces/component/UICommandTestCase.java b/impl/src/test/java/jakarta/faces/component/UICommandTestCase.java new file mode 100644 index 0000000000..b6d12aea60 --- /dev/null +++ b/impl/src/test/java/jakarta/faces/component/UICommandTestCase.java @@ -0,0 +1,320 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces.component; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.sun.faces.mock.MockExternalContext; + +import jakarta.faces.FactoryFinder; +import jakarta.faces.context.FacesContext; +import jakarta.faces.event.ActionEvent; +import jakarta.faces.event.ActionListener; +import jakarta.faces.event.PhaseId; +import jakarta.faces.render.RenderKit; +import jakarta.faces.render.RenderKitFactory; +import jakarta.faces.render.Renderer; + +/** + *

+ * Unit tests for {@link UICommand}. + *

+ */ +public class UICommandTestCase extends UIComponentBaseTestCase { + + // ---------------------------------------------------- Overall Test Methods + // Set up instance variables required by this test case. + @Override + @BeforeEach + public void setUp() throws Exception { + super.setUp(); + component = new UICommand(); + expectedFamily = UICommand.COMPONENT_FAMILY; + expectedId = null; + expectedRendererType = "jakarta.faces.Button"; + } + + // ------------------------------------------------- Individual Test Methods + // Test order of action listener calls with actionListener also + public void PENDING_FIXME_testActionOrder() throws Exception { + + RenderKitFactory renderKitFactory = (RenderKitFactory) FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY); + RenderKit renderKit = renderKitFactory.getRenderKit(facesContext, RenderKitFactory.HTML_BASIC_RENDER_KIT); + renderKit.addRenderer(UICommand.COMPONENT_FAMILY, "jakarta.faces.Button", new ButtonRenderer()); + UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null); + root.getChildren().add(component); + UICommand command = (UICommand) component; + command.setId("command"); + command.addActionListener(new CommandActionListenerTestImpl("l1")); + command.addActionListener(new CommandActionListenerTestImpl("l2")); + command.setImmediate(true); + request.setAttribute("l3", new CommandActionListenerTestImpl("l3")); + + // Override the default action listener to test ordering + ActionListener oldDefaultActionListener = facesContext.getApplication().getActionListener(); + facesContext.getApplication().setActionListener(new CommandActionListenerTestImpl("14")); + Map map = new HashMap<>(); + map.put(command.getClientId(facesContext), ""); + MockExternalContext econtext = (MockExternalContext) facesContext.getExternalContext(); + econtext.setRequestParameterMap(map); + CommandActionListenerTestImpl.trace(null); + root.processDecodes(facesContext); + assertEquals("/l1/l2/l3", CommandActionListenerTestImpl.trace()); + + // Restore the default action listener + facesContext.getApplication().setActionListener(oldDefaultActionListener); + } + + // Test event queuing and broadcasting (any phase listeners) + @Test + public void testEventsGeneric() { + + UICommand command = (UICommand) component; + command.setRendererType(null); + ActionEvent event = new ActionEvent(command); + + // Register three listeners + command.addActionListener(new ActionListenerTestImpl("AP0")); + command.addActionListener(new ActionListenerTestImpl("AP1")); + command.addActionListener(new ActionListenerTestImpl("AP2")); + + // Fire events and evaluate results + ActionListenerTestImpl.trace(null); + UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null); + root.getChildren().add(command); + command.queueEvent(event); + root.processDecodes(facesContext); + root.processValidators(facesContext); + root.processApplication(facesContext); + assertEquals("/AP0@INVOKE_APPLICATION 5/AP1@INVOKE_APPLICATION 5/AP2@INVOKE_APPLICATION 5", ActionListenerTestImpl.trace()); + + } + + // Test event queuing and broadcasting (mixed phase listeners) + @Test + public void testEventsMixed() { + + UICommand command = (UICommand) component; + command.setRendererType(null); + ActionEvent event = new ActionEvent(command); + + // Register three listeners + command.addActionListener(new ActionListenerTestImpl("ARV")); + command.addActionListener(new ActionListenerTestImpl("PV")); + command.addActionListener(new ActionListenerTestImpl("AP")); + + // Fire events and evaluate results + ActionListenerTestImpl.trace(null); + UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null); + root.getChildren().add(command); + command.queueEvent(event); + root.processDecodes(facesContext); + root.processValidators(facesContext); + root.processApplication(facesContext); + assertEquals("/ARV@INVOKE_APPLICATION 5/PV@INVOKE_APPLICATION 5/AP@INVOKE_APPLICATION 5", ActionListenerTestImpl.trace()); + + } + + // Test event queuing and broadcasting (mixed phase listeners), with + // immediate set. + @Test + public void testEventsMixedImmediate() { + + UICommand command = (UICommand) component; + command.setImmediate(true); + command.setRendererType(null); + ActionEvent event = new ActionEvent(command); + + // Register three listeners + command.addActionListener(new ActionListenerTestImpl("ARV")); + command.addActionListener(new ActionListenerTestImpl("PV")); + command.addActionListener(new ActionListenerTestImpl("AP")); + + // Fire events and evaluate results + ActionListenerTestImpl.trace(null); + UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null); + root.getChildren().add(command); + command.queueEvent(event); + root.processDecodes(facesContext); + root.processValidators(facesContext); + root.processApplication(facesContext); + assertEquals("/ARV@APPLY_REQUEST_VALUES 2/PV@APPLY_REQUEST_VALUES 2/AP@APPLY_REQUEST_VALUES 2", ActionListenerTestImpl.trace()); + + } + + // Test listener registration and deregistration + @Test + public void testListeners() { + + CommandTestImpl command = new CommandTestImpl(); + + command.addActionListener(new ActionListenerTestImpl("ARV0")); + command.addActionListener(new ActionListenerTestImpl("ARV1")); + command.addActionListener(new ActionListenerTestImpl("PV0")); + command.addActionListener(new ActionListenerTestImpl("PV1")); + command.addActionListener(new ActionListenerTestImpl("PV2")); + + ActionListener listeners[] = command.getActionListeners(); + assertEquals(5, listeners.length); + command.removeActionListener(listeners[2]); + listeners = command.getActionListeners(); + assertEquals(4, listeners.length); + + } + + // Test empty listener list + @Test + public void testEmptyListeners() { + + CommandTestImpl command = new CommandTestImpl(); + + // No listeners added, should be empty + ActionListener listeners[] = command.getActionListeners(); + assertEquals(0, listeners.length); + + } + + // Suppress lifecycle tests since we do not have a renderer + @Override + @Test + public void testLifecycleManagement() { + } + + @Test + public void testNestedCommands() { + UIViewRoot root = new UIViewRoot(); + UICommand c1 = new UICommand(); + UICommand c2 = new UICommand(); + root.getChildren().add(c1); + c2.setImmediate(true); + c1.getChildren().add(c2); + ActionEvent ae = new ActionEvent(c2); + c2.queueEvent(ae); + assertTrue(ae.getPhaseId().equals(PhaseId.APPLY_REQUEST_VALUES)); + + root = new UIViewRoot(); + c1 = new UICommand(); + c2 = new UICommand(); + root.getChildren().add(c1); + c1.setImmediate(true); + c2.setImmediate(false); + c1.getChildren().add(c2); + ae = new ActionEvent(c2); + c2.queueEvent(ae); + assertTrue(ae.getPhaseId().equals(PhaseId.INVOKE_APPLICATION)); + } + + @Test + public void testGetActionListeners() throws Exception { + UICommand command = (UICommand) component; + UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null); + root.getChildren().add(command); + + ActionListenerTestImpl ta1 = new ActionListenerTestImpl("ta1"), ta2 = new ActionListenerTestImpl("ta2"); + + command.addActionListener(ta1); + command.addActionListener(ta2); + ActionListener[] listeners = command.getActionListeners(); + assertEquals(2, listeners.length); + ActionListenerTestImpl[] taListeners = (ActionListenerTestImpl[]) command.getFacesListeners(ActionListenerTestImpl.class); + } + + // --------------------------------------------------------- Support Methods + + // Create a pristine component of the type to be used in state holder tests + @Override + protected UIComponent createComponent() { + UIComponent component = new UICommand(); + component.setRendererType(null); + return component; + } + + protected boolean listenersAreEqual(FacesContext context, UICommand comp1, UICommand comp2) { + ActionListener[] list1 = comp1.getActionListeners(); + ActionListener[] list2 = comp2.getActionListeners(); + // make sure they're either both null or both non-null + if (null == list1 && null != list2 || null != list1 && null == list2) { + return false; + } + if (null == list1) { + return true; + } + int j = 0, outerLen = list1.length; + boolean result = true; + if (outerLen != list2.length) { + return false; + } + for (j = 0; j < outerLen; j++) { + result = list1[j].equals(list2[j]); + if (!result) { + return false; + } + } + return true; + } + + // --------------------------------------------------------- Private Classes + // "Button" Renderer + class ButtonRenderer extends Renderer { + + @Override + public void decode(FacesContext context, UIComponent component) { + + if (context == null || component == null) { + throw new NullPointerException(); + } + + if (!(component instanceof ActionSource)) { + return; + } + String clientId = component.getClientId(context); + Map params = context.getExternalContext().getRequestParameterMap(); + if (params.containsKey(clientId)) { + component.queueEvent(new ActionEvent(component)); + } + } + + @Override + public void encodeBegin(FacesContext context, UIComponent component) throws IOException { + if (context == null || component == null) { + throw new NullPointerException(); + } + } + + @Override + public void encodeChildren(FacesContext context, UIComponent component) throws IOException { + if (context == null || component == null) { + throw new NullPointerException(); + } + } + + @Override + public void encodeEnd(FacesContext context, UIComponent component) throws IOException { + if (context == null || component == null) { + throw new NullPointerException(); + } + } + } +} diff --git a/impl/src/test/java/jakarta/faces/component/UIComponentBaseAttachedStateTestCase.java b/impl/src/test/java/jakarta/faces/component/UIComponentBaseAttachedStateTestCase.java new file mode 100644 index 0000000000..37d97bc789 --- /dev/null +++ b/impl/src/test/java/jakarta/faces/component/UIComponentBaseAttachedStateTestCase.java @@ -0,0 +1,322 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces.component; + +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.assertTrue; + +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.Stack; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.sun.faces.mock.MockExternalContext; +import com.sun.faces.mock.MockFacesContext; +import com.sun.faces.mock.MockHttpServletRequest; +import com.sun.faces.mock.MockHttpServletResponse; +import com.sun.faces.mock.MockLifecycle; +import com.sun.faces.mock.MockServletContext; + +import jakarta.faces.FactoryFinder; +import jakarta.faces.context.FacesContext; +import jakarta.faces.event.FacesListener; +import jakarta.faces.event.ValueChangeListener; + +public class UIComponentBaseAttachedStateTestCase { + + private MockFacesContext facesContext = null; + private MockServletContext servletContext; + private MockHttpServletRequest request; + private MockHttpServletResponse response; + + @BeforeEach + public void setUp() throws Exception { + facesContext = new MockFacesContext(); + + servletContext = new MockServletContext(); + servletContext.addInitParameter("appParamName", "appParamValue"); + servletContext.setAttribute("appScopeName", "appScopeValue"); + request = new MockHttpServletRequest(null); + request.setAttribute("reqScopeName", "reqScopeValue"); + response = new MockHttpServletResponse(); + + // Create something to stand-in as the InitFacesContext + new MockFacesContext(new MockExternalContext(servletContext, request, response), new MockLifecycle()); + + } + + @AfterEach + public void tearDown() throws Exception { + FactoryFinder.releaseFactories(); + Method reInitializeFactoryManager = FactoryFinder.class.getDeclaredMethod("reInitializeFactoryManager", (Class[]) null); + reInitializeFactoryManager.setAccessible(true); + reInitializeFactoryManager.invoke(null, (Object[]) null); + } + + + @Test + public void testAttachedObjectsSet() throws Exception { + Set attachedObjects = new HashSet<>(); + ValueChangeListener toAdd = new ValueChangeListenerTestImpl(); + attachedObjects.add(toAdd); + toAdd = new ValueChangeListenerTestImpl(); + attachedObjects.add(toAdd); + toAdd = new ValueChangeListenerTestImpl(); + attachedObjects.add(toAdd); + Object result = UIComponentBase.saveAttachedState(facesContext, attachedObjects); + @SuppressWarnings("unchecked") + Set returnedAttachedObjects = (Set) UIComponentBase + .restoreAttachedState(facesContext, result); + assertNotNull(returnedAttachedObjects); + } + + @Test + public void testAttachedObjectsStack() throws Exception { + Stack attachedObjects = new Stack<>(); + ValueChangeListener toAdd = new ValueChangeListenerTestImpl(); + attachedObjects.add(toAdd); + toAdd = new ValueChangeListenerTestImpl(); + attachedObjects.add(toAdd); + toAdd = new ValueChangeListenerTestImpl(); + attachedObjects.add(toAdd); + Object result = UIComponentBase.saveAttachedState(facesContext, attachedObjects); + @SuppressWarnings("unchecked") + Stack returnedAttachedObjects = (Stack) UIComponentBase + .restoreAttachedState(facesContext, result); + assertNotNull(returnedAttachedObjects); + } + + @Test + public void testAttachedObjectsMap() throws Exception { + Map attachedObjects = new HashMap<>(); + ValueChangeListener toAdd = new ValueChangeListenerTestImpl(); + attachedObjects.put("one", toAdd); + toAdd = new ValueChangeListenerTestImpl(); + attachedObjects.put("two", toAdd); + toAdd = new ValueChangeListenerTestImpl(); + attachedObjects.put("three", toAdd); + Object result = UIComponentBase.saveAttachedState(facesContext, attachedObjects); + @SuppressWarnings("unchecked") + Map returnedAttachedObjects = (Map) UIComponentBase + .restoreAttachedState(facesContext, result); + assertNotNull(returnedAttachedObjects); + } + + // Regression test for bug #907 + @SuppressWarnings("unchecked") + public void testAttachedObjectsCount() throws Exception { + Set returnedAttachedObjects = null, attachedObjects = new HashSet<>(); + ValueChangeListener toAdd = new ValueChangeListenerTestImpl(); + attachedObjects.add(toAdd); + toAdd = new ValueChangeListenerTestImpl(); + attachedObjects.add(toAdd); + toAdd = new ValueChangeListenerTestImpl(); + attachedObjects.add(toAdd); + Object result = UIComponentBase.saveAttachedState(facesContext, attachedObjects); + returnedAttachedObjects = (Set) UIComponentBase.restoreAttachedState(facesContext, result); + int firstSize = returnedAttachedObjects.size(); + returnedAttachedObjects = (Set) UIComponentBase.restoreAttachedState(facesContext, result); + int secondSize = returnedAttachedObjects.size(); + assertEquals(firstSize, secondSize); + } + + @Test + public void testFacesListenerState() { + UIComponent component = new UIOutput(); + TestFacesListener listener = new TestFacesListener(); + listener.setValue("initial"); + component.addFacesListener(listener); + component.markInitialState(); + assertTrue(component.initialStateMarked()); + assertTrue(listener.initialStateMarked()); + + Object state = component.saveState(facesContext); + assertNull(state); + + component = new UIOutput(); + listener = new TestFacesListener(); + component.addFacesListener(listener); + listener.setValue("initial"); + component.markInitialState(); + listener.setValue("newvalue"); + state = component.saveState(facesContext); + assertNotNull(state); + + // verify that state is applied to existing Listener instances. + component = new UIOutput(); + listener = new TestFacesListener(); + component.addFacesListener(listener); + listener.setValue("newinitial"); + component.restoreState(facesContext, state); + assertEquals("newvalue", listener.getValue()); + + // verify listeners are overwritten when using full state saving + component = new UIOutput(); + listener = new TestFacesListener(); + component.addFacesListener(listener); + listener.setValue("initial"); + state = component.saveState(facesContext); + assertNotNull(state); + listener.setValue("postsave"); + + component.restoreState(facesContext, state); + TestFacesListener l = (TestFacesListener) component.getFacesListeners(TestFacesListener.class)[0]; + assertTrue(l != listener); + assertEquals("initial", l.getValue()); + } + + @Test + public void testTransientListenersState() { + UIComponent output = new UIOutput(); + output.markInitialState(); + TestFacesListener l1 = new TestFacesListener(); + TestFacesListener l2 = new TestFacesListener(); + TestFacesListener l3 = new TestFacesListener(); + TestFacesListener l4 = new TestFacesListener(); + l1.setValue("l1"); + l2.setValue("l2"); + l3.setValue("l3"); + l4.setValue("l4"); + l2.setTransient(true); + l4.setTransient(true); + + output.addFacesListener(l1); + output.addFacesListener(l2); + output.addFacesListener(l3); + output.addFacesListener(l4); + + Object state = output.saveState(facesContext); + assertNotNull(state); + output = new UIOutput(); + output.restoreState(facesContext, state); + FacesListener[] listeners = output.getFacesListeners(TestFacesListener.class); + assertTrue(listeners.length == 2); + assertEquals("l1", ((TestFacesListener) listeners[0]).getValue()); + assertEquals("l3", ((TestFacesListener) listeners[1]).getValue()); + + output = new UIOutput(); + output.markInitialState(); + output.addFacesListener(l2); + state = output.saveState(facesContext); + assertNotNull(state); + output = new UIOutput(); + output.restoreState(facesContext, state); + listeners = output.getFacesListeners(TestFacesListener.class); + assertTrue(listeners.length == 0); + } + + @Test + public void testTransientListenersState2() { + UIComponent output = new UIOutput(); + TestFacesListener l1 = new TestFacesListener(); + TestFacesListener l2 = new TestFacesListener(); + TestFacesListener l3 = new TestFacesListener(); + TestFacesListener l4 = new TestFacesListener(); + l1.setValue("l1"); + l2.setValue("l2"); + l3.setValue("l3"); + l4.setValue("l4"); + l2.setTransient(true); + l4.setTransient(true); + + output.addFacesListener(l1); + output.addFacesListener(l2); + output.addFacesListener(l3); + output.addFacesListener(l4); + + Object state = output.saveState(facesContext); + assertNotNull(state); + output = new UIOutput(); + output.restoreState(facesContext, state); + FacesListener[] listeners = output.getFacesListeners(TestFacesListener.class); + assertTrue(listeners.length == 2); + assertEquals("l1", ((TestFacesListener) listeners[0]).getValue()); + assertEquals("l3", ((TestFacesListener) listeners[1]).getValue()); + + output = new UIOutput(); + output.addFacesListener(l2); + state = output.saveState(facesContext); + assertNotNull(state); + output = new UIOutput(); + output.restoreState(facesContext, state); + listeners = output.getFacesListeners(TestFacesListener.class); + assertTrue(listeners.length == 0); + } + + // ---------------------------------------------------------- Nested Classes + public static final class TestFacesListener implements FacesListener, PartialStateHolder { + + private boolean initialState; + private String value; + private boolean trans; + + public String getValue() { + return value; + } + + public void setValue(String value) { + clearInitialState(); + this.value = value; + } + + @Override + public void markInitialState() { + initialState = true; + } + + @Override + public boolean initialStateMarked() { + return initialState; + } + + @Override + public void clearInitialState() { + initialState = false; + } + + @Override + public Object saveState(FacesContext context) { + return !initialState ? new Object[] { value } : null; + } + + @Override + public void restoreState(FacesContext context, Object state) { + if (state != null) { + Object[] values = (Object[]) state; + value = (String) values[0]; + } + } + + @Override + public boolean isTransient() { + return trans; + } + + @Override + public void setTransient(boolean trans) { + this.trans = trans; + } + } +} diff --git a/impl/src/test/java/jakarta/faces/component/UIComponentBaseBehaviorTestCase.java b/impl/src/test/java/jakarta/faces/component/UIComponentBaseBehaviorTestCase.java new file mode 100644 index 0000000000..8fe128cc07 --- /dev/null +++ b/impl/src/test/java/jakarta/faces/component/UIComponentBaseBehaviorTestCase.java @@ -0,0 +1,258 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces.component; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Collections; +import java.util.EnumSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.junit.jupiter.api.Test; + +import jakarta.faces.component.behavior.ClientBehavior; +import jakarta.faces.component.behavior.ClientBehaviorContext; +import jakarta.faces.component.behavior.ClientBehaviorHint; +import jakarta.faces.component.behavior.ClientBehaviorHolder; +import jakarta.faces.context.FacesContext; +import jakarta.faces.event.BehaviorEvent; + +/** + *

+ * Test case for component behaviors. + *

+ * + * @since 2.0 + */ +public class UIComponentBaseBehaviorTestCase extends UIComponentTestCase { + + private static final String ONTEST = "ontest"; + private static final String ONCLICK = "onclick"; + private static final String ONCHANGE = "onchange"; + private static final String TEST_FAMILY = "jakarta.faces.Test"; + private static final Collection EVENTS = Set.of(ONTEST, ONCLICK, ONCHANGE); + + public static class BehaviorComponent extends UIComponentBase implements ClientBehaviorHolder { + + /* + * (non-Javadoc) + * + * @see jakarta.faces.component.UIComponent#getFamily() + */ + @Override + public String getFamily() { + return TEST_FAMILY; + } + + @Override + public Collection getEventNames() { + return EVENTS; + } + + @Override + public String getDefaultEventName() { + return ONTEST; + } + + } + + public static class TestBehavior implements ClientBehavior, Serializable { + + private static final long serialVersionUID = 1L; + + private static final Set HINTS = Collections.unmodifiableSet(EnumSet.of(ClientBehaviorHint.SUBMITTING)); + + private static int sequence = 0; + + private final int id; + + public TestBehavior() { + id = sequence++; + } + + public String getRendererType() { + return TEST_FAMILY; + } + + @Override + public Set getHints() { + return HINTS; + } + + @Override + public void broadcast(BehaviorEvent event) { + } + + @Override + public void decode(FacesContext context, UIComponent component) { + } + + @Override + public String getScript(ClientBehaviorContext bContext) { + return null; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + id; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + TestBehavior other = (TestBehavior) obj; + if (id != other.id) { + return false; + } + return true; + } + + @Override + public String toString() { + return "Behavior #" + id; + } + } + + /** + * Test method for {@link jakarta.faces.component.UIComponentBase#saveState(jakarta.faces.context.FacesContext)}. + */ + @Test + public void testSaveState() { + BehaviorComponent comp = new BehaviorComponent(); + // Cast component to the interface, to be sure about method definition. + ClientBehaviorHolder holder = comp; + TestBehavior behavior = new TestBehavior(); + holder.addClientBehavior(ONCLICK, behavior); + TestBehavior behavior2 = new TestBehavior(); + holder.addClientBehavior(ONCLICK, behavior2); + TestBehavior behavior3 = new TestBehavior(); + holder.addClientBehavior(ONCHANGE, behavior3); + Object state = comp.saveState(facesContext); + BehaviorComponent restoredComp = new BehaviorComponent(); + restoredComp.restoreState(facesContext, state); + Map> behaviors = restoredComp.getClientBehaviors(); + assertFalse(behaviors.isEmpty()); + assertTrue(behaviors.containsKey(ONCLICK)); + assertTrue(behaviors.containsKey(ONCHANGE)); + assertFalse(behaviors.containsKey(ONTEST)); + assertEquals(2, behaviors.entrySet().size()); + assertEquals(2, behaviors.size()); + assertEquals(2, behaviors.size()); + assertEquals(2, behaviors.get(ONCLICK).size()); + assertEquals(1, behaviors.get(ONCHANGE).size()); + assertEquals(behavior3, behaviors.get(ONCHANGE).get(0)); + assertEquals(behavior, behaviors.get(ONCLICK).get(0)); + assertEquals(behavior2, behaviors.get(ONCLICK).get(1)); + } + + @Test + public void testNonClientBehaviorHolder() throws Exception { + UIInput input = new UIInput(); + try { + input.addClientBehavior(ONTEST, new TestBehavior()); + } catch (IllegalStateException e) { + return; + } + assertFalse(true); + } + + /** + * Test method for + * {@link jakarta.faces.component.UIComponentBase#addClientBehavior(java.lang.String, jakarta.faces.component.behavior.ClientBehavior)}. + */ + @Test + public void testAddBehavior() { + BehaviorComponent comp = new BehaviorComponent(); + // Cast component to the interface, to be sure about method definition. + ClientBehaviorHolder holder = comp; + holder.addClientBehavior(ONCLICK, new TestBehavior()); + assertTrue(holder.getClientBehaviors().containsKey(ONCLICK)); + holder.addClientBehavior(ONCLICK, new TestBehavior()); + assertTrue(holder.getClientBehaviors().containsKey(ONCLICK)); + holder.addClientBehavior(ONCHANGE, new TestBehavior()); + assertTrue(holder.getClientBehaviors().containsKey(ONCHANGE)); + holder.addClientBehavior("foo", new TestBehavior()); + assertFalse(holder.getClientBehaviors().containsKey("foo")); + } + + /** + * Test method for {@link jakarta.faces.component.UIComponentBase#getEventNames()}. + */ + @Test + public void testGetEventNames() { + BehaviorComponent comp = new BehaviorComponent(); + ClientBehaviorHolder holder = comp; + assertEquals(EVENTS, holder.getEventNames()); + } + + /** + * Test method for {@link jakarta.faces.component.UIComponentBase#getClientBehaviors()}. + */ + @Test + public void testGetBehaviors() { + BehaviorComponent comp = new BehaviorComponent(); + // Cast component to the interface, to be sure about method definition. + ClientBehaviorHolder holder = comp; + Map> behaviors = holder.getClientBehaviors(); + assertTrue(behaviors.isEmpty()); + assertFalse(behaviors.containsKey(ONCLICK)); + assertFalse(behaviors.containsValue(new TestBehavior())); + assertEquals(0, behaviors.entrySet().size()); + holder.addClientBehavior(ONCLICK, new TestBehavior()); + holder.addClientBehavior(ONCLICK, new TestBehavior()); + holder.addClientBehavior(ONCHANGE, new TestBehavior()); + behaviors = holder.getClientBehaviors(); + assertFalse(behaviors.isEmpty()); + assertTrue(behaviors.containsKey(ONCLICK)); + assertTrue(behaviors.containsKey(ONCHANGE)); + assertFalse(behaviors.containsKey(ONTEST)); + assertEquals(2, behaviors.entrySet().size()); + assertEquals(2, behaviors.size()); + assertEquals(2, behaviors.size()); + assertEquals(2, behaviors.get(ONCLICK).size()); + assertEquals(1, behaviors.get(ONCHANGE).size()); + } + + /** + * Test method for {@link jakarta.faces.component.UIComponentBase#getDefaultEventName()}. + */ + @Test + public void testGetDefaultEventName() { + BehaviorComponent comp = new BehaviorComponent(); + // Cast component to the interface, to be sure about method definition. + ClientBehaviorHolder holder = comp; + assertEquals(ONTEST, holder.getDefaultEventName()); + } + +} diff --git a/impl/src/test/java/jakarta/faces/component/UIComponentBaseTestCase.java b/impl/src/test/java/jakarta/faces/component/UIComponentBaseTestCase.java new file mode 100644 index 0000000000..b07172cd55 --- /dev/null +++ b/impl/src/test/java/jakarta/faces/component/UIComponentBaseTestCase.java @@ -0,0 +1,1565 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces.component; + +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.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import jakarta.faces.FacesException; +import jakarta.faces.context.FacesContext; +import jakarta.faces.event.AbortProcessingException; +import jakarta.faces.event.ComponentSystemEvent; +import jakarta.faces.event.ComponentSystemEventListener; +import jakarta.faces.event.PostAddToViewEvent; +import jakarta.faces.event.PostConstructViewMapEvent; +import jakarta.faces.event.PreRenderComponentEvent; +import jakarta.faces.event.SystemEvent; +import jakarta.faces.event.SystemEventListener; + +/** + *

+ * Base unit tests for all {@link UIComponentBase} subclasses. + *

+ */ +public class UIComponentBaseTestCase extends UIComponentTestCase { + + // ---------------------------------------------------- Overall Test Methods + // Set up instance variables required by this test case. + @Override + @BeforeEach + public void setUp() throws Exception { + + // Set up the component under test + super.setUp(); + component = new ComponentTestImpl(expectedId); + + } + + // Tear down instance variables required by ths test case + @Override + @AfterEach + public void tearDown() throws Exception { + externalContext.setRequestParameterMap(null); + + super.tearDown(); + + } + + // ------------------------------------------------- Individual Test Methods + // Test lifecycle management methods + @Test + public void testLifecycleManagement() { + + checkLifecycleParentRendered(); + checkLifecycleParentUnrendered(); + checkLifecycleSelfRendered(); + checkLifecycleSelfUnrendered(); + + } + + @Test + public void testComponentToFromEL2() throws Exception { + + final FacesContext ctx = facesContext; + ComponentTestImpl c = new ComponentTestImpl(); + ComponentTestImpl c2 = new ComponentTestImpl(); + UIComponent eeo = new UIComponentOverrideEncodeEnd(); + ComponentTestImpl c3 = new ComponentTestImpl(); + UIComponent ebo = new UIComponentOverrideEncodeBegin(); + + c.encodeBegin(ctx); + assertEquals(UIComponent.getCurrentComponent(ctx), c); + c2.encodeBegin(ctx); + assertEquals(UIComponent.getCurrentComponent(ctx), c2); + c2.encodeEnd(ctx); + assertEquals(UIComponent.getCurrentComponent(ctx), c); + c.encodeEnd(ctx); + assertNull(UIComponent.getCurrentComponent(ctx)); + + // sanity check for the case where a component overrides + // encodeBegin() without calling super or pushComponentToEL + c.encodeBegin(ctx); + c2.encodeBegin(ctx); + ebo.encodeBegin(ctx); + assertEquals(UIComponent.getCurrentComponent(ctx), c2); + ebo.encodeEnd(ctx); // if the component wasn't pushed + // it shouldn't be popped. + assertEquals(UIComponent.getCurrentComponent(ctx), c2); + c2.encodeEnd(ctx); + assertEquals(UIComponent.getCurrentComponent(ctx), c); + c.encodeEnd(ctx); + assertNull(UIComponent.getCurrentComponent(ctx)); + + // sanity check for the case where a component overrides + // encodeEnd() without calling super or popComponentFromEL + c.encodeBegin(ctx); + c2.encodeBegin(ctx); + eeo.encodeBegin(ctx); + assertEquals(UIComponent.getCurrentComponent(ctx), eeo); + eeo.encodeEnd(ctx); + // this is ugly. Because of a component not doing calling + // super() or popComponentFromEL, c2 won't be visible + // as the current component. + assertEquals(UIComponent.getCurrentComponent(ctx), eeo); + c2.encodeEnd(ctx); + assertEquals(UIComponent.getCurrentComponent(ctx), c); + c.encodeEnd(ctx); + assertNull(UIComponent.getCurrentComponent(ctx)); + + UIComponent eeo2 = new UIComponentOverrideEncodeEnd(); + c.encodeBegin(ctx); + c2.encodeBegin(ctx); + eeo.encodeBegin(ctx); + assertEquals(UIComponent.getCurrentComponent(ctx), eeo); + c3.encodeBegin(ctx); + assertEquals(UIComponent.getCurrentComponent(ctx), c3); + eeo2.encodeBegin(ctx); + assertEquals(UIComponent.getCurrentComponent(ctx), eeo2); + eeo2.encodeEnd(ctx); + // this is ugly. + assertEquals(UIComponent.getCurrentComponent(ctx), eeo2); + c3.encodeEnd(ctx); + assertEquals(UIComponent.getCurrentComponent(ctx), eeo); + eeo.encodeEnd(ctx); + // this is ugly. + assertEquals(UIComponent.getCurrentComponent(ctx), eeo); + c2.encodeEnd(ctx); + assertEquals(UIComponent.getCurrentComponent(ctx), c); + c.encodeEnd(ctx); + assertNull(UIComponent.getCurrentComponent(ctx)); + + } + + @Test + public void testEncodeChildren() throws Exception { + ComponentTestImpl.trace(null); + UIComponent comp1 = new ComponentTestImpl("one"); + UIComponent comp2 = new ComponentTestImpl("two"); + UIComponent comp3 = new ComponentTestImpl("three"); + UIComponent comp4 = new ComponentTestImpl("four"); + + comp1.getChildren().add(comp2); + comp1.getChildren().add(comp3); + comp1.getChildren().add(comp4); + + comp1.encodeChildren(facesContext); + System.out.println("Actual: " + ComponentTestImpl.trace()); + System.out.println("Expected: " + "/eC-one/eB-two/eE-two/eB-three/eE-three/eB-four/eE-four"); + assertEquals("/eC-one/eB-two/eE-two/eB-three/eE-three/eB-four/eE-four", ComponentTestImpl.trace()); + + } + + // Test recursive adding and removing child trees with ids + @Test + public void testChildrenRecursive() { + + // Create the components we will need + UIComponent testComponent = new ComponentTestImpl(); + UIComponent child1 = new ComponentTestImpl("child1"); + UIComponent child2 = new ComponentTestImpl("child2"); + UIComponent child3 = new ComponentTestImpl("child3"); + + // Prepare ancestry tree before adding to base component + child1.getChildren().add(child2); + child2.getChildren().add(child3); + + // Verify that no child ids are visible yet + assertNull(testComponent.findComponent("child1")); + assertNull(testComponent.findComponent("child2")); + assertNull(testComponent.findComponent("child3")); + + // Add the entire tree + testComponent.getChildren().add(child1); + + // Verify that all named children get added + assertEquals(child1, testComponent.findComponent("child1")); + assertEquals(child2, testComponent.findComponent("child2")); + assertEquals(child3, testComponent.findComponent("child3")); + + // Remove the entire tree + testComponent.getChildren().remove(child1); + + // Verify that child ids are no longer visible + assertNull(testComponent.findComponent("child1")); + assertNull(testComponent.findComponent("child2")); + assertNull(testComponent.findComponent("child3")); + + } + + @Test + public void testChildrenAndFacetsWithNullGetParent() throws Exception { + ComponentTestImpl child = new ComponentTestImpl() { + @Override + public UIComponent getParent() { + return null; + } + }; + component.getChildren().add(child); + assertNull(component.getChildren().get(0).getParent()); + ComponentTestImpl facet = new ComponentTestImpl() { + @Override + public UIComponent getParent() { + return null; + } + }; + component.getFacets().put("nullParent", facet); + assertNull(component.getFacets().get("nullParent").getParent()); + } + + // Test reconnecting a child or facet to a different component + @Test + public void testComponentReconnect() { + + UIComponent parent1 = new ComponentTestImpl(); + UIComponent parent2 = new ComponentTestImpl(); + + // Reconnect an existing child as a child + checkChildCount(parent1, 0); + checkChildCount(parent2, 0); + parent1.getChildren().add(component); + checkChildCount(parent1, 1); + checkChildCount(parent2, 0); + checkChildPresent(parent1, component, 0); + parent2.getChildren().add(component); + checkChildCount(parent1, 0); + checkChildCount(parent2, 1); + checkChildPresent(parent2, component, 0); + parent2.getChildren().clear(); + checkChildCount(parent1, 0); + checkChildCount(parent2, 0); + + // Reconnect an existing child as a facet + checkChildCount(parent1, 0); + checkFacetCount(parent2, 0); + parent1.getChildren().add(component); + checkChildCount(parent1, 1); + checkFacetCount(parent2, 0); + checkChildPresent(parent1, component, 0); + parent2.getFacets().put("facet", component); + checkChildCount(parent1, 0); + checkFacetCount(parent2, 1); + checkFacetPresent(parent2, "facet", component); + parent2.getFacets().clear(); + checkChildCount(parent1, 0); + checkFacetCount(parent2, 0); + + // Reconnect an existing facet as a child + checkFacetCount(parent1, 0); + checkChildCount(parent2, 0); + parent1.getFacets().put("facet", component); + checkFacetCount(parent1, 1); + checkChildCount(parent2, 0); + checkFacetPresent(parent1, "facet", component); + parent2.getChildren().add(component); + checkFacetCount(parent1, 0); + checkChildCount(parent2, 1); + checkChildPresent(parent2, component, 0); + parent2.getChildren().clear(); + checkFacetCount(parent1, 0); + checkChildCount(parent2, 0); + + } + + // Test removing components from our naming container. + @Test + public void testComponentRemoval() { + + UIComponent testComponent = new ComponentTestImpl(); + UIComponent child1 = new ComponentTestImpl("child1"); + UIComponent child2 = new ComponentTestImpl("child2"); + UIComponent child3 = new ComponentTestImpl("child3"); + UIComponent child = null; + + // adding children to naming container + testComponent.getChildren().add(child1); + testComponent.getChildren().add(child2); + testComponent.getChildren().add(child3); + + // make sure children are stored in naming container properly + Iterator kidItr = null; + + kidItr = testComponent.getFacetsAndChildren(); + + child = kidItr.next(); + assertTrue(child.equals(child1)); + + child = kidItr.next(); + assertTrue(child.equals(child2)); + + child = kidItr.next(); + assertTrue(child.equals(child3)); + + // make sure child is removed from component and naming container + // pass in a component to remove method + testComponent.getChildren().remove(child1); + + kidItr = testComponent.getFacetsAndChildren(); + + child = kidItr.next(); + assertTrue(child.equals(child2)); + + child = kidItr.next(); + assertTrue(child.equals(child3)); + + // make sure child is removed from component and naming container + // pass an index to remove method + testComponent.getChildren().remove(0); + + kidItr = testComponent.getFacetsAndChildren(); + + child = kidItr.next(); + assertTrue(child.equals(child3)); + + // make sure child is removed from component and naming container + // remove all children + testComponent.getChildren().clear(); + + kidItr = testComponent.getFacetsAndChildren(); + assertTrue(!kidItr.hasNext()); + } + + @Test + public void testStateHolder2() throws Exception { + + UIComponent c = new UIComponentListener(); + c.subscribeToEvent(PostAddToViewEvent.class, c); + Object state = c.saveState(facesContext); + c = new UIComponentListener(); + c.pushComponentToEL(facesContext, c); + c.restoreState(facesContext, state); + c.popComponentFromEL(facesContext); + assertTrue(c.getListenersForEventClass(PostAddToViewEvent.class).size() == 1); + + } + + @Test + public void testAttributesThatAreSetStateHolder() throws Exception { + ComponentTestImpl c = new ComponentTestImpl(); + c.getAttributes().put("attr1", "value1"); + c.markInitialState(); + c.getAttributes().put("attr2", "value2"); + assertEquals(Arrays.asList("attr1", "attr2"), c.getAttributes().get("jakarta.faces.component.UIComponentBase.attributesThatAreSet")); + + Object state = c.saveState(facesContext); + c = new ComponentTestImpl(); + c.pushComponentToEL(facesContext, c); + c.restoreState(facesContext, state); + c.popComponentFromEL(facesContext); + assertEquals(Arrays.asList("attr1", "attr2"), c.getAttributes().get("jakarta.faces.component.UIComponentBase.attributesThatAreSet")); + } + + @Test + public void testValueExpressions() throws Exception { + + UIComponentBase test = (UIComponentBase) component; + + // generic attributes + request.setAttribute("foo", "bar"); + test.getAttributes().clear(); + assertNull(test.getAttributes().get("baz")); + test.setValueExpression("baz", application.getExpressionFactory().createValueExpression(facesContext.getELContext(), "#{foo}", String.class)); + assertEquals("bar", test.getAttributes().get("baz")); + test.getAttributes().put("baz", "bop"); + assertEquals("bop", test.getAttributes().get("baz")); + test.getAttributes().remove("baz"); + assertEquals("bar", test.getAttributes().get("baz")); + test.setValueExpression("baz", null); + assertNull(test.getAttributes().get("baz")); + + // "id" property + try { + test.setValueExpression("id", application.getExpressionFactory().createValueExpression(facesContext.getELContext(), "#{foo}", String.class)); + fail("Should have thrown IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // Expected response + } + + // "parent" property + try { + test.setValueExpression("parent", + application.getExpressionFactory().createValueExpression(facesContext.getELContext(), "#{foo}", UIComponent.class)); + fail("Should have thrown IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // Expected response + } + + // "rendered" property + request.setAttribute("foo", Boolean.FALSE); + test.setValueExpression("rendered", null); + boolean initial = test.isRendered(); + if (initial) { + request.setAttribute("foo", Boolean.FALSE); + } else { + request.setAttribute("foo", Boolean.TRUE); + } + test.setValueExpression("rendered", application.getExpressionFactory().createValueExpression(facesContext.getELContext(), "#{foo}", Boolean.class)); + assertEquals(!initial, test.isRendered()); + test.setRendered(initial); + assertEquals(initial, test.isRendered()); + assertNotNull(test.getValueExpression("rendered")); + + // "rendererType" property + request.setAttribute("foo", "bar"); + test.setRendererType(null); + assertNull(test.getRendererType()); + test.setValueExpression("rendererType", application.getExpressionFactory().createValueExpression(facesContext.getELContext(), "#{foo}", String.class)); + assertNotNull(test.getValueExpression("rendererType")); + assertEquals("bar", test.getRendererType()); + test.setRendererType("baz"); + assertEquals("baz", test.getRendererType()); + test.setRendererType(null); + assertEquals("bar", test.getRendererType()); + test.setValueExpression("rendererType", null); + assertNull(test.getValueExpression("rendererType")); + assertNull(test.getRendererType()); + + } + + // --------------------------------------------------------- support Methods + // Check that the attributes on the specified components are equal + protected void checkAttributes(UIComponent comp1, UIComponent comp2) { + assertEquals(comp1.getAttributes(), comp2.getAttributes()); + } + + // Check that the specified components are equal + protected void checkComponents(UIComponent comp1, UIComponent comp2) { + checkAttributes(comp1, comp2); + // checkFacets(comp1, comp2); // Not saved and restored by component + checkProperties(comp1, comp2); + } + + // Check lifecycle processing when parent "rendered" property is "true" + private void checkLifecycleParentRendered() { + + // Put our component under test in a tree under a UIViewRoot + component.getAttributes().clear(); + component.getChildren().clear(); + component.getFacets().clear(); + component.setRendered(true); + UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null); + UIPanel panel = new UIPanel(); + panel.setRendered(true); + root.getChildren().add(panel); + panel.getChildren().add(component); + + // Establish a view with multiple facets and children + UIComponent facet1 = new ComponentTestImpl("f1"); + UIComponent facet2 = new ComponentTestImpl("f2"); + UIComponent facet3 = new ComponentTestImpl("f3"); + component.getFacets().put("f1", facet1); + component.getFacets().put("f2", facet2); + component.getFacets().put("f3", facet3); + checkFacetCount(component, 3); + UIComponent child1 = new ComponentTestImpl("c1"); + UIComponent child2 = new ComponentTestImpl("c2"); + UIComponent child3 = new ComponentTestImpl("c3"); + component.getChildren().add(child1); + component.getChildren().add(child2); + component.getChildren().add(child3); + checkChildCount(component, 3); + UIComponent child2a = new ComponentTestImpl("c2a"); + UIComponent child2b = new ComponentTestImpl("c2b"); + child2.getChildren().add(child2a); + child2.getChildren().add(child2b); + checkChildCount(child2, 2); + + // Enqueue a single FacesEvent for each component + component.queueEvent(new EventTestImpl(component)); + component.queueEvent(new EventTestImpl(facet1)); + component.queueEvent(new EventTestImpl(facet2)); + component.queueEvent(new EventTestImpl(facet3)); + component.queueEvent(new EventTestImpl(child1)); + component.queueEvent(new EventTestImpl(child2)); + component.queueEvent(new EventTestImpl(child3)); + component.queueEvent(new EventTestImpl(child2a)); + component.queueEvent(new EventTestImpl(child2b)); + + // Test processDecodes() + ComponentTestImpl.trace(null); + component.processDecodes(facesContext); + assertEquals(lifecycleTrace("pD", "d"), ComponentTestImpl.trace()); + + // Test processValidators() + ComponentTestImpl.trace(null); + component.processValidators(facesContext); + assertEquals(lifecycleTrace("pV", null), ComponentTestImpl.trace()); + + // Test processUpdates() + ComponentTestImpl.trace(null); + component.processUpdates(facesContext); + assertEquals(lifecycleTrace("pU", null), ComponentTestImpl.trace()); + + } + + // Check lifecycle processing when parent "rendered" property is "false" + private void checkLifecycleParentUnrendered() { + + // Put our component under test in a tree under a UIViewRoot + component.getAttributes().clear(); + component.getChildren().clear(); + component.getFacets().clear(); + component.setRendered(true); + UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null); + UIPanel panel = new UIPanel(); + panel.setRendered(false); + root.getChildren().add(panel); + panel.getChildren().add(component); + + // Establish a view with multiple facets and children + UIComponent facet1 = new ComponentTestImpl("f1"); + UIComponent facet2 = new ComponentTestImpl("f2"); + UIComponent facet3 = new ComponentTestImpl("f3"); + component.getFacets().put("f1", facet1); + component.getFacets().put("f2", facet2); + component.getFacets().put("f3", facet3); + checkFacetCount(component, 3); + UIComponent child1 = new ComponentTestImpl("c1"); + UIComponent child2 = new ComponentTestImpl("c2"); + UIComponent child3 = new ComponentTestImpl("c3"); + component.getChildren().add(child1); + component.getChildren().add(child2); + component.getChildren().add(child3); + checkChildCount(component, 3); + UIComponent child2a = new ComponentTestImpl("c2a"); + UIComponent child2b = new ComponentTestImpl("c2b"); + child2.getChildren().add(child2a); + child2.getChildren().add(child2b); + checkChildCount(child2, 2); + + // Enqueue a single FacesEvent for each component + component.queueEvent(new EventTestImpl(component)); + component.queueEvent(new EventTestImpl(facet1)); + component.queueEvent(new EventTestImpl(facet2)); + component.queueEvent(new EventTestImpl(facet3)); + component.queueEvent(new EventTestImpl(child1)); + component.queueEvent(new EventTestImpl(child2)); + component.queueEvent(new EventTestImpl(child3)); + component.queueEvent(new EventTestImpl(child2a)); + component.queueEvent(new EventTestImpl(child2b)); + + // Test processDecodes() + ComponentTestImpl.trace(null); + component.processDecodes(facesContext); + assertEquals(lifecycleTrace("pD", "d"), ComponentTestImpl.trace()); + + // Test processValidators() + ComponentTestImpl.trace(null); + component.processValidators(facesContext); + assertEquals(lifecycleTrace("pV", null), ComponentTestImpl.trace()); + + // Test processUpdates() + ComponentTestImpl.trace(null); + component.processUpdates(facesContext); + assertEquals(lifecycleTrace("pU", null), ComponentTestImpl.trace()); + + } + + // Check lifecycle processing when our "rendered" property is "true" + private void checkLifecycleSelfRendered() { + + // Put our component under test in a tree under a UIViewRoot + component.getAttributes().clear(); + component.getChildren().clear(); + component.getFacets().clear(); + component.setRendered(true); + UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null); + root.getChildren().add(component); + + // Establish a view with multiple facets and children + UIComponent facet1 = new ComponentTestImpl("f1"); + UIComponent facet2 = new ComponentTestImpl("f2"); + UIComponent facet3 = new ComponentTestImpl("f3"); + component.getFacets().put("f1", facet1); + component.getFacets().put("f2", facet2); + component.getFacets().put("f3", facet3); + checkFacetCount(component, 3); + UIComponent child1 = new ComponentTestImpl("c1"); + UIComponent child2 = new ComponentTestImpl("c2"); + UIComponent child3 = new ComponentTestImpl("c3"); + component.getChildren().add(child1); + component.getChildren().add(child2); + component.getChildren().add(child3); + checkChildCount(component, 3); + UIComponent child2a = new ComponentTestImpl("c2a"); + UIComponent child2b = new ComponentTestImpl("c2b"); + child2.getChildren().add(child2a); + child2.getChildren().add(child2b); + checkChildCount(child2, 2); + + // Enqueue a single FacesEvent for each component + component.queueEvent(new EventTestImpl(component)); + component.queueEvent(new EventTestImpl(facet1)); + component.queueEvent(new EventTestImpl(facet2)); + component.queueEvent(new EventTestImpl(facet3)); + component.queueEvent(new EventTestImpl(child1)); + component.queueEvent(new EventTestImpl(child2)); + component.queueEvent(new EventTestImpl(child3)); + component.queueEvent(new EventTestImpl(child2a)); + component.queueEvent(new EventTestImpl(child2b)); + + // Test processDecodes() + ComponentTestImpl.trace(null); + component.processDecodes(facesContext); + assertEquals(lifecycleTrace("pD", "d"), ComponentTestImpl.trace()); + + // Test processValidators() + ComponentTestImpl.trace(null); + component.processValidators(facesContext); + assertEquals(lifecycleTrace("pV", null), ComponentTestImpl.trace()); + + // Test processUpdates() + ComponentTestImpl.trace(null); + component.processUpdates(facesContext); + assertEquals(lifecycleTrace("pU", null), ComponentTestImpl.trace()); + + } + + // Check lifecycle processing when our "rendered" property is "false" + private void checkLifecycleSelfUnrendered() { + + // Put our component under test in a tree under a UIViewRoot + component.getAttributes().clear(); + component.getChildren().clear(); + component.getFacets().clear(); + component.setRendered(false); + UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null); + root.getChildren().add(component); + + // Establish a view with multiple facets and children + UIComponent facet1 = new ComponentTestImpl("f1"); + UIComponent facet2 = new ComponentTestImpl("f2"); + UIComponent facet3 = new ComponentTestImpl("f3"); + component.getFacets().put("f1", facet1); + component.getFacets().put("f2", facet2); + component.getFacets().put("f3", facet3); + checkFacetCount(component, 3); + UIComponent child1 = new ComponentTestImpl("c1"); + UIComponent child2 = new ComponentTestImpl("c2"); + UIComponent child3 = new ComponentTestImpl("c3"); + component.getChildren().add(child1); + component.getChildren().add(child2); + component.getChildren().add(child3); + checkChildCount(component, 3); + UIComponent child2a = new ComponentTestImpl("c2a"); + UIComponent child2b = new ComponentTestImpl("c2b"); + child2.getChildren().add(child2a); + child2.getChildren().add(child2b); + checkChildCount(child2, 2); + + // Enqueue a single FacesEvent for each component + component.queueEvent(new EventTestImpl(component)); + component.queueEvent(new EventTestImpl(facet1)); + component.queueEvent(new EventTestImpl(facet2)); + component.queueEvent(new EventTestImpl(facet3)); + component.queueEvent(new EventTestImpl(child1)); + component.queueEvent(new EventTestImpl(child2)); + component.queueEvent(new EventTestImpl(child3)); + component.queueEvent(new EventTestImpl(child2a)); + component.queueEvent(new EventTestImpl(child2b)); + + // Test processDecodes() + ComponentTestImpl.trace(null); + component.processDecodes(facesContext); + assertEquals(lifecycleTrace("pD", "d"), ComponentTestImpl.trace()); + + // Test processValidators() + ComponentTestImpl.trace(null); + component.processValidators(facesContext); + assertEquals(lifecycleTrace("pV", null), ComponentTestImpl.trace()); + + // Test processUpdates() + ComponentTestImpl.trace(null); + component.processUpdates(facesContext); + assertEquals(lifecycleTrace("pU", null), ComponentTestImpl.trace()); + + } + + // Check that the properties on the specified components are equal + protected void checkProperties(UIComponent comp1, UIComponent comp2) { + assertEquals(comp1.getClientId(facesContext), comp2.getClientId(facesContext)); + assertEquals(comp1.getId(), comp2.getId()); + assertEquals(comp1.isRendered(), comp2.isRendered()); + assertEquals(comp1.getRendererType(), comp2.getRendererType()); + assertEquals(comp1.getRendersChildren(), comp2.getRendersChildren()); + } + + protected void checkComponentListeners(UIComponent control, UIComponent toValidate) { + + List lc = control.getListenersForEventClass(PostAddToViewEvent.class); + List tvl = toValidate.getListenersForEventClass(PostAddToViewEvent.class); + List lc2 = control.getListenersForEventClass(PostConstructViewMapEvent.class); + List tvl2 = toValidate.getListenersForEventClass(PostConstructViewMapEvent.class); + assertTrue(lc.size() == tvl.size()); + assertTrue(lc2.size() == tvl2.size()); + + } + + // Create a pristine component of the type to be used in state holder tests + protected UIComponent createComponent() { + return new ComponentTestImpl(); + } + + /** + * Construct and return a lifecycle method call trace for the specified method names. + * + * @param lmethod Name of the lifecycle method under test + * @param cmethod Name of the component method that corresponds + * @return + */ + protected String lifecycleTrace(String lmethod, String cmethod) { + StringBuilder sb = new StringBuilder(); + lifecycleTrace(lmethod, cmethod, component, sb); + return sb.toString(); + } + + protected void lifecycleTrace(String lmethod, String cmethod, UIComponent component, StringBuilder sb) { + + // Append the call for this lifecycle method + String id = component.getId(); + sb.append('/').append(lmethod).append('-').append(id); + if (!component.isRendered()) { + return; + } + + // Append the calls for each facet + Iterator names = component.getFacets().keySet().iterator(); + while (names.hasNext()) { + String name = names.next(); + sb.append('/').append(lmethod).append('-').append(name); + if (cmethod != null && component.getFacets().get(name).isRendered()) { + sb.append('/').append(cmethod).append('-').append(name); + } + } + + // Append the calls for each child + Iterator kids = component.getChildren().iterator(); + while (kids.hasNext()) { + UIComponent kid = kids.next(); + lifecycleTrace(lmethod, cmethod, kid, sb); + } + + // Append the call for this component's component method + if (cmethod != null && component.isRendered()) { + sb.append('/').append(cmethod).append('-').append(id); + } + + } + + @Test + public void testGetFacetsAndChildren() { + + UIComponent testComponent = new ComponentTestImpl(); + UIComponent child1 = new ComponentTestImpl("child1"); + UIComponent child2 = new ComponentTestImpl("child2"); + UIComponent child3 = new ComponentTestImpl("child3"); + UIComponent facet1 = new ComponentTestImpl("facet1"); + UIComponent facet2 = new ComponentTestImpl("facet2"); + UIComponent facet3 = new ComponentTestImpl("facet3"); + + testComponent.getChildren().add(child1); + testComponent.getChildren().add(child2); + testComponent.getChildren().add(child3); + testComponent.getFacets().put("facet1", facet1); + testComponent.getFacets().put("facet2", facet2); + testComponent.getFacets().put("facet3", facet3); + + Iterator iter = testComponent.getFacetsAndChildren(); + Object cur = null; + boolean exceptionThrown = false; + assertTrue(iter.hasNext()); + + try { + iter.remove(); + } catch (UnsupportedOperationException e) { + exceptionThrown = true; + } + assertTrue(exceptionThrown); + + // facets are returned in an undefined order. + cur = iter.next(); + assertTrue(cur == facet1 || cur == facet2 || cur == facet3); + cur = iter.next(); + assertTrue(cur == facet1 || cur == facet2 || cur == facet3); + cur = iter.next(); + assertTrue(cur == facet1 || cur == facet2 || cur == facet3); + + // followed by components, in the order added + cur = iter.next(); + assertTrue(cur == child1); + cur = iter.next(); + assertTrue(cur == child2); + cur = iter.next(); + assertTrue(cur == child3); + + assertTrue(!iter.hasNext()); + + } + + private Object foundComponent = null; + + /** + *

+ * Build a tree with the following layout. + *

+ *
+     * root: id: root
+     *
+     *   form1: id: form1
+     *
+     *     panel1: id: panel
+     *
+     *       input1: id: input1
+     *
+     *       input2: id: input2
+     *
+     *   form2: id: form2
+     *
+     *     panel2: id: panel
+     *
+     *       input3: id: input1
+     *
+     *       input4: id: input2
+     * 
+ * + * @return a Map. The key is the string before the first : in the above layout. The value is the + * component instance. Note that the keys in the map are not the ids. + */ + private Map setupInvokeOnComponentTree() { + UIViewRoot root = new UIViewRoot(); + UIForm form1 = new UIForm(); + UIPanel panel1 = new UIPanel(); + UIInput input1 = new UIInput(); + UIInput input2 = new UIInput(); + UIForm form2 = new UIForm(); + UIPanel panel2 = new UIPanel(); + UIInput input3 = new UIInput(); + UIInput input4 = new UIInput(); + + root.setId("root"); + form1.setId("form1"); + panel1.setId("panel"); + input1.setId("input1"); + input2.setId("input2"); + + form2.setId("form2"); + panel2.setId("panel"); + input3.setId("input1"); + input4.setId("input2"); + + root.getChildren().add(form1); + form1.getChildren().add(panel1); + panel1.getChildren().add(input1); + panel1.getChildren().add(input2); + + root.getChildren().add(form2); + form2.getChildren().add(panel2); + panel2.getChildren().add(input3); + panel2.getChildren().add(input4); + Map result = new HashMap<>(); + result.put("root", root); + result.put("form1", form1); + result.put("panel1", panel1); + result.put("input1", input1); + result.put("input2", input2); + result.put("form2", form2); + result.put("panel2", panel2); + result.put("input3", input3); + result.put("input4", input4); + + return result; + } + + @Test + public void testInvokeOnComponentPositive() throws Exception { + + Map tree = setupInvokeOnComponentTree(); + + UIViewRoot root = (UIViewRoot) tree.get("root"); + UIInput input1 = (UIInput) tree.get("input1"); + + foundComponent = null; + boolean result = false; + + assertNull(UIComponent.getCurrentComponent(facesContext)); + + result = root.invokeOnComponent(facesContext, input1.getClientId(facesContext), new ContextCallback() { + @Override + public void invokeContextCallback(FacesContext context, UIComponent component) { + assertEquals(UIComponent.getCurrentComponent(context), component); + foundComponent = component; + } + }); + assertEquals(input1, foundComponent); + assertTrue(result); + assertNull(UIComponent.getCurrentComponent(facesContext)); + + } + + @Test + public void testInvokeOnComponentNegative() throws Exception { + Map tree = setupInvokeOnComponentTree(); + + UIViewRoot root = (UIViewRoot) tree.get("root"); + UIInput input4 = (UIInput) tree.get("input4"); + + foundComponent = null; + boolean result = false; + boolean exceptionThrown = false; + + // Negative case 0, null pointers + exceptionThrown = false; + FacesContext nullContext = null; + ContextCallback nullCallback = null; + try { + root.invokeOnComponent(nullContext, "form:input7", nullCallback); + } catch (NullPointerException npe) { + exceptionThrown = true; + } + assertTrue(exceptionThrown); + + exceptionThrown = false; + try { + root.invokeOnComponent(facesContext, null, nullCallback); + } catch (NullPointerException npe) { + exceptionThrown = true; + } + assertTrue(exceptionThrown); + + exceptionThrown = false; + try { + root.invokeOnComponent(nullContext, null, nullCallback); + } catch (NullPointerException npe) { + exceptionThrown = true; + } + assertTrue(exceptionThrown); + + // Negative case 1, not found component. + result = root.invokeOnComponent(facesContext, "form:input7", new ContextCallback() { + @Override + public void invokeContextCallback(FacesContext context, UIComponent component) { + foundComponent = component; + } + }); + assertNull(foundComponent); + assertTrue(!result); + + // Negative case 2A, callback throws exception with found component + foundComponent = null; + result = false; + exceptionThrown = false; + try { + result = root.invokeOnComponent(facesContext, "form2:input2", new ContextCallback() { + @Override + public void invokeContextCallback(FacesContext context, UIComponent component) { + foundComponent = component; + // When else am I going to get the chance to throw this exception? + throw new IllegalStateException(); + } + }); + } catch (FacesException e) { + assertTrue(e.getCause() instanceof IllegalStateException); + exceptionThrown = true; + } + assertTrue(exceptionThrown); + assertEquals(foundComponent, input4); + assertTrue(!result); + + // Negative case 2B, callback throws exception with not found component + foundComponent = null; + result = false; + exceptionThrown = false; + try { + result = root.invokeOnComponent(facesContext, "form2:input6", new ContextCallback() { + @Override + public void invokeContextCallback(FacesContext context, UIComponent component) { + foundComponent = component; + // When else am I going to get the chance to throw this exception? + throw new IllegalStateException(); + } + }); + } catch (FacesException e) { + assertTrue(e.getCause() instanceof IllegalStateException); + exceptionThrown = true; + } + assertTrue(!exceptionThrown); + assertNull(foundComponent); + assertTrue(!result); + + } + + @Test + public void testInvokeOnComponentWithPrependId() throws Exception { + Map tree = setupInvokeOnComponentTree(); + + UIViewRoot root = (UIViewRoot) tree.get("root"); + UIForm truePrependIdForm = (UIForm) tree.get("form1"); + UIForm falsePrependIdForm = (UIForm) tree.get("form2"); + UIInput truePrependIdInput = (UIInput) tree.get("input2"); + UIInput falsePrependIdInput = (UIInput) tree.get("input3"); + + truePrependIdForm.setPrependId(true); + falsePrependIdForm.setPrependId(false); + + foundComponent = null; + boolean result = false; + boolean exceptionThrown = false; + + // Case 1, positive find with prependId == true + result = root.invokeOnComponent(facesContext, "form1:input2", new ContextCallback() { + @Override + public void invokeContextCallback(FacesContext context, UIComponent component) { + foundComponent = component; + } + }); + assertEquals(truePrependIdInput, foundComponent); + assertTrue(result); + + // Case 2, negative find with prependId == true + foundComponent = null; + result = false; + + result = root.invokeOnComponent(facesContext, "form9:input5", new ContextCallback() { + @Override + public void invokeContextCallback(FacesContext context, UIComponent component) { + foundComponent = component; + } + }); + assertNull(foundComponent); + assertTrue(!result); + + // Case 3, exception positive find with prependId == true + foundComponent = null; + result = false; + exceptionThrown = false; + try { + + result = root.invokeOnComponent(facesContext, "form1:input2", new ContextCallback() { + @Override + public void invokeContextCallback(FacesContext context, UIComponent component) { + foundComponent = component; + throw new IllegalStateException(); + } + }); + } catch (FacesException e) { + assertTrue(e.getCause() instanceof IllegalStateException); + exceptionThrown = true; + } + assertEquals(truePrependIdInput, foundComponent); + assertTrue(!result); + assertTrue(exceptionThrown); + + // Case 4, exception negative find with prependId == true + foundComponent = null; + result = false; + exceptionThrown = false; + try { + + result = root.invokeOnComponent(facesContext, "formFozzy:inputKermit", new ContextCallback() { + @Override + public void invokeContextCallback(FacesContext context, UIComponent component) { + foundComponent = component; + throw new IllegalStateException(); + } + }); + } catch (FacesException e) { + assertTrue(e.getCause() instanceof IllegalStateException); + exceptionThrown = true; + } + assertNull(foundComponent); + assertTrue(!result); + assertTrue(!exceptionThrown); + + // Case 5, positive find with prependId == false + result = root.invokeOnComponent(facesContext, "input1", new ContextCallback() { + @Override + public void invokeContextCallback(FacesContext context, UIComponent component) { + foundComponent = component; + } + }); + assertEquals(falsePrependIdInput, foundComponent); + assertTrue(result); + + // Case 6, negative find with prependId == false + foundComponent = null; + result = false; + + result = root.invokeOnComponent(facesContext, "input99", new ContextCallback() { + @Override + public void invokeContextCallback(FacesContext context, UIComponent component) { + foundComponent = component; + } + }); + assertNull(foundComponent); + assertTrue(!result); + + // Case 3, exception positive find with prependId == false + foundComponent = null; + result = false; + exceptionThrown = false; + try { + + result = root.invokeOnComponent(facesContext, "input1", new ContextCallback() { + @Override + public void invokeContextCallback(FacesContext context, UIComponent component) { + foundComponent = component; + throw new IllegalStateException(); + } + }); + } catch (FacesException e) { + assertTrue(e.getCause() instanceof IllegalStateException); + exceptionThrown = true; + } + assertEquals(falsePrependIdInput, foundComponent); + assertTrue(!result); + assertTrue(exceptionThrown); + + // Case 4, exception negative find with prependId == false + foundComponent = null; + result = false; + exceptionThrown = false; + try { + + result = root.invokeOnComponent(facesContext, "inputKermit", new ContextCallback() { + @Override + public void invokeContextCallback(FacesContext context, UIComponent component) { + foundComponent = component; + throw new IllegalStateException(); + } + }); + } catch (FacesException e) { + assertTrue(e.getCause() instanceof IllegalStateException); + exceptionThrown = true; + } + assertNull(foundComponent); + assertTrue(!result); + assertTrue(!exceptionThrown); + + } + + @Test + public void testChildrenListAfterAddPublish() { + + Listener listener = new Listener(); + application.subscribeToEvent(PostAddToViewEvent.class, listener); + + UIComponent c1 = createComponent(); + c1.setInView(true); + UIComponent c2 = createComponent(); + c2.setInView(true); + UIComponent c3 = createComponent(); + c3.setInView(true); + + c1.getChildren().add(c2); + SystemEvent e = listener.getEvent(); + assertNotNull(e); + assertTrue(e.getSource() == c2); + assertTrue(((UIComponent) e.getSource()).getParent() == c1); + listener.reset(); + c2.getChildren().add(c3); + e = listener.getEvent(); + assertNotNull(e); + assertTrue(e.getSource() == c3); + assertTrue(((UIComponent) e.getSource()).getParent() == c2); + + // ensure events are re-published if the event is added + listener.reset(); + c2.getChildren().remove(c3); + c1.getChildren().add(c3); + e = listener.getEvent(); + assertNotNull(e); + assertTrue(e.getSource() == c3); + assertTrue(((UIComponent) e.getSource()).getParent() == c1); + + application.unsubscribeFromEvent(PostAddToViewEvent.class, listener); + + } + + @Test + public void testFacetMapAfterAddViewPublish() { + + QueueingListener listener = new QueueingListener(); + application.subscribeToEvent(PostAddToViewEvent.class, listener); + + UIComponent c1 = createComponent(); + UIComponent c2 = createComponent(); + UIComponent c3 = createComponent(); + + List e = listener.getEvents(); + Map facets = c1.getFacets(); + facets.put("c2", c2); + assertEquals(0, e.size()); + + UIViewRoot root = new UIViewRoot(); + root.getChildren().add(c1); + assertEquals(2, e.size()); + assertTrue(e.get(0).getSource() == c1); + assertTrue(e.get(1).getSource() == c2); + + // remove c1 from the root and add c3 as a facet to c1 - no events should be + // published + e.clear(); + root.getChildren().remove(c1); + facets = c1.getFacets(); + facets.put("c3", c3); + assertEquals(0, e.size()); + + // reorganize the facet structure to ensure nested facets work + facets.remove("c3"); + c2.getFacets().put("c3", c3); + root.getChildren().add(c1); + assertEquals(3, e.size()); + assertTrue(e.get(0).getSource() == c1); + assertTrue(e.get(1).getSource() == c2); + assertTrue(e.get(2).getSource() == c3); + + e.clear(); + // ensure clear() method disconnects the facets from the view + facets.clear(); + c2.getFacets().remove("c3"); + c2.getFacets().put("c3", c3); + assertEquals(0, e.size()); + + application.unsubscribeFromEvent(PostAddToViewEvent.class, listener); + + } + + @Test + public void testChildrenListAfterAddViewPublish() { + + QueueingListener listener = new QueueingListener(); + application.subscribeToEvent(PostAddToViewEvent.class, listener); + + UIComponent c1 = createComponent(); + UIComponent c2 = createComponent(); + UIComponent c3 = createComponent(); + UIComponent c4 = createComponent(); + c1.getChildren().add(c2); + List e = listener.getEvents(); + assertTrue(e.isEmpty()); + c2.getChildren().add(c3); + assertTrue(e.isEmpty()); + UIViewRoot root = new UIViewRoot(); + root.getChildren().add(c1); + + // sub-tree has been added to the view. Ensure that subsequent additions + // to that sub-tree cause the PostAddToViewEvent to fire. + c2.getChildren().add(c4); + assertEquals(4, e.size()); + + UIComponent[] comps = { c1, c2, c3, c4 }; + for (int i = 0; i < comps.length; i++) { + assertTrue(e.get(i).getSource() == comps[i], "Index " + i + " invalid"); + } + + // remove c1 and it's children from the subview, then remove and + // re-add one of the children in the sub-tree. No event should + // be fired + e.clear(); + root.getChildren().remove(c1); + c2.getChildren().remove(c4); + c2.getChildren().add(c4); + assertEquals(0, e.size()); + + c2.getChildren().remove(c4); + c1.getChildren().add(c4); + assertEquals(0, e.size()); + + // re-wire c1 as a child of root and ensure all children get re-notified + root.getChildren().add(c1); + assertEquals(4, e.size()); + + for (int i = 0; i < comps.length; i++) { + assertTrue(e.get(i).getSource() == comps[i], "Index " + i + " invalid"); + } + + // validate clearing c1's children (effectively removing them from the view + // will result in no events being fired of components are added to any of + // the disconnected children. + // At this point in the test, c2 and c4 are children of c1, and c3 + // is a child of c2. + c1.getChildren().clear(); + UIComponent temp = createComponent(); + e.clear(); + c2.getChildren().add(temp); + assertEquals(0, e.size()); + c2.getChildren().remove(temp); + c3.getChildren().add(temp); + assertEquals(0, e.size()); + c3.getChildren().remove(temp); + c4.getChildren().add(temp); + assertEquals(0, e.size()); + c4.getChildren().remove(temp); + + // now add c2 and c4 as children of c1. This should cause three + // events to fire + c1.getChildren().add(c2); + c1.getChildren().add(c4); + assertEquals(3, e.size()); + + UIComponent[] comps2 = { c2, c3, c4 }; + for (int i = 0; i < comps2.length; i++) { + assertTrue(e.get(i).getSource() == comps2[i], "Index " + i + " invalid"); + } + + // validate add(int, UIComponent) fires events + e.clear(); + c1.getChildren().remove(c4); + c1.getChildren().add(0, c4); + + assertTrue(c1.getChildren().get(0) == c4); + assertTrue(c1.getChildren().get(1) == c2); + assertEquals(1, e.size()); + assertTrue(e.get(0).getSource() == c4); + + // validate addAll(Collection) fires events + e.clear(); + c1.getChildren().clear(); + List children = new ArrayList<>(2); + Collections.addAll(children, c2, c4); + c1.getChildren().addAll(children); + assertTrue(c1.getChildren().get(0) == c2); + assertTrue(c1.getChildren().get(1) == c4); + assertEquals(3, e.size()); + assertTrue(e.get(0).getSource() == c2); + assertTrue(e.get(2).getSource() == c4); + + // validate addAll(int, Collection) fires events + e.clear(); + children = new ArrayList<>(2); + UIComponent t1 = createComponent(); + UIComponent t2 = createComponent(); + Collections.addAll(children, t1, t2); + c1.getChildren().addAll(0, children); + assertTrue(c1.getChildren().get(0) == t1); + assertTrue(c1.getChildren().get(1) == t2); + assertTrue(c1.getChildren().get(2) == c2); + assertTrue(c1.getChildren().get(3) == c4); + assertEquals(2, e.size()); + assertTrue(e.get(0).getSource() == t1); + assertTrue(e.get(1).getSource() == t2); + + // validate retainAll(Collection properly disconnects + // the components from the view such that events aren't fired + // if children are added to them + e.clear(); + List retained = new ArrayList<>(2); + Collections.addAll(retained, c2, c4); + c1.getChildren().retainAll(retained); + assertTrue(c1.getChildren().size() == 2); + assertTrue(c1.getChildren().get(0) == c2); + assertTrue(c1.getChildren().get(1) == c4); + t1.getChildren().add(t2); + assertEquals(0, e.size()); + + // test set(int, UIComponent) properly fires an event if the parent + // the component is being added to is wired to the view + e.clear(); + c1.getChildren().set(0, t1); + assertTrue(c1.getChildren().size() == 2); + assertTrue(c1.getChildren().get(0) == t1); + assertTrue(c1.getChildren().get(1) == c4); + assertEquals(2, e.size()); + assertTrue(e.get(0).getSource() == t1); + assertTrue(e.get(1).getSource() == t2); + + // c2 was removed by the set operation, so ensure it's marked as + // having been removed from the view by ensuring events aren't fired. + e.clear(); + UIComponent t3 = createComponent(); + c2.getChildren().add(t3); + assertEquals(0, e.size()); + + application.unsubscribeFromEvent(PostAddToViewEvent.class, listener); + + // validate Iterator.remove() over c1's children correctly disconnects + // the children from the view + for (Iterator i = c1.getChildren().iterator(); i.hasNext();) { + i.next(); + i.remove(); + } + + // at this point, t1 and c4 should be disconnected meaning adding children + // to t1, t2, or c4 should result in no events being fired + e.clear(); + t1.getChildren().add(temp); + assertEquals(0, e.size()); + t1.getChildren().remove(temp); + t2.getChildren().add(temp); + assertEquals(0, e.size()); + t2.getChildren().remove(temp); + c4.getChildren().add(temp); + assertEquals(0, e.size()); + c4.getChildren().remove(temp); + + } + + @Test + public void testEncodeBeginPublish() throws Exception { + + Listener listener = new Listener(); + application.subscribeToEvent(PreRenderComponentEvent.class, listener); + + UIComponent c1 = createComponent(); + c1.encodeBegin(facesContext); + SystemEvent e = listener.getEvent(); + assertNotNull(e); + assertTrue(e.getSource() == c1); + listener.reset(); + c1.encodeChildren(facesContext); + assertNull(listener.getEvent()); + c1.encodeEnd(facesContext); + assertNull(listener.getEvent()); + + application.unsubscribeFromEvent(PreRenderComponentEvent.class, listener); + + } + + // --------------------------------------------------------- Private Classes + public static final class Listener implements SystemEventListener { + + private SystemEvent event; + + @Override + public void processEvent(SystemEvent event) throws AbortProcessingException { + this.event = event; + } + + @Override + public boolean isListenerForSource(Object source) { + return source instanceof UIComponent; + } + + public SystemEvent getEvent() { + return event; + } + + public void reset() { + event = null; + } + } + + public static final class QueueingListener implements SystemEventListener { + + private List events = new ArrayList<>(); + + @Override + public void processEvent(SystemEvent event) throws AbortProcessingException { + events.add(event); + } + + @Override + public boolean isListenerForSource(Object source) { + return source instanceof UIComponent; + } + + public List getEvents() { + return events; + } + + public void reset() { + events.clear(); + } + } + + public static final class ComponentListener implements ComponentSystemEventListener { + + @Override + public void processEvent(ComponentSystemEvent event) throws AbortProcessingException { + + } + } + + public static final class UIComponentListener extends UIComponentBase implements ComponentSystemEventListener { + + @Override + public String getFamily() { + return "family"; + } + + @Override + public void processEvent(ComponentSystemEvent event) throws AbortProcessingException { + } + + } + + public static final class UIComponentOverrideEncodeBegin extends UIComponentBase { + + @Override + public String getFamily() { + return "UIComponentOverrideEncodeBegin"; + } + + @Override + public void encodeBegin(FacesContext context) throws IOException { + // no-op + } + + } + + public static final class UIComponentOverrideEncodeEnd extends UIComponentBase { + + @Override + public String getFamily() { + return "UIComponentOverrideEncodeEnd"; + } + + @Override + public void encodeEnd(FacesContext context) throws IOException { + // no-op + } + } + + public static final class CustomAbortProcessingException extends AbortProcessingException { + + /** + * + */ + private static final long serialVersionUID = 1L; + + public CustomAbortProcessingException() { + } + + public CustomAbortProcessingException(String message) { + super(message); + } + + public CustomAbortProcessingException(Throwable cause) { + super(cause); + } + + public CustomAbortProcessingException(String message, Throwable cause) { + super(message, cause); + } + } +} diff --git a/impl/src/test/java/jakarta/faces/component/UIComponentTestCase.java b/impl/src/test/java/jakarta/faces/component/UIComponentTestCase.java new file mode 100644 index 0000000000..0e3eb7f9f9 --- /dev/null +++ b/impl/src/test/java/jakarta/faces/component/UIComponentTestCase.java @@ -0,0 +1,1883 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces.component; + +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.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Set; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.sun.faces.junit.JUnitFacesTestCaseBase; +import com.sun.faces.mock.MockRenderKit; + +import jakarta.faces.FactoryFinder; +import jakarta.faces.context.FacesContext; +import jakarta.faces.event.AbortProcessingException; +import jakarta.faces.event.ComponentSystemEvent; +import jakarta.faces.event.ComponentSystemEventListener; +import jakarta.faces.event.PostValidateEvent; +import jakarta.faces.event.PreValidateEvent; +import jakarta.faces.render.RenderKit; +import jakarta.faces.render.RenderKitFactory; +import jakarta.faces.validator.Validator; +import jakarta.faces.validator.ValidatorException; + +/** + *

+ * Base unit tests for all {@link UIComponent} implementation classes. + *

+ */ +public class UIComponentTestCase extends JUnitFacesTestCaseBase { + + // ------------------------------------------------------ Instance Variables + // The component to be tested + protected UIComponent component = null; + + // The set of attribute names expected on a pristine component instance + protected String expectedAttributes[] = null; + + // The expected component family on a pristine component instance + protected String expectedFamily = null; + + // The expected component identifier on a pristine component instance + protected String expectedId = null; + + // The expected rendered on a pristine component instance + protected boolean expectedRendered = true; + + // The expected rendererType on a pristine component instance + protected String expectedRendererType = null; + + // The expected rendersChildren on a pristine component instance + protected boolean expectedRendersChildren = false; + + private Map.Entry bogusEntry = new Map.Entry<>() { + @Override + public boolean equals(Object r) { + return false; + } + + @Override + public String getKey() { + return "key"; + } + + @Override + public int hashCode() { + return 0; + } + + @Override + public UIComponent getValue() { + return null; + } + + @Override + public UIComponent setValue(UIComponent value) { + return null; + } + + }; + + // ---------------------------------------------------- Overall Test Methods + // Set up instance variables required by this test case. + @Override + @BeforeEach + public void setUp() throws Exception { + super.setUp(); + + expectedAttributes = new String[0]; + expectedFamily = "Test"; + expectedId = "test"; + expectedRendered = true; + expectedRendererType = null; + expectedRendersChildren = false; + + UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null); + root.setViewId("/viewId"); + facesContext.setViewRoot(root); + RenderKitFactory renderKitFactory = (RenderKitFactory) FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY); + RenderKit renderKit = new MockRenderKit(); + try { + renderKitFactory.addRenderKit(RenderKitFactory.HTML_BASIC_RENDER_KIT, renderKit); + } catch (IllegalArgumentException e) { + } + + component = new ComponentTestImpl(expectedId); + } + + // Tear down instance variables required by this test case. + @Override + @AfterEach + public void tearDown() throws Exception { + + component = null; + expectedAttributes = null; + expectedFamily = null; + expectedId = null; + expectedRendered = true; + expectedRendererType = null; + expectedRendersChildren = false; + super.tearDown(); + + } + + // ------------------------------------------------- Individual Test Methods + @Test + public void testValidationEvents() { + Listener prelistener = new Listener(); + Listener postlistener = new Listener(); + List ldata = new ArrayList<>(); + ldata.add("one"); + UIViewRoot root = new UIViewRoot(); + root.setId("root"); + root.subscribeToEvent(PreValidateEvent.class, prelistener); + root.subscribeToEvent(PostValidateEvent.class, postlistener); + UIOutput out = new UIOutput(); + out.setId("out"); + out.subscribeToEvent(PreValidateEvent.class, prelistener); + out.subscribeToEvent(PostValidateEvent.class, postlistener); + root.getChildren().add(out); + UIForm f = new UIForm(); + f.setSubmitted(true); + f.setId("form"); + f.subscribeToEvent(PreValidateEvent.class, prelistener); + f.subscribeToEvent(PostValidateEvent.class, postlistener); + root.getChildren().add(f); + UIData data = new UIData(); + data.setId("data"); + data.subscribeToEvent(PreValidateEvent.class, prelistener); + data.subscribeToEvent(PostValidateEvent.class, postlistener); + data.setValue(ldata); + UIColumn c = new UIColumn(); + c.setId("column"); + c.subscribeToEvent(PreValidateEvent.class, prelistener); + c.subscribeToEvent(PostValidateEvent.class, postlistener); + UIInput in = new UIInput(); + in.setId("in"); + in.subscribeToEvent(PreValidateEvent.class, prelistener); + in.subscribeToEvent(PostValidateEvent.class, postlistener); + in.addValidator(new ValidationSignal()); + c.getChildren().add(in); + data.getChildren().add(c); + f.getChildren().add(data); + data.setRowIndex(0); + UIComponent col = data.getChildren().get(0); + ((UIInput) col.getChildren().get(0)).setSubmittedValue("hello"); + data.setRowIndex(-1); + root.processValidators(facesContext); + assertEquals("root/out/form/data/in/", prelistener.getResults()); + assertEquals("out/*/in/data/form/root/", postlistener.getResults()); + + } + + // Test behavior of Map returned by getAttributes() + @Test + public void testAttributesMap() { + + // Initialize some attributes + Map attributes = component.getAttributes(); + attributes.put("foo", "bar"); + attributes.put("baz", "bop"); + + // Test containsKey() + assertTrue(attributes.containsKey("foo")); + assertTrue(attributes.containsKey("baz")); + assertTrue(!attributes.containsKey("bar")); + assertTrue(!attributes.containsKey("bop")); + assertTrue(!attributes.containsKey("id")); // Property name + assertTrue(!attributes.containsKey("parent")); // Property name + + // Test get() + assertEquals("bar", attributes.get("foo")); + assertEquals("bop", attributes.get("baz")); + assertNull(attributes.get("bar")); + assertNull(attributes.get("bop")); + component.setId("oldvalue"); + assertEquals("oldvalue", attributes.get("id")); // Property + component.setRendered(false); + assertTrue(!((Boolean) attributes.get("rendered")).booleanValue()); + component.setRendered(true); + assertTrue(((Boolean) attributes.get("rendered")).booleanValue()); + + // Test put() + try { + attributes.put(null, "dummy"); + fail("Should have thrown NullPointerException"); + } catch (NullPointerException e) { + // Expected result + } + try { + attributes.put("rendersChildren", null); // Primitive property + fail("Should have thrown IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // Expected result + } + try { + attributes.put("rendersChildren", Boolean.TRUE); // Write-only + fail("Should have thrown IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // Expected result + } + attributes.put("id", "newvalue"); + assertEquals("newvalue", attributes.get("id")); + assertEquals("newvalue", component.getId()); + attributes.put("rendered", Boolean.TRUE); + assertTrue(component.isRendered()); + attributes.put("rendered", Boolean.FALSE); + assertTrue(!component.isRendered()); + + // Test remove() + attributes.remove("baz"); + assertTrue(!attributes.containsKey("baz")); + assertNull(attributes.get("baz")); + try { + attributes.remove("id"); + fail("Should have thrown IllegalArgumentException()"); + } catch (IllegalArgumentException e) { + // Expected result + } + + } + + // Negative tests on attribute methods + @Test + public void testAttributesNegative() { + + // getAttributes().get() - null + try { + component.getAttributes().get(null); + fail("should have thrown NullPointerException"); + } catch (NullPointerException e) { + // Expected result + } + + // getAttributes().put() - null + try { + component.getAttributes().put(null, "bar"); + fail("should have thrown NullPointerException"); + } catch (NullPointerException e) { + // Expected result + } + + } + + // Positive tests on attribute methods + @Test + public void testAttributesPositive() { + + checkAttributeCount(component, expectedAttributes.length); + checkAttributeMissing(component, "foo"); + checkAttributeMissing(component, "baz"); + + component.getAttributes().put("foo", "bar"); + checkAttributeCount(component, expectedAttributes.length + 1); + checkAttributePresent(component, "foo", "bar"); + checkAttributeMissing(component, "baz"); + + component.getAttributes().put("baz", "bop"); + checkAttributeCount(component, expectedAttributes.length + 2); + checkAttributePresent(component, "foo", "bar"); + checkAttributePresent(component, "baz", "bop"); + + component.getAttributes().put("baz", "boo"); + checkAttributeCount(component, expectedAttributes.length + 2); + checkAttributePresent(component, "foo", "bar"); + checkAttributePresent(component, "baz", "boo"); + + component.getAttributes().remove("foo"); + checkAttributeCount(component, expectedAttributes.length + 1); + checkAttributeMissing(component, "foo"); + checkAttributePresent(component, "baz", "boo"); + + } + + // Test attribute-property transparency + @Test + public void testAttributesTransparency() { + + assertEquals(component.getChildren(), component.getAttributes().get("children")); + + assertEquals(component.getFacets(), component.getAttributes().get("facets")); + + assertEquals(component.getId(), component.getAttributes().get("id")); + + assertEquals(component.getParent(), component.getAttributes().get("parent")); + + assertEquals(component.isRendered(), ((Boolean) component.getAttributes().get("rendered")).booleanValue()); + component.setRendered(false); + assertEquals(Boolean.FALSE, component.getAttributes().get("rendered")); + component.setRendered(true); + assertEquals(Boolean.TRUE, component.getAttributes().get("rendered")); + component.getAttributes().put("rendered", Boolean.FALSE); + assertTrue(!component.isRendered()); + component.getAttributes().put("rendered", Boolean.TRUE); + assertTrue(component.isRendered()); + + component.setRendererType("foo"); + assertEquals("foo", component.getAttributes().get("rendererType")); + component.setRendererType(null); + assertNull(component.getAttributes().get("rendererType")); + component.getAttributes().put("rendererType", "bar"); + assertEquals("bar", component.getRendererType()); + component.getAttributes().put("rendererType", null); + assertNull(component.getRendererType()); + + assertEquals(component.getRendersChildren(), ((Boolean) component.getAttributes().get("rendersChildren")).booleanValue()); + + } + + // Test getChildren().iterator() + @Test + public void testChildrenIterator() { + + Iterator kids; + + // Construct components we will need + UIComponent comp0 = new ComponentTestImpl(null); + UIComponent comp1 = new ComponentTestImpl("comp1"); + UIComponent comp2 = new ComponentTestImpl("comp2"); + UIComponent comp3 = new ComponentTestImpl("comp3"); + UIComponent comp4 = new ComponentTestImpl("comp4"); + UIComponent comp5 = new ComponentTestImpl("comp5"); + List comps = new ArrayList<>(); + comps.add(comp0); + comps.add(comp1); + comps.add(comp2); + comps.add(comp3); + comps.add(comp4); + comps.add(comp5); + + // Test hasNext() and next() + component.getChildren().clear(); + component.getChildren().addAll(comps); + kids = component.getChildren().iterator(); + assertTrue(kids.hasNext()); + assertEquals(comp0, kids.next()); + assertEquals(comp1, kids.next()); + assertEquals(comp2, kids.next()); + assertEquals(comp3, kids.next()); + assertEquals(comp4, kids.next()); + assertEquals(comp5, kids.next()); + assertTrue(!kids.hasNext()); + + // Test remove() + component.getChildren().clear(); + component.getChildren().addAll(comps); + kids = component.getChildren().iterator(); + while (kids.hasNext()) { + UIComponent kid = kids.next(); + if (kid == comp2 || kid == comp4) { + kids.remove(); + } + } + kids = component.getChildren().iterator(); + assertTrue(kids.hasNext()); + assertEquals(comp0, kids.next()); + assertEquals(comp1, kids.next()); + assertEquals(comp3, kids.next()); + assertEquals(comp5, kids.next()); + assertTrue(!kids.hasNext()); + + } + + // Test getChildren().listIterator() + @Test + public void testChildrenListIterator() { + + ListIterator kids; + + // Construct components we will need + UIComponent comp0 = new ComponentTestImpl(null); + UIComponent comp1 = new ComponentTestImpl("comp1"); + UIComponent comp2 = new ComponentTestImpl("comp2"); + UIComponent comp3 = new ComponentTestImpl("comp3"); + UIComponent comp4 = new ComponentTestImpl("comp4"); + UIComponent comp5 = new ComponentTestImpl("comp5"); + UIComponent comp6 = new ComponentTestImpl("comp6"); + List comps = new ArrayList<>(); + comps.add(comp0); + comps.add(comp1); + comps.add(comp2); + comps.add(comp3); + comps.add(comp4); + comps.add(comp5); + + // Test hasNext(), next(), and nextIndex() + component.getChildren().clear(); + component.getChildren().addAll(comps); + kids = component.getChildren().listIterator(); + assertTrue(kids.hasNext()); + assertEquals(0, kids.nextIndex()); + assertEquals(comp0, kids.next()); + assertEquals(1, kids.nextIndex()); + assertEquals(comp1, kids.next()); + assertEquals(2, kids.nextIndex()); + assertEquals(comp2, kids.next()); + assertEquals(3, kids.nextIndex()); + assertEquals(comp3, kids.next()); + assertEquals(4, kids.nextIndex()); + assertEquals(comp4, kids.next()); + assertEquals(5, kids.nextIndex()); + assertEquals(comp5, kids.next()); + assertEquals(6, kids.nextIndex()); + assertTrue(!kids.hasNext()); + + // Test hasPrevious(), previous(), and previousIndex() + assertTrue(kids.hasPrevious()); + assertEquals(5, kids.previousIndex()); + assertEquals(comp5, kids.previous()); + assertEquals(4, kids.previousIndex()); + assertEquals(comp4, kids.previous()); + assertEquals(3, kids.previousIndex()); + assertEquals(comp3, kids.previous()); + assertEquals(2, kids.previousIndex()); + assertEquals(comp2, kids.previous()); + assertEquals(1, kids.previousIndex()); + assertEquals(comp1, kids.previous()); + assertEquals(0, kids.previousIndex()); + assertEquals(comp0, kids.previous()); + assertEquals(-1, kids.previousIndex()); + assertTrue(!kids.hasPrevious()); + + // Test remove() + component.getChildren().clear(); + component.getChildren().addAll(comps); + kids = component.getChildren().listIterator(); + while (kids.hasNext()) { + UIComponent kid = kids.next(); + if (kid == comp2 || kid == comp4) { + kids.remove(); + } + } + kids = component.getChildren().listIterator(); + assertTrue(kids.hasNext()); + assertEquals(comp0, kids.next()); + assertEquals(comp1, kids.next()); + assertEquals(comp3, kids.next()); + assertEquals(comp5, kids.next()); + assertTrue(!kids.hasNext()); + + // Test set() + component.getChildren().clear(); + component.getChildren().addAll(comps); + kids = component.getChildren().listIterator(); + while (kids.hasNext()) { + UIComponent kid = kids.next(); + if (kid == comp2) { + kids.set(comp6); + } + } + kids = component.getChildren().listIterator(); + assertTrue(kids.hasNext()); + assertEquals(0, kids.nextIndex()); + assertEquals(comp0, kids.next()); + assertEquals(1, kids.nextIndex()); + assertEquals(comp1, kids.next()); + assertEquals(2, kids.nextIndex()); + assertEquals(comp6, kids.next()); + assertEquals(3, kids.nextIndex()); + assertEquals(comp3, kids.next()); + assertEquals(4, kids.nextIndex()); + assertEquals(comp4, kids.next()); + assertEquals(5, kids.nextIndex()); + assertEquals(comp5, kids.next()); + assertEquals(6, kids.nextIndex()); + assertTrue(!kids.hasNext()); + + // Test add() + component.getChildren().clear(); + component.getChildren().addAll(comps); + kids = component.getChildren().listIterator(); + while (kids.hasNext()) { + UIComponent kid = kids.next(); + if (kid == comp2) { + kids.add(comp6); + } + } + kids = component.getChildren().listIterator(); + assertTrue(kids.hasNext()); + assertEquals(0, kids.nextIndex()); + assertEquals(comp0, kids.next()); + assertEquals(1, kids.nextIndex()); + assertEquals(comp1, kids.next()); + assertEquals(2, kids.nextIndex()); + assertEquals(comp2, kids.next()); + assertEquals(3, kids.nextIndex()); + assertEquals(comp6, kids.next()); + assertEquals(4, kids.nextIndex()); + assertEquals(comp3, kids.next()); + assertEquals(5, kids.nextIndex()); + assertEquals(comp4, kids.next()); + assertEquals(6, kids.nextIndex()); + assertEquals(comp5, kids.next()); + assertEquals(7, kids.nextIndex()); + assertTrue(!kids.hasNext()); + + // Test listIterator(int) + component.getChildren().clear(); + component.getChildren().addAll(comps); + kids = component.getChildren().listIterator(2); + assertTrue(kids.hasNext()); + assertTrue(kids.hasPrevious()); + assertEquals(2, kids.nextIndex()); + assertEquals(1, kids.previousIndex()); + assertEquals(comp2, kids.next()); + assertEquals(comp3, kids.next()); + assertEquals(comp4, kids.next()); + assertEquals(comp4, kids.previous()); + assertEquals(comp3, kids.previous()); + assertEquals(comp2, kids.previous()); + assertEquals(comp1, kids.previous()); + + // Test IOB exception for list iterator + component.getChildren().clear(); + component.getChildren().addAll(comps); + try { + component.getChildren().listIterator(-1); + fail("Should throw IndexOutOfBoundsException on index -1"); + } catch (IndexOutOfBoundsException e) { + // Expected result + } + + try { + component.getChildren().listIterator(component.getChildren().size() + 1); + fail("Should throw IndexOutOfBoundsException on index = size() + 1"); + } catch (IndexOutOfBoundsException e) { + // Expected result + } + + // Iterate with list iterator in reverse order + int i = component.getChildren().size() - 1; + for (ListIterator li = component.getChildren().listIterator(component.getChildren().size()); li.hasPrevious();) { + + assertEquals(comps.get(i--), li.previous()); + } + + } + + // Negative tests on children methods + @Test + public void testChidrenNegative() { + + // Construct components we will need + UIComponent comp0 = new ComponentTestImpl(null); + UIComponent comp1 = new ComponentTestImpl("comp1"); + UIComponent comp2 = new ComponentTestImpl("comp2"); + UIComponent comp3 = new ComponentTestImpl("comp3"); + + // Set up and verify initial state + List children = component.getChildren(); + children.add(comp0); + children.add(comp1); + children.add(comp2); + checkChildCount(component, 3); + checkChildPresent(component, comp0, 0); + checkChildPresent(component, comp1, 1); + checkChildPresent(component, comp2, 2); + checkChildMissing(component, comp3); + + // add(Object) - NullPointerException + try { + children.add(null); + fail("Should have thrown NullPointerException"); + } catch (NullPointerException e) { + // Expected result + } + checkChildCount(component, 3); + checkChildPresent(component, comp0, 0); + checkChildPresent(component, comp1, 1); + checkChildPresent(component, comp2, 2); + checkChildMissing(component, comp3); + + // add(int,Object) - IndexOutOfBoundsException low + try { + children.add(-1, comp3); + fail("Should have thrown IndexOutOfBoundsException low"); + } catch (IndexOutOfBoundsException e) { + // Expected result + } + checkChildCount(component, 3); + checkChildPresent(component, comp0, 0); + checkChildPresent(component, comp1, 1); + checkChildPresent(component, comp2, 2); + checkChildMissing(component, comp3); + + // add(int,Object) - IndexOutOfBoundsException high + try { + children.add(4, comp3); + fail("Should have thrown IndexOutOfBoundsException high"); + } catch (IndexOutOfBoundsException e) { + // Expected result + } + checkChildCount(component, 3); + checkChildPresent(component, comp0, 0); + checkChildPresent(component, comp1, 1); + checkChildPresent(component, comp2, 2); + checkChildMissing(component, comp3); + + // add(int,Object) - NullPointerException + try { + children.add(1, null); + fail("Should have thrown NullPointerException"); + } catch (NullPointerException e) { + // Expected result + } + checkChildCount(component, 3); + checkChildPresent(component, comp0, 0); + checkChildPresent(component, comp1, 1); + checkChildPresent(component, comp2, 2); + checkChildMissing(component, comp3); + + // set(int,Object) - IndexOutOfBoundsException low + try { + children.set(-1, comp3); + fail("Should have thrown IndexOutOfBoundsException low"); + } catch (IndexOutOfBoundsException e) { + // Expected result + } + checkChildCount(component, 3); + checkChildPresent(component, comp0, 0); + checkChildPresent(component, comp1, 1); + checkChildPresent(component, comp2, 2); + checkChildMissing(component, comp3); + + // set(int,Object) - IndexOutOfBoundsException high + try { + children.set(4, comp3); + fail("Should have thrown IndexOutOfBoundsException high"); + } catch (IndexOutOfBoundsException e) { + // Expected result + } + checkChildCount(component, 3); + checkChildPresent(component, comp0, 0); + checkChildPresent(component, comp1, 1); + checkChildPresent(component, comp2, 2); + checkChildMissing(component, comp3); + + // set(int,Object) - NullPointerException + try { + children.set(1, null); + fail("Should have thrown NullPointerException"); + } catch (NullPointerException e) { + // Expected result + } + checkChildCount(component, 3); + checkChildPresent(component, comp0, 0); + checkChildPresent(component, comp1, 1); + checkChildPresent(component, comp2, 2); + checkChildMissing(component, comp3); + + } + + // Positive tests on children methods + @Test + public void testChildrenPositive() { + + // Construct components we will need + UIComponent comp0 = new ComponentTestImpl(null); + UIComponent comp1 = new ComponentTestImpl("comp1"); + UIComponent comp2 = new ComponentTestImpl("comp2"); + UIComponent comp3 = new ComponentTestImpl("comp3"); + UIComponent comp4 = new ComponentTestImpl("comp4"); + UIComponent comp5 = new ComponentTestImpl("comp5"); + UIComponent comp6 = new ComponentTestImpl("comp6"); + + // Verify initial state + List children = component.getChildren(); + checkChildMissing(component, comp0); + checkChildCount(component, 0); + checkChildMissing(component, comp1); + checkChildMissing(component, comp2); + checkChildMissing(component, comp3); + checkChildMissing(component, comp4); + checkChildMissing(component, comp5); + checkChildMissing(component, comp6); + + // add(Object) + children.add(comp1); + checkChildCount(component, 1); + checkChildMissing(component, comp0); + checkChildPresent(component, comp1, 0); + checkChildMissing(component, comp2); + checkChildMissing(component, comp3); + checkChildMissing(component, comp4); + checkChildMissing(component, comp5); + checkChildMissing(component, comp6); + + // add(int, Object) + children.add(0, comp0); + checkChildCount(component, 2); + checkChildPresent(component, comp0, 0); + checkChildPresent(component, comp1, 1); + checkChildMissing(component, comp2); + checkChildMissing(component, comp3); + checkChildMissing(component, comp4); + checkChildMissing(component, comp5); + checkChildMissing(component, comp6); + + // addAll(Collection) + ArrayList list1 = new ArrayList<>(); + list1.add(comp4); + list1.add(comp5); + children.addAll(list1); + checkChildCount(component, 4); + checkChildPresent(component, comp0, 0); + checkChildPresent(component, comp1, 1); + checkChildMissing(component, comp2); + checkChildMissing(component, comp3); + checkChildPresent(component, comp4, 2); + checkChildPresent(component, comp5, 3); + checkChildMissing(component, comp6); + + // addAll(int, Collection) + ArrayList list2 = new ArrayList<>(); + list2.add(comp2); + list2.add(comp3); + children.addAll(2, list2); + checkChildCount(component, 6); + checkChildPresent(component, comp0, 0); + checkChildPresent(component, comp1, 1); + checkChildPresent(component, comp2, 2); + checkChildPresent(component, comp3, 3); + checkChildPresent(component, comp4, 4); + checkChildPresent(component, comp5, 5); + checkChildMissing(component, comp6); + + // contains(Object) is tested in checkChildPresent / checkChildMissing + // containsAll(Collection) + assertTrue(children.containsAll(list1)); + assertTrue(children.containsAll(list2)); + + // get(int) is tested in checkChildPresent / checkChildMissing + // indexOf(Object) is tested in checkChildPresent / checkChildMissing + // isEmpty() is tested in checkChildCount + // iterator() is tested in testChildrenIterator + // listIterator() is tested in testChildrenListIterator + // toArray(Object[]) + UIComponent kids[] = children.toArray(new UIComponent[0]); + assertEquals(comp0, kids[0]); + assertEquals(comp1, kids[1]); + assertEquals(comp2, kids[2]); + assertEquals(comp3, kids[3]); + assertEquals(comp4, kids[4]); + assertEquals(comp5, kids[5]); + + // subList(int,int) + List subList = children.subList(3, 5); + assertEquals(2, subList.size()); + assertEquals(comp3, subList.get(0)); + assertEquals(comp4, subList.get(1)); + + // set(int,Object) + children.set(4, comp6); + checkChildCount(component, 6); + checkChildPresent(component, comp0, 0); + checkChildPresent(component, comp1, 1); + checkChildPresent(component, comp2, 2); + checkChildPresent(component, comp3, 3); + checkChildMissing(component, comp4); + checkChildPresent(component, comp5, 5); + checkChildPresent(component, comp6, 4); + assertTrue(!children.containsAll(list1)); + assertTrue(children.containsAll(list2)); + + // remove(int) + children.remove(4); + checkChildCount(component, 5); + checkChildPresent(component, comp0, 0); + checkChildPresent(component, comp1, 1); + checkChildPresent(component, comp2, 2); + checkChildPresent(component, comp3, 3); + checkChildMissing(component, comp4); + checkChildPresent(component, comp5, 4); + checkChildMissing(component, comp6); + assertTrue(!children.containsAll(list1)); + assertTrue(children.containsAll(list2)); + + // removeAll(Collection) + children.removeAll(list2); + checkChildCount(component, 3); + checkChildPresent(component, comp0, 0); + checkChildPresent(component, comp1, 1); + checkChildMissing(component, comp2); + checkChildMissing(component, comp3); + checkChildMissing(component, comp4); + checkChildPresent(component, comp5, 2); + checkChildMissing(component, comp6); + assertTrue(!children.containsAll(list1)); + assertTrue(!children.containsAll(list2)); + + // retainAll() + ArrayList list3 = new ArrayList<>(); + list3.add(comp1); + list3.add(comp3); + list3.add(comp5); + children.retainAll(list3); + checkChildCount(component, 2); + checkChildMissing(component, comp0); + checkChildPresent(component, comp1, 0); + checkChildMissing(component, comp2); + checkChildMissing(component, comp3); + checkChildMissing(component, comp4); + checkChildPresent(component, comp5, 1); + checkChildMissing(component, comp6); + assertTrue(!children.containsAll(list3)); + + // size() is tested in checkChildCount + // clear() + children.clear(); + checkChildCount(component, 0); + assertNull(comp0.getParent()); + assertNull(comp1.getParent()); + assertNull(comp2.getParent()); + assertNull(comp3.getParent()); + assertNull(comp4.getParent()); + assertNull(comp5.getParent()); + assertNull(comp6.getParent()); + + } + + // Test replacing a child with a new one that has the same id + @Test + public void testChidrenReplace() { + + ComponentTestImpl child1 = new ComponentTestImpl("child"); + ComponentTestImpl child2 = new ComponentTestImpl("child"); + + checkChildCount(component, 0); + component.getChildren().add(child1); + checkChildCount(component, 1); + checkChildPresent(component, child1, 0); + checkChildMissing(component, child2); + component.getChildren().set(0, child2); + checkChildCount(component, 1); + checkChildMissing(component, child1); + checkChildPresent(component, child2, 0); + component.getChildren().clear(); + checkChildCount(component, 0); + + } + + // Test Set returned by getFacets().entrySet() + @Test + public void testFacetsMapEntrySet() { + + Map facets; + Set> matches; + Set> entrySet; + Iterator> entries; + + // Construct the pre-load set of facets we will need + UIComponent facet1 = new ComponentTestImpl("facet1"); + UIComponent facet2 = new ComponentTestImpl("facet2"); + UIComponent facet3 = new ComponentTestImpl("facet3"); + UIComponent facet4 = new ComponentTestImpl("facet4"); + UIComponent facet5 = new ComponentTestImpl("facet5"); + UIComponent facet6 = new ComponentTestImpl("facet6"); // Not normally added + Map preload = new HashMap<>(); + preload.put("a", facet1); + preload.put("b", facet2); + preload.put("c", facet3); + preload.put("d", facet4); + preload.put("e", facet5); + + // Test add() + facets = component.getFacets(); + facets.clear(); + facets.putAll(preload); + entrySet = facets.entrySet(); + try { + entrySet.add(bogusEntry); + fail("Should have thrown UnsupportedOperationExcepton"); + } catch (UnsupportedOperationException e) { + // Expected result + } + + // Test clear() + facets = component.getFacets(); + facets.clear(); + facets.putAll(preload); + entrySet = facets.entrySet(); + assertEquals(5, facets.size()); + assertEquals(5, entrySet.size()); + checkFacetPresent(component, "a", facet1); + checkFacetPresent(component, "b", facet2); + checkFacetPresent(component, "c", facet3); + checkFacetPresent(component, "d", facet4); + checkFacetPresent(component, "e", facet5); + entrySet.clear(); + assertEquals(0, facets.size()); + assertEquals(0, entrySet.size()); + checkFacetMissing(component, "a", facet1); + checkFacetMissing(component, "b", facet2); + checkFacetMissing(component, "c", facet3); + checkFacetMissing(component, "d", facet4); + checkFacetMissing(component, "e", facet5); + + // Test contains() + facets = component.getFacets(); + facets.clear(); + facets.putAll(preload); + entrySet = facets.entrySet(); + assertTrue(entrySet.contains(new TestMapEntry("a", facet1))); + assertTrue(entrySet.contains(new TestMapEntry("b", facet2))); + assertTrue(entrySet.contains(new TestMapEntry("c", facet3))); + assertTrue(entrySet.contains(new TestMapEntry("d", facet4))); + assertTrue(entrySet.contains(new TestMapEntry("e", facet5))); + assertTrue(!entrySet.contains(new TestMapEntry("f", facet6))); + + // Test containsAll() + facets = component.getFacets(); + facets.clear(); + facets.putAll(preload); + entrySet = facets.entrySet(); + matches = new HashSet<>(); + matches.add(new TestMapEntry("a", facet1)); + matches.add(new TestMapEntry("c", facet3)); + matches.add(new TestMapEntry("d", facet4)); + assertTrue(entrySet.containsAll(matches)); + matches = new HashSet<>(); + matches.add(new TestMapEntry("a", facet1)); + matches.add(new TestMapEntry("c", facet3)); + matches.add(new TestMapEntry("f", facet6)); + assertTrue(!entrySet.containsAll(matches)); + + // Test iterator().hasNext() and iterator().next() + facets = component.getFacets(); + facets.clear(); + facets.putAll(preload); + entrySet = facets.entrySet(); + matches = new HashSet<>(); + entries = entrySet.iterator(); + while (entries.hasNext()) { + matches.add(entries.next()); + } + assertTrue(entrySet.equals(matches)); + + // Test iterator().remove() + facets = component.getFacets(); + facets.clear(); + facets.putAll(preload); + entrySet = facets.entrySet(); + entries = entrySet.iterator(); + while (entries.hasNext()) { + var entry = entries.next(); + if ("b".equals(entry.getKey()) || "d".equals(entry.getKey())) { + entries.remove(); + } + } + assertEquals(3, facets.size()); + assertEquals(3, entrySet.size()); + checkFacetPresent(component, "a", facet1); + checkFacetMissing(component, "b", facet2); + checkFacetPresent(component, "c", facet3); + checkFacetMissing(component, "d", facet4); + checkFacetPresent(component, "e", facet5); + + // Test iterator() based modify-value + facets = component.getFacets(); + facets.clear(); + facets.putAll(preload); + entrySet = facets.entrySet(); + entries = entrySet.iterator(); + while (entries.hasNext()) { + var entry = entries.next(); + if ("c".equals(entry.getKey())) { + entry.setValue(facet6); + } + } + assertEquals(5, facets.size()); + assertEquals(5, entrySet.size()); + checkFacetPresent(component, "a", facet1); + checkFacetPresent(component, "b", facet2); + checkFacetPresent(component, "c", facet6); + checkFacetPresent(component, "d", facet4); + checkFacetPresent(component, "e", facet5); + + // Test remove() + facets = component.getFacets(); + facets.clear(); + facets.putAll(preload); + entrySet = facets.entrySet(); + entrySet.remove(new TestMapEntry("c", facet3)); + assertEquals(4, facets.size()); + assertEquals(4, entrySet.size()); + checkFacetPresent(component, "a", facet1); + checkFacetPresent(component, "b", facet2); + checkFacetMissing(component, "c", facet3); + checkFacetPresent(component, "d", facet4); + checkFacetPresent(component, "e", facet5); + + // Test removeAll() + facets = component.getFacets(); + facets.clear(); + facets.putAll(preload); + entrySet = facets.entrySet(); + matches = new HashSet<>(); + matches.add(new TestMapEntry("b", facet2)); + matches.add(new TestMapEntry("d", facet4)); + entrySet.removeAll(matches); + assertEquals(3, facets.size()); + assertEquals(3, entrySet.size()); + checkFacetPresent(component, "a", facet1); + checkFacetMissing(component, "b", facet2); + checkFacetPresent(component, "c", facet3); + checkFacetMissing(component, "d", facet4); + checkFacetPresent(component, "e", facet5); + + // Test retainAll() + facets = component.getFacets(); + facets.clear(); + facets.putAll(preload); + entrySet = facets.entrySet(); + matches = new HashSet<>(); + matches.add(new TestMapEntry("b", facet2)); + matches.add(new TestMapEntry("d", facet4)); + matches.add(new TestMapEntry("f", facet6)); + entrySet.retainAll(matches); + assertEquals(2, facets.size()); + assertEquals(2, entrySet.size()); + checkFacetMissing(component, "a", facet1); + checkFacetPresent(component, "b", facet2); + checkFacetMissing(component, "c", facet3); + checkFacetPresent(component, "d", facet4); + checkFacetMissing(component, "e", facet5); + + } + + // Test Set returned by getFacets().keySet() + @Test + public void testFacetsMapKeySet() { + + Map facets; + Set matches; + Set keySet; + Iterator keys; + + // Construct the pre-load set of facets we will need + UIComponent facet1 = new ComponentTestImpl("facet1"); + UIComponent facet2 = new ComponentTestImpl("facet2"); + UIComponent facet3 = new ComponentTestImpl("facet3"); + UIComponent facet4 = new ComponentTestImpl("facet4"); + UIComponent facet5 = new ComponentTestImpl("facet5"); + Map preload = new HashMap<>(); + preload.put("a", facet1); + preload.put("b", facet2); + preload.put("c", facet3); + preload.put("d", facet4); + preload.put("e", facet5); + + // Test clear() + facets = component.getFacets(); + facets.clear(); + facets.putAll(preload); + keySet = facets.keySet(); + assertEquals(5, facets.size()); + assertEquals(5, keySet.size()); + checkFacetPresent(component, "a", facet1); + checkFacetPresent(component, "b", facet2); + checkFacetPresent(component, "c", facet3); + checkFacetPresent(component, "d", facet4); + checkFacetPresent(component, "e", facet5); + keySet.clear(); + assertEquals(0, facets.size()); + assertEquals(0, keySet.size()); + checkFacetMissing(component, "a", facet1); + checkFacetMissing(component, "b", facet2); + checkFacetMissing(component, "c", facet3); + checkFacetMissing(component, "d", facet4); + checkFacetMissing(component, "e", facet5); + + // Test contains() + facets = component.getFacets(); + facets.clear(); + facets.putAll(preload); + keySet = facets.keySet(); + assertTrue(keySet.contains("a")); + assertTrue(keySet.contains("b")); + assertTrue(keySet.contains("c")); + assertTrue(keySet.contains("d")); + assertTrue(keySet.contains("e")); + assertTrue(!keySet.contains("f")); + + // Test containsAll() + facets = component.getFacets(); + facets.clear(); + facets.putAll(preload); + keySet = facets.keySet(); + matches = new HashSet<>(); + matches.add("a"); + matches.add("c"); + matches.add("d"); + assertTrue(keySet.containsAll(matches)); + matches = new HashSet<>(); + matches.add("a"); + matches.add("c"); + matches.add("f"); + assertTrue(!keySet.containsAll(matches)); + + // Test iterator().hasNext() and iterator().next() + facets = component.getFacets(); + facets.clear(); + facets.putAll(preload); + keySet = facets.keySet(); + matches = new HashSet<>(); + keys = keySet.iterator(); + while (keys.hasNext()) { + matches.add(keys.next()); + } + assertTrue(keySet.equals(matches)); + + // Test iterator().remove() + facets = component.getFacets(); + facets.clear(); + facets.putAll(preload); + keySet = facets.keySet(); + keys = keySet.iterator(); + while (keys.hasNext()) { + String key = keys.next(); + if ("b".equals(key) || "d".equals(key)) { + keys.remove(); + } + } + assertEquals(3, facets.size()); + assertEquals(3, keySet.size()); + checkFacetPresent(component, "a", facet1); + checkFacetMissing(component, "b", facet2); + checkFacetPresent(component, "c", facet3); + checkFacetMissing(component, "d", facet4); + checkFacetPresent(component, "e", facet5); + + // Test remove() + facets = component.getFacets(); + facets.clear(); + facets.putAll(preload); + keySet = facets.keySet(); + keySet.remove("c"); + assertEquals(4, facets.size()); + assertEquals(4, keySet.size()); + checkFacetPresent(component, "a", facet1); + checkFacetPresent(component, "b", facet2); + checkFacetMissing(component, "c", facet3); + checkFacetPresent(component, "d", facet4); + checkFacetPresent(component, "e", facet5); + + // Test removeAll() + facets = component.getFacets(); + facets.clear(); + facets.putAll(preload); + keySet = facets.keySet(); + matches = new HashSet<>(); + matches.add("b"); + matches.add("d"); + keySet.removeAll(matches); + assertEquals(3, facets.size()); + assertEquals(3, keySet.size()); + checkFacetPresent(component, "a", facet1); + checkFacetMissing(component, "b", facet2); + checkFacetPresent(component, "c", facet3); + checkFacetMissing(component, "d", facet4); + checkFacetPresent(component, "e", facet5); + + // Test retainAll() + facets = component.getFacets(); + facets.clear(); + facets.putAll(preload); + keySet = facets.keySet(); + matches = new HashSet<>(); + matches.add("b"); + matches.add("d"); + matches.add("f"); + keySet.retainAll(matches); + assertEquals(2, facets.size()); + assertEquals(2, keySet.size()); + checkFacetMissing(component, "a", facet1); + checkFacetPresent(component, "b", facet2); + checkFacetMissing(component, "c", facet3); + checkFacetPresent(component, "d", facet4); + checkFacetMissing(component, "e", facet5); + + } + + // Test Collection returned by getFacets().values() + @Test + public void testFacetsMapValues() { + + Map facets; + Collection matches; + Collection values; + Iterator vals; + + // Construct the pre-load set of facets we will need + UIComponent facet1 = new ComponentTestImpl("facet1"); + UIComponent facet2 = new ComponentTestImpl("facet2"); + UIComponent facet3 = new ComponentTestImpl("facet3"); + UIComponent facet4 = new ComponentTestImpl("facet4"); + UIComponent facet5 = new ComponentTestImpl("facet5"); + UIComponent facet6 = new ComponentTestImpl("facet6"); // Not normally added + Map preload = new HashMap<>(); + preload.put("a", facet1); + preload.put("b", facet2); + preload.put("c", facet3); + preload.put("d", facet4); + preload.put("e", facet5); + + // Test add() + facets = component.getFacets(); + facets.clear(); + facets.putAll(preload); + values = facets.values(); + try { + values.add(new ComponentTestImpl("facet0")); + fail("Should have thrown UnsupportedOperationExcepton"); + } catch (UnsupportedOperationException e) { + // Expected result + } + + // Test addAll() + facets = component.getFacets(); + facets.clear(); + facets.putAll(preload); + values = facets.values(); + try { + values.addAll(preload.values()); + fail("Should have thrown UnsupportedOperationExcepton"); + } catch (UnsupportedOperationException e) { + // Expected result + } + + // Test clear() + facets = component.getFacets(); + facets.clear(); + facets.putAll(preload); + values = facets.values(); + assertEquals(5, facets.size()); + assertEquals(5, values.size()); + checkFacetPresent(component, "a", facet1); + checkFacetPresent(component, "b", facet2); + checkFacetPresent(component, "c", facet3); + checkFacetPresent(component, "d", facet4); + checkFacetPresent(component, "e", facet5); + values.clear(); + assertEquals(0, facets.size()); + assertEquals(0, values.size()); + checkFacetMissing(component, "a", facet1); + checkFacetMissing(component, "b", facet2); + checkFacetMissing(component, "c", facet3); + checkFacetMissing(component, "d", facet4); + checkFacetMissing(component, "e", facet5); + + // Test contains() + facets = component.getFacets(); + facets.clear(); + facets.putAll(preload); + values = facets.values(); + assertTrue(values.contains(facet1)); + assertTrue(values.contains(facet2)); + assertTrue(values.contains(facet3)); + assertTrue(values.contains(facet4)); + assertTrue(values.contains(facet5)); + assertTrue(!values.contains(facet6)); + + // Test containsAll() + facets = component.getFacets(); + facets.clear(); + facets.putAll(preload); + values = facets.values(); + matches = new ArrayList<>(); + matches.add(facet1); + matches.add(facet3); + matches.add(facet4); + assertTrue(values.containsAll(matches)); + matches = new ArrayList<>(); + matches.add(facet1); + matches.add(facet3); + matches.add(facet6); + assertTrue(!values.containsAll(matches)); + + // Test iterator().hasNext() and iterator().next() + facets = component.getFacets(); + facets.clear(); + facets.putAll(preload); + values = facets.values(); + matches = new ArrayList<>(); + vals = values.iterator(); + while (vals.hasNext()) { + matches.add(vals.next()); + } + assertTrue(matches.containsAll(values)); + + // Test iterator().remove() + facets = component.getFacets(); + facets.clear(); + facets.putAll(preload); + values = facets.values(); + vals = values.iterator(); + while (vals.hasNext()) { + UIComponent val = vals.next(); + if (facet2.equals(val) || facet4.equals(val)) { + vals.remove(); + } + } + assertEquals(3, facets.size()); + assertEquals(3, values.size()); + checkFacetPresent(component, "a", facet1); + checkFacetMissing(component, "b", facet2); + checkFacetPresent(component, "c", facet3); + checkFacetMissing(component, "d", facet4); + checkFacetPresent(component, "e", facet5); + + // Test remove() + facets = component.getFacets(); + facets.clear(); + facets.putAll(preload); + values = facets.values(); + values.remove(facet3); + assertEquals(4, facets.size()); + assertEquals(4, values.size()); + checkFacetPresent(component, "a", facet1); + checkFacetPresent(component, "b", facet2); + checkFacetMissing(component, "c", facet3); + checkFacetPresent(component, "d", facet4); + checkFacetPresent(component, "e", facet5); + + // Test removeAll() + facets = component.getFacets(); + facets.clear(); + facets.putAll(preload); + values = facets.values(); + matches = new ArrayList<>(); + matches.add(facet2); + matches.add(facet4); + values.removeAll(matches); + assertEquals(3, facets.size()); + assertEquals(3, values.size()); + checkFacetPresent(component, "a", facet1); + checkFacetMissing(component, "b", facet2); + checkFacetPresent(component, "c", facet3); + checkFacetMissing(component, "d", facet4); + checkFacetPresent(component, "e", facet5); + + // Test retainAll() + facets = component.getFacets(); + facets.clear(); + facets.putAll(preload); + values = facets.values(); + matches = new ArrayList<>(); + matches.add(facet2); + matches.add(facet4); + matches.add(facet6); + values.retainAll(matches); + assertEquals(2, facets.size()); + assertEquals(2, values.size()); + checkFacetMissing(component, "a", facet1); + checkFacetPresent(component, "b", facet2); + checkFacetMissing(component, "c", facet3); + checkFacetPresent(component, "d", facet4); + checkFacetMissing(component, "e", facet5); + + } + + // Negative tests on facet methods + @Test + public void testFacetsNegative() { + + // Construct components we will need + UIComponent facet1 = new ComponentTestImpl("facet1"); + UIComponent facet2 = new ComponentTestImpl("facet2"); + UIComponent facet3 = new ComponentTestImpl("facet3"); + + // Set up and verify initial conditions + Map facets = component.getFacets(); + facets.put("facet1", facet1); + facets.put("facet2", facet2); + checkFacetCount(component, 2); + checkFacetPresent(component, "facet1", facet1); + checkFacetPresent(component, "facet2", facet2); + checkFacetMissing(component, "facet3", facet3); + + // put(Object,Object) - null first argument + try { + facets.put(null, facet3); + fail("Should have thrown NullPointerException"); + } catch (NullPointerException e) { + // Expected result + } + checkFacetCount(component, 2); + checkFacetPresent(component, "facet1", facet1); + checkFacetPresent(component, "facet2", facet2); + checkFacetMissing(component, "facet3", facet3); + + // put(Object,Object) - null second argument + try { + facets.put("facet3", null); + fail("Should have thrown NullPointerException"); + } catch (NullPointerException e) { + // Expected result + } + checkFacetCount(component, 2); + checkFacetPresent(component, "facet1", facet1); + checkFacetPresent(component, "facet2", facet2); + checkFacetMissing(component, "facet3", facet3); + + } + + // Positive tests on facet methods + @Test + public void testFacetsPositive() { + + // Construct components we will need + UIComponent facet1 = new ComponentTestImpl("facet1"); + UIComponent facet2 = new ComponentTestImpl("facet2"); + UIComponent facet3 = new ComponentTestImpl("facet3"); + UIComponent facet4 = new ComponentTestImpl("facet4"); + UIComponent facet5 = new ComponentTestImpl("facet5"); + + // Verify initial conditions + Map facets = component.getFacets(); + checkFacetCount(component, 0); + checkFacetMissing(component, "facet1", facet1); + checkFacetMissing(component, "facet2", facet2); + checkFacetMissing(component, "facet3", facet3); + checkFacetMissing(component, "facet4", facet4); + checkFacetMissing(component, "facet5", facet5); + + // containsKey(Object) is tested in checkFacetMissing / checkFacetPresent + // containsValue(Object) is tested in checkFacetMissing / checkFacetPresent + // entrySet() is tested in testFacetsMapEntrySet() + // get(Object) is tested in checkFacetMissing / checkFacetPresent + // isEmpty() is tested in checkFacetCount + // keySet() is tested in testFacetsMapKeySet() + // put(Object,Object) + facets.put("facet1", facet1); + checkFacetCount(component, 1); + checkFacetPresent(component, "facet1", facet1); + checkFacetMissing(component, "facet2", facet2); + checkFacetMissing(component, "facet3", facet3); + checkFacetMissing(component, "facet4", facet4); + checkFacetMissing(component, "facet5", facet5); + + // put(Object,Object) + facets.put("facet4", facet4); + checkFacetCount(component, 2); + checkFacetPresent(component, "facet1", facet1); + checkFacetMissing(component, "facet2", facet2); + checkFacetMissing(component, "facet3", facet3); + checkFacetPresent(component, "facet4", facet4); + checkFacetMissing(component, "facet5", facet5); + + // putAll(Map) + Map map = new HashMap<>(); + map.put("facet2", facet2); + map.put("facet3", facet3); + facets.putAll(map); + checkFacetCount(component, 4); + checkFacetPresent(component, "facet1", facet1); + checkFacetPresent(component, "facet2", facet2); + checkFacetPresent(component, "facet3", facet3); + checkFacetPresent(component, "facet4", facet4); + checkFacetMissing(component, "facet5", facet5); + + // remove(Object) + facets.remove("facet3"); + checkFacetCount(component, 3); + checkFacetPresent(component, "facet1", facet1); + checkFacetPresent(component, "facet2", facet2); + checkFacetMissing(component, "facet3", facet3); + checkFacetPresent(component, "facet4", facet4); + checkFacetMissing(component, "facet5", facet5); + + // values() is tested in testFacetsMapValues() + // clear() + facets.clear(); + checkFacetCount(component, 0); + checkFacetMissing(component, "facet1", facet1); + checkFacetMissing(component, "facet2", facet2); + checkFacetMissing(component, "facet3", facet3); + checkFacetMissing(component, "facet4", facet4); + checkFacetMissing(component, "facet5", facet5); + + } + + // Test a pristine UIComponent instance + @Test + public void testPristine() { + + // Validate attributes + checkAttributeCount(component, expectedAttributes.length); + for (int i = 0; i < expectedAttributes.length; i++) { + checkAttributePresent(component, expectedAttributes[i], null); + } + + // Validate properties + assertEquals(expectedFamily, component.getFamily()); + assertEquals(expectedId, component.getId()); + assertNull(component.getParent()); + assertEquals(expectedRendered, component.isRendered()); + assertEquals(expectedRendererType, component.getRendererType()); + assertEquals(expectedRendersChildren, component.getRendersChildren()); + + // Validate children and facets + checkChildCount(component, 0); + checkFacetCount(component, 0); + int n = 0; + Iterator items = component.getFacetsAndChildren(); + assertNotNull(items); + while (items.hasNext()) { + items.next(); + n++; + } + assertEquals(0, n); + + } + + // Test setting properties to invalid values + @Test + public void testPropertiesInvalid() throws Exception { + + // id - zero length + try { + component.setId(""); + fail("should have thrown IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // Expected result + } + + // id - leading digits + try { + component.setId("1abc"); + fail("should have thrown IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // Expected result + } + + // id - invalid characters 1 + try { + component.setId("a*c"); + fail("should have thrown IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // Expected result + } + + // id - invalid characters 2 + try { + component.setId(" abc"); + fail("should have thrown IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // Expected result + } + + // id - invalid characters 3 + try { + component.setId("-abc"); + fail("should have thrown IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // Expected result + } + + } + + // Test setting properties to valid values + @Test + public void testPropertiesValid() throws Exception { + + // id - simple name + component.setId("foo"); + assertEquals("foo", component.getId()); + + // id - complex name + component.setId("a123-bcd_e"); + assertEquals("a123-bcd_e", component.getId()); + + // parent + UIComponent parent = new ComponentTestImpl("parent"); + component.setParent(parent); + assertEquals(parent, component.getParent()); + + // rendered + component.setRendered(!expectedRendered); + assertEquals(!expectedRendered, component.isRendered()); + + // rendererType + component.setRendererType("foo"); + assertEquals("foo", component.getRendererType()); + + } + + // --------------------------------------------------------- Support Methods + // Validate that the specified number of attributes are present. + protected void checkAttributeCount(UIComponent component, int count) { + int result = 0; + Iterator names = component.getAttributes().keySet().iterator(); + while (names.hasNext()) { + names.next(); + result++; + } + assertEquals(count, result); + } + + // Validate that the specified attribute name is not present + protected void checkAttributeMissing(UIComponent component, String name) { + assertNull(component.getAttributes().get(name), "Attribute " + name + " should be missing"); + Iterator keys = component.getAttributes().keySet().iterator(); + while (keys.hasNext()) { + String key = (String) keys.next(); + if (name.equals(key)) { + fail("Attribute " + name + " should not be in names list"); + } + } + } + + // Validate that the specified attribute name is present with the + // specified value (if value is not null) + protected void checkAttributePresent(UIComponent component, String name, Object value) { + assertNotNull(component.getAttributes().get(name), "attribute " + name + " should be present"); + if (value != null) { + assertEquals(value, component.getAttributes().get(name)); + } + Iterator keys = component.getAttributes().keySet().iterator(); + while (keys.hasNext()) { + String key = (String) keys.next(); + if (name.equals(key)) { + if (value != null) { + assertEquals(value, component.getAttributes().get(name)); + } + return; + } + } + fail("attribute " + name + " should be in name list"); + + } + + // Validate that the specified number of children are present + protected void checkChildCount(UIComponent component, int count) { + assertEquals(count, component.getChildCount()); + assertEquals(count, component.getChildren().size()); + assertEquals(count, component.getChildCount()); + if (count == 0) { + assertTrue(component.getChildren().isEmpty()); + } else { + assertTrue(!component.getChildren().isEmpty()); + } + } + + // Validate that the specified child is not present + protected void checkChildMissing(UIComponent component, UIComponent child) { + assertNull(child.getParent(), "child " + child + " has no parent"); + List children = component.getChildren(); + assertTrue(!children.contains(child), "child " + child + " should not be contained"); + assertEquals(-1, children.indexOf(child)); + for (int i = 0; i < children.size(); i++) { + if (child.equals(children.get(i))) { + fail("child " + child + " should be missing"); + } + } + } + + // Validate that the specified child is present at the specified index + protected void checkChildPresent(UIComponent component, UIComponent child, int index) { + List children = component.getChildren(); + assertTrue(children.contains(child), "child " + child + " should be contained"); + assertEquals(index, children.indexOf(child)); + UIComponent kid = children.get(index); + assertEquals(child, kid); + assertEquals(component, kid.getParent()); + } + + // Validate that the specified number of facets is present + protected void checkFacetCount(UIComponent component, int count) { + assertEquals(count, component.getFacets().size()); + if (count == 0) { + assertTrue(component.getFacets().isEmpty()); + } else { + assertTrue(!component.getFacets().isEmpty()); + } + } + + // Validate that the specified facet is not present + protected void checkFacetMissing(UIComponent component, String name, UIComponent facet) { + assertNull(facet.getParent(), "facet " + name + " has no parent"); + Map facets = component.getFacets(); + assertTrue(!facets.containsKey(name), "facet " + name + " key not present"); + assertTrue(!facets.containsValue(facet), "facet " + name + " value not present"); + assertNull(facets.get(name), "facet " + name + " key not found by get"); + assertNull(component.getFacet(name), "facet " + name + " not returned by getFacet(String)"); + Iterator keys = facets.keySet().iterator(); + while (keys.hasNext()) { + String key = keys.next(); + if (name.equals(key)) { + fail("facet " + name + " found in keys"); + } + } + Iterator values = facets.values().iterator(); + while (values.hasNext()) { + UIComponent value = values.next(); + if (facet.equals(value)) { + fail("facet " + name + " found in values"); + } + } + } + + // Validate that the specified facet is present + protected void checkFacetPresent(UIComponent component, String name, UIComponent facet) { + + assertEquals(component, facet.getParent()); + Map facets = component.getFacets(); + assertTrue(facets.containsKey(name), "facet " + name + " key is present"); + assertTrue(facets.containsValue(facet), "facet " + name + " value is present"); + assertEquals(facet, facets.get(name)); + assertTrue(facet == component.getFacet(name), "facet " + name + " returned by getFacet(String)"); + boolean found = false; + Iterator keys = facets.keySet().iterator(); + while (keys.hasNext()) { + String key = keys.next(); + if (name.equals(key)) { + found = true; + break; + } + } + if (!found) { + fail("facet " + name + " not found in keys"); + } + found = false; + Iterator values = facets.values().iterator(); + while (values.hasNext()) { + UIComponent value = values.next(); + if (facet.equals(value)) { + found = true; + break; + } + } + if (!found) { + fail("facet " + name + " not found in values"); + } + } + + // --------------------------------------------------------- Private Classes + // Test Implementation of Map.Entry + private class TestMapEntry implements Map.Entry { + + private TestMapEntry(String key, UIComponent value) { + this.key = key; + this.value = value; + } + + private String key; + private UIComponent value; + + @Override + public boolean equals(Object o) { + if (o == null) { + return false; + } + if (!(o instanceof Map.Entry)) { + return false; + } + Map.Entry e = (Map.Entry) o; + if (key == null) { + if (e.getKey() != null) { + return false; + } + } else { + if (!key.equals(e.getKey())) { + return false; + } + } + if (value == null) { + if (e.getValue() != null) { + return false; + } + } else { + if (!value.equals(e.getValue())) { + return false; + } + } + return true; + } + + @Override + public String getKey() { + return key; + } + + @Override + public UIComponent getValue() { + return value; + } + + @Override + public int hashCode() { + return (key == null ? 0 : key.hashCode()) ^ (value == null ? 0 : value.hashCode()); + } + + @Override + public UIComponent setValue(UIComponent value) { + UIComponent previous = this.value; + this.value = value; + return previous; + } + + } + + public static class Listener implements ComponentSystemEventListener, Serializable { + + private static final long serialVersionUID = 1L; + private StringBuilder sb = new StringBuilder(); + + @Override + public void processEvent(ComponentSystemEvent event) throws AbortProcessingException { + UIComponent source = (UIComponent) event.getSource(); + Boolean validatorCalled = (Boolean) source.getAttributes().remove("vCalled"); + if (validatorCalled != null) { + sb.append("*/"); + } + sb.append(source.getId()).append('/'); + } + + public String getResults() { + return sb.toString(); + } + } + + public static class ValidationSignal implements Validator { + + @Override + public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException { + + component.getAttributes().put("vCalled", Boolean.TRUE); + + } + } + +} diff --git a/impl/src/test/java/jakarta/faces/component/UIDataTest.java b/impl/src/test/java/jakarta/faces/component/UIDataTest.java new file mode 100644 index 0000000000..5804801c20 --- /dev/null +++ b/impl/src/test/java/jakarta/faces/component/UIDataTest.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces.component; + +import static jakarta.faces.component.NamingContainer.SEPARATOR_CHAR; +import static jakarta.faces.component.UINamingContainer.SEPARATOR_CHAR_PARAM_NAME; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.junit.jupiter.api.Test; + +import jakarta.faces.context.ExternalContext; +import jakarta.faces.context.FacesContext; +import jakarta.faces.render.RenderKit; + +/** + * @author Manfred Riem (manfred.riem@oracle.com) + */ +public class UIDataTest { + + /** + * Test partial state saving. + */ + @Test + public void testSaveState() { + FacesContext context = mock(FacesContext.class); + UIData data = new UIData(); + data.markInitialState(); + assertNull(data.saveState(context)); + } + + /** + * Test full state saving. + */ + @Test + public void testSaveState2() { + FacesContext context = mock(FacesContext.class); + UIData data = new UIData(); + assertNotNull(data.saveState(context)); + } + + /** + * Test partial state saving with rowIndex. + */ + @Test + public void testSaveState3() { + FacesContext context = mock(FacesContext.class); + UIData data = new UIData(); + data.markInitialState(); + data.setRowIndex(4); + assertNotNull(data.saveState(context)); + } + + @Test + public void testInvokeOnComponentMustNotCallSetRowIndexIfNotTouched() throws Exception { + FacesContext context = mock(FacesContext.class); + ExternalContext externalContext = mock(ExternalContext.class); + when(context.getExternalContext()).thenReturn(externalContext); + RenderKit renderKit = mock(RenderKit.class); + when(context.getRenderKit()).thenReturn(renderKit); + context.getAttributes().put(SEPARATOR_CHAR_PARAM_NAME, SEPARATOR_CHAR); + + UIData data = new UIData() { + @Override + public void setRowIndex(int rowIndex) { + context.getAttributes().put("setRowIndexCalled", true); + } + }; + + data.setId("data"); + + data.invokeOnComponent(context, "differentId", (contextInLambda, target) -> { + }); + + assertNull(context.getAttributes().get("setRowIndexCalled")); + } +} diff --git a/impl/src/test/java/jakarta/faces/component/UIGraphicTestCase.java b/impl/src/test/java/jakarta/faces/component/UIGraphicTestCase.java new file mode 100644 index 0000000000..01380c37da --- /dev/null +++ b/impl/src/test/java/jakarta/faces/component/UIGraphicTestCase.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces.component; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + *

+ * Unit tests for {@link UIGraphic}. + *

+ */ +public class UIGraphicTestCase extends UIComponentBaseTestCase { + + // ---------------------------------------------------- Overall Test Methods + // Set up instance variables required by this test case. + @Override + @BeforeEach + public void setUp() throws Exception { + super.setUp(); + component = new UIGraphic(); + expectedFamily = UIGraphic.COMPONENT_FAMILY; + expectedId = null; + expectedRendererType = "jakarta.faces.Image"; + } + + // ------------------------------------------------- Individual Test Methods + // Test attribute-property transparency + @Override + @Test + public void testAttributesTransparency() { + + super.testAttributesTransparency(); + UIGraphic graphic = (UIGraphic) component; + + assertEquals(graphic.getValue(), component.getAttributes().get("value")); + graphic.setValue("foo"); + assertEquals("foo", component.getAttributes().get("value")); + graphic.setValue(null); + assertNull(component.getAttributes().get("value")); + component.getAttributes().put("value", "bar"); + assertEquals("bar", graphic.getValue()); + component.getAttributes().put("value", null); + assertNull(graphic.getValue()); + + assertEquals(graphic.getUrl(), graphic.getAttributes().get("url")); + graphic.setUrl("foo"); + assertEquals("foo", graphic.getAttributes().get("url")); + graphic.setUrl(null); + assertNull(graphic.getAttributes().get("url")); + graphic.getAttributes().put("url", "bar"); + assertEquals("bar", graphic.getUrl()); + graphic.getAttributes().put("url", null); + assertNull(graphic.getUrl()); + } + + // Suppress lifecycle tests since we do not have a renderer + @Override + @Test + public void testLifecycleManagement() { + } + + // Test a pristine UIGraphic instance + @Override + @Test + public void testPristine() { + super.testPristine(); + UIGraphic graphic = (UIGraphic) component; + + assertNull(graphic.getValue()); + assertNull(graphic.getUrl()); + } + + // Test setting properties to invalid values + @Override + @Test + public void testPropertiesInvalid() throws Exception { + super.testPropertiesInvalid(); + } + + // --------------------------------------------------------- Support Methods + // Create a pristine component of the type to be used in state holder tests + @Override + protected UIComponent createComponent() { + UIComponent component = new UIGraphic(); + component.setRendererType(null); + return component; + } +} diff --git a/impl/src/test/java/jakarta/faces/component/UIInputTest.java b/impl/src/test/java/jakarta/faces/component/UIInputTest.java new file mode 100644 index 0000000000..9646a002d3 --- /dev/null +++ b/impl/src/test/java/jakarta/faces/component/UIInputTest.java @@ -0,0 +1,193 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces.component; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import jakarta.faces.context.FacesContext; +import jakarta.faces.validator.LengthValidator; +import jakarta.faces.validator.LongRangeValidator; +import jakarta.faces.validator.Validator; + +/** + * @author Manfred Riem (manfred.riem@oracle.com) + */ +public class UIInputTest { + + @Test + public void testSaveState() { + FacesContext context = Mockito.mock(FacesContext.class); + UIInput input = new UIInput(); + assertNotNull(input.saveState(context)); + } + + @Test + public void testSaveState2() { + UIInput input = new UIInput(); + assertThrows(NullPointerException.class, () -> input.saveState(null)); + } + + @Test + public void testSaveState3() { + FacesContext context = Mockito.mock(FacesContext.class); + UIInput input = new UIInput(); + input.markInitialState(); + assertNull(input.saveState(context)); + } + + @Test + public void testSaveState4() { + FacesContext context = Mockito.mock(FacesContext.class); + UIInput input = new UIInput(); + LengthValidator l1 = new LengthValidator(); + LengthValidator l2 = new LengthValidator(); + input.addValidator(l1); + input.addValidator(l2); + l1.setMinimum(1); + l2.setMinimum(2); + input.markInitialState(); + assertTrue(input.initialStateMarked()); + assertTrue(l1.initialStateMarked()); + assertTrue(l2.initialStateMarked()); + Object state = input.saveState(context); + assertNull(state); + } + + @Test + public void testRestoreState() { + FacesContext context = Mockito.mock(FacesContext.class); + UIInput input = new UIInput(); + input.restoreState(context, null); + } + + @Test + public void testRestoreState2() { + FacesContext context = Mockito.mock(FacesContext.class); + UIInput input = new UIInput(); + assertThrows(NullPointerException.class, () -> input.restoreState(null, null)); + } + + @Test + public void testRestoreState3() { + FacesContext context = Mockito.mock(FacesContext.class); + UIInput input = new UIInput(); + Object state = input.saveState(context); + assertNotNull(state); + input.restoreState(context, state); + } + + @Test + public void testRestoreState4() { + FacesContext context = Mockito.mock(FacesContext.class); + UIInput input = new UIInput(); + input.addValidator(new LongRangeValidator()); + Object state = input.saveState(context); + assertNotNull(state); + input = new UIInput(); + input.restoreState(context, state); + } + + @Test + public void testRestoreState5() { + FacesContext context = Mockito.mock(FacesContext.class); + UIInput input = new UIInput(); + LengthValidator l1 = new LengthValidator(); + LengthValidator l2 = new LengthValidator(); + input.addValidator(l1); + input.addValidator(l2); + l1.setMinimum(1); + l2.setMinimum(2); + input.markInitialState(); + l2.setMinimum(3); + assertTrue(input.initialStateMarked()); + assertTrue(l1.initialStateMarked()); + assertTrue(!l2.initialStateMarked()); + Object state = input.saveState(context); + assertTrue(state instanceof Object[]); + Object[] validatorState = (Object[]) ((Object[]) state)[1]; + assertNotNull(validatorState); + assertNull(validatorState[0]); + assertNotNull(validatorState[1]); + assertTrue(!(validatorState[1] instanceof StateHolderSaver)); + input = new UIInput(); + l1 = new LengthValidator(); + l2 = new LengthValidator(); + l1.setMinimum(1); + l2.setMinimum(2); + input.addValidator(l1); + input.addValidator(l2); + input.restoreState(context, state); + assertTrue(l1.getMinimum() == 1); + assertTrue(l2.getMinimum() == 3); + assertTrue(input.getValidators().length == 2); + + input = new UIInput(); + l1 = new LengthValidator(); + l2 = new LengthValidator(); + input.addValidator(l1); + input.addValidator(l2); + l1.setMinimum(1); + l2.setMinimum(2); + input.markInitialState(); + LengthValidator l3 = new LengthValidator(); + l3.setMinimum(3); + input.addValidator(l3); + state = input.saveState(context); + assertNotNull(validatorState); + assertTrue(state instanceof Object[]); + validatorState = (Object[]) ((Object[]) state)[1]; + assertNotNull(validatorState); + assertTrue(validatorState.length == 3); + assertNotNull(validatorState[0]); + assertNotNull(validatorState[1]); + assertNotNull(validatorState[2]); + assertTrue(validatorState[0] instanceof StateHolderSaver); + assertTrue(validatorState[1] instanceof StateHolderSaver); + assertTrue(validatorState[2] instanceof StateHolderSaver); + + input = new UIInput(); + l1 = new LengthValidator(); + l2 = new LengthValidator(); + l3 = new LengthValidator(); + LengthValidator l4 = new LengthValidator(); + input.addValidator(l1); + input.addValidator(l2); + input.addValidator(l3); + input.addValidator(l4); + l1.setMinimum(100); + l2.setMinimum(101); + l3.setMinimum(102); + l4.setMinimum(103); + assertTrue(input.getValidators().length == 4); + input.markInitialState(); + input.restoreState(context, state); + assertTrue(input.getValidators().length == 3); + + Validator[] validators = input.getValidators(); + for (int i = 0, len = validators.length; i < len; i++) { + LengthValidator v = (LengthValidator) validators[i]; + assertTrue(v.getMinimum() == i + 1); + } + + } +} diff --git a/impl/src/test/java/jakarta/faces/component/UIInputTestCase.java b/impl/src/test/java/jakarta/faces/component/UIInputTestCase.java new file mode 100644 index 0000000000..1013cd5732 --- /dev/null +++ b/impl/src/test/java/jakarta/faces/component/UIInputTestCase.java @@ -0,0 +1,318 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces.component; + +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.assertTrue; + +import java.util.Iterator; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import jakarta.faces.application.FacesMessage; +import jakarta.faces.context.FacesContext; +import jakarta.faces.event.PhaseId; +import jakarta.faces.event.ValueChangeEvent; +import jakarta.faces.event.ValueChangeListener; +import jakarta.faces.validator.Validator; + +/** + *

+ * Unit tests for {@link UIInput}. + *

+ */ +public class UIInputTestCase extends UIOutputTestCase { + + // ---------------------------------------------------- Overall Test Methods + // Set up instance variables required by this test case. + @Override + @BeforeEach + public void setUp() throws Exception { + super.setUp(); + component = new UIInput(); + expectedFamily = UIInput.COMPONENT_FAMILY; + expectedRendererType = "jakarta.faces.Text"; + } + + // ------------------------------------------------- Individual Test Methods + // Test attribute-property transparency + @Override + @Test + public void testAttributesTransparency() { + super.testAttributesTransparency(); + UIInput input = (UIInput) component; + + assertEquals(input.getSubmittedValue(), input.getAttributes().get("submittedValue")); + input.setSubmittedValue("foo"); + assertEquals("foo", input.getAttributes().get("submittedValue")); + input.setSubmittedValue(null); + assertNull(input.getAttributes().get("submittedValue")); + input.getAttributes().put("submittedValue", "bar"); + assertEquals("bar", input.getSubmittedValue()); + input.getAttributes().put("submittedValue", null); + assertNull(input.getSubmittedValue()); + + input.setRequired(true); + assertEquals(Boolean.TRUE, input.getAttributes().get("required")); + input.setRequired(false); + assertEquals(Boolean.FALSE, input.getAttributes().get("required")); + input.getAttributes().put("required", Boolean.TRUE); + assertTrue(input.isRequired()); + input.getAttributes().put("required", Boolean.FALSE); + assertTrue(!input.isRequired()); + + assertEquals(input.isValid(), true); + assertEquals(input.isValid(), ((Boolean) component.getAttributes().get("valid")).booleanValue()); + input.setValid(false); + assertEquals(input.isValid(), ((Boolean) component.getAttributes().get("valid")).booleanValue()); + component.getAttributes().put("valid", Boolean.TRUE); + assertEquals(input.isValid(), ((Boolean) component.getAttributes().get("valid")).booleanValue()); + + } + + // Test the compareValues() method + @Test + public void testCompareValues() { + InputTestImpl input = new InputTestImpl(); + Object value1a = "foo"; + Object value1b = "foo"; + Object value2 = "bar"; + Object value3 = null; + + assertTrue(!input.compareValues(value1a, value1a)); + assertTrue(!input.compareValues(value1a, value1b)); + assertTrue(!input.compareValues(value1b, value1b)); + assertTrue(!input.compareValues(value2, value2)); + assertTrue(!input.compareValues(value3, value3)); + + assertTrue(input.compareValues(value1a, value2)); + assertTrue(input.compareValues(value1a, value3)); + assertTrue(input.compareValues(value2, value3)); + assertTrue(input.compareValues(value3, value2)); + } + + // Test event queuing and broadcasting (any phase listeners) + @Test + public void testEventsGeneric() { + UIInput input = (UIInput) component; + ValueChangeEvent event = new ValueChangeEvent(input, null, null); + + // Register three listeners + input.addValueChangeListener(new ValueChangeListenerTestImpl("AP0")); + input.addValueChangeListener(new ValueChangeListenerTestImpl("AP1")); + input.addValueChangeListener(new ValueChangeListenerTestImpl("AP2")); + + // Fire events and evaluate results + ValueChangeListenerTestImpl.trace(null); + input.broadcast(event); + assertEquals("/AP0/AP1/AP2", ValueChangeListenerTestImpl.trace()); + } + + // Test event queuing and broadcasting (mixed phase listeners) + @Test + public void testEventsMixed() { + UIInput input = (UIInput) component; + input.setRendererType(null); + UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null); + root.getChildren().add(input); + ValueChangeEvent event = null; + + // Register three listeners + input.addValueChangeListener(new ValueChangeListenerTestImpl("ARV")); + input.addValueChangeListener(new ValueChangeListenerTestImpl("PV")); + input.addValueChangeListener(new ValueChangeListenerTestImpl("AP")); + + ValueChangeListenerTestImpl.trace(null); + event = new ValueChangeEvent(input, null, null); + event.setPhaseId(PhaseId.APPLY_REQUEST_VALUES); + input.queueEvent(event); + + event = new ValueChangeEvent(input, null, null); + event.setPhaseId(PhaseId.PROCESS_VALIDATIONS); + input.queueEvent(event); + + event = new ValueChangeEvent(input, null, null); + event.setPhaseId(PhaseId.INVOKE_APPLICATION); + input.queueEvent(event); + + // Fire events and evaluate results + root.processDecodes(facesContext); + root.processValidators(facesContext); + root.processApplication(facesContext); + assertEquals("/ARV/PV/AP/ARV/PV/AP/ARV/PV/AP", ValueChangeListenerTestImpl.trace()); + } + + // Test listener registration and deregistration + @Test + public void testListeners() { + InputTestImpl input = new InputTestImpl(); + + input.addValueChangeListener(new ValueChangeListenerTestImpl("ARV0")); + input.addValueChangeListener(new ValueChangeListenerTestImpl("ARV1")); + input.addValueChangeListener(new ValueChangeListenerTestImpl("PV0")); + input.addValueChangeListener(new ValueChangeListenerTestImpl("PV1")); + input.addValueChangeListener(new ValueChangeListenerTestImpl("PV2")); + + ValueChangeListener listeners[] = input.getValueChangeListeners(); + assertEquals(5, listeners.length); + input.removeValueChangeListener(listeners[2]); + listeners = input.getValueChangeListeners(); + assertEquals(4, listeners.length); + } + + // Test empty listener list + @Test + public void testEmptyListeners() { + InputTestImpl input = new InputTestImpl(); + + // No listeners added, should be empty + ValueChangeListener listeners[] = input.getValueChangeListeners(); + assertEquals(0, listeners.length); + } + + // Test a pristine UIInput instance + @Override + @Test + public void testPristine() { + super.testPristine(); + UIInput input = (UIInput) component; + + assertNull(input.getSubmittedValue()); + assertTrue(!input.isRequired()); + assertTrue(input.isValid()); + assertTrue(!input.isImmediate()); + } + + // Test setting properties to invalid values + @Override + @Test + public void testPropertiesInvalid() throws Exception { + super.testPropertiesInvalid(); + UIInput input = (UIInput) component; + } + + // Test validation of a required field + @Test + public void testValidateRequired() throws Exception { + UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null); + root.getChildren().add(component); + UIInput input = (UIInput) component; + input.setRequired(true); + checkMessages(0); + + input.setValid(true); + input.setSubmittedValue("foo"); + input.validate(facesContext); + checkMessages(0); + assertTrue(input.isValid()); + + input.getAttributes().put("label", "mylabel"); + input.setValid(true); + input.setSubmittedValue(""); + input.validate(facesContext); + checkMessages(1); + assertTrue(!input.isValid()); + + Iterator messages = facesContext.getMessages(); + while (messages.hasNext()) { + FacesMessage message = messages.next(); + assertTrue(message.getSummary().indexOf("mylabel") >= 0); + } + + input.setValid(true); + input.setSubmittedValue(null); + input.validate(facesContext); + // awiner: this was formerly "checkMessages(2)", but a submitted + // value of null now explicitly means _do not validate_. + checkMessages(1); + // awiner: And this next line flipped as well + assertTrue(input.isValid()); + } + + @Test + public void testGetValueChangeListeners() throws Exception { + UIInput command = (UIInput) component; + UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null); + root.getChildren().add(command); + + ValueChangeListenerTestImpl ta1 = new ValueChangeListenerTestImpl("ta1"), ta2 = new ValueChangeListenerTestImpl("ta2"); + + command.addValueChangeListener(ta1); + command.addValueChangeListener(ta2); + ValueChangeListener[] listeners = command.getValueChangeListeners(); + assertEquals(2, listeners.length); + ValueChangeListenerTestImpl[] taListeners = (ValueChangeListenerTestImpl[]) command.getFacesListeners(ValueChangeListenerTestImpl.class); + assertTrue(taListeners != null); + } + + // --------------------------------------------------------- Support Methods + // Check that the number of queued messages equals the expected count + // and that each of them is of severity ERROR + protected void checkMessages(int expected) { + facesContext.getExceptionHandler().handle(); + int n = 0; + Iterator messages = facesContext.getMessages(); + while (messages.hasNext()) { + FacesMessage message = messages.next(); + assertEquals(FacesMessage.SEVERITY_ERROR, message.getSeverity()); + n++; + // System.err.println(message.getSummary()); + } + assertEquals(expected, n); + } + + // Create a pristine component of the type to be used in state holder tests + @Override + protected UIComponent createComponent() { + UIComponent component = new UIInput(); + component.setRendererType(null); + return component; + } + + protected boolean listenersAreEqual(FacesContext context, UIInput comp1, UIInput comp2) { + + ValueChangeListener list1[] = comp1.getValueChangeListeners(); + ValueChangeListener list2[] = comp2.getValueChangeListeners(); + assertNotNull(list1); + assertNotNull(list2); + assertEquals(list1.length, list2.length); + for (int i = 0; i < list1.length; i++) { + assertTrue(list1[i].getClass() == list2[i].getClass()); + } + return true; + } + + protected void setupNewValue(UIInput input) { + input.setSubmittedValue("foo"); + } + + protected boolean validatorsAreEqual(FacesContext context, UIInput comp1, UIInput comp2) { + + Validator list1[] = comp1.getValidators(); + Validator list2[] = comp2.getValidators(); + assertNotNull(list1); + assertNotNull(list2); + assertEquals(list1.length, list2.length); + for (int i = 0; i < list1.length; i++) { + assertTrue(list1[i].getClass() == list2[i].getClass()); + } + return true; + } +} diff --git a/impl/src/test/java/jakarta/faces/component/UIOutputAttachedObjectStateTestCase.java b/impl/src/test/java/jakarta/faces/component/UIOutputAttachedObjectStateTestCase.java new file mode 100644 index 0000000000..cabd4b5704 --- /dev/null +++ b/impl/src/test/java/jakarta/faces/component/UIOutputAttachedObjectStateTestCase.java @@ -0,0 +1,156 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces.component; + +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.assertTrue; + +import java.lang.reflect.Method; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.sun.faces.mock.MockExternalContext; +import com.sun.faces.mock.MockFacesContext; +import com.sun.faces.mock.MockHttpServletRequest; +import com.sun.faces.mock.MockHttpServletResponse; +import com.sun.faces.mock.MockLifecycle; +import com.sun.faces.mock.MockServletContext; + +import jakarta.faces.FactoryFinder; +import jakarta.faces.convert.Converter; +import jakarta.faces.convert.DateTimeConverter; + +public class UIOutputAttachedObjectStateTestCase { + + private MockFacesContext facesContext = null; + private MockServletContext servletContext; + private MockHttpServletRequest request; + private MockHttpServletResponse response; + + @BeforeEach + public void setUp() throws Exception { + facesContext = new MockFacesContext(); + facesContext = new MockFacesContext(); + + servletContext = new MockServletContext(); + servletContext.addInitParameter("appParamName", "appParamValue"); + servletContext.setAttribute("appScopeName", "appScopeValue"); + request = new MockHttpServletRequest(null); + request.setAttribute("reqScopeName", "reqScopeValue"); + response = new MockHttpServletResponse(); + + // Create something to stand-in as the InitFacesContext + new MockFacesContext(new MockExternalContext(servletContext, request, response), new MockLifecycle()); + + } + + @AfterEach + protected void tearDown() throws Exception { + FactoryFinder.releaseFactories(); + Method reInitializeFactoryManager = FactoryFinder.class.getDeclaredMethod("reInitializeFactoryManager", (Class[]) null); + reInitializeFactoryManager.setAccessible(true); + reInitializeFactoryManager.invoke(null, (Object[]) null); + } + + // ------------------------------------------------------------ Test Methods + + @Test + public void testConverterState() { + UIOutput output = new UIOutput(); + DateTimeConverter converter = new DateTimeConverter(); + converter.setPattern("MM-dd-yy"); + output.setConverter(converter); + output.markInitialState(); + assertTrue(output.initialStateMarked()); + assertTrue(converter.initialStateMarked()); + + Object result = output.saveState(facesContext); + // initial state has been marked an no changes + // have occurred, we should have null state. + assertNull(result); + + // setup the scenario again, but this time, + // update the converter pattern. + output = new UIOutput(); + converter = new DateTimeConverter(); + converter.setPattern("MM-dd-yy"); + output.setConverter(converter); + output.markInitialState(); + assertTrue(output.initialStateMarked()); + assertTrue(converter.initialStateMarked()); + + // now tweak the converter + converter.setPattern("dd-MM-yy"); + result = output.saveState(facesContext); + assertTrue(result instanceof Object[]); + Object[] state = (Object[]) result; + + // state should have a lenght of 2. The first element + // is the state from UIComponentBase, where the second + // is the converter state. The first element in this + // case should be null + assertTrue(state.length == 2); + assertTrue(state[0] == null); + assertTrue(state[1] != null); + + output = new UIOutput(); + converter = new DateTimeConverter(); + output.setConverter(converter); + + // now validate what we've restored + // first, ensure converter is null. This will + // be the case when initialState has been marked + // for the component. + output.restoreState(facesContext, state); + assertTrue(output.getConverter() != null); + assertEquals("dd-MM-yy", converter.getPattern()); + + // now validate the case where UIOutput has some event + // that adds a converter *after* initial state has been + // marked. This will cause the component to save full + // state. + output = new UIOutput(); + output.markInitialState(); + output.setConverter(converter); + assertTrue(!output.initialStateMarked()); + assertTrue(!converter.initialStateMarked()); + + result = output.saveState(facesContext); + assertNotNull(result); + + // this time, both elements in the state array will not + // be null. If we call retoreState() on a new component instance + // without setting a converter, we should have a new DateTimeConverter + // *with* the expected pattern. + assertTrue(result instanceof Object[]); + state = (Object[]) result; + assertTrue(state.length == 2); + assertTrue(state[1] instanceof StateHolderSaver); + output = new UIOutput(); + assertNull(output.getConverter()); + output.restoreState(facesContext, state); + Converter c = output.getConverter(); + assertNotNull(c); + assertTrue(c instanceof DateTimeConverter); + converter = (DateTimeConverter) c; + assertEquals("dd-MM-yy", converter.getPattern()); + } +} diff --git a/impl/src/test/java/jakarta/faces/component/UIOutputTestCase.java b/impl/src/test/java/jakarta/faces/component/UIOutputTestCase.java new file mode 100644 index 0000000000..c5a620a011 --- /dev/null +++ b/impl/src/test/java/jakarta/faces/component/UIOutputTestCase.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces.component; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + *

+ * Unit tests for {@link UIOutput}. + *

+ */ +public class UIOutputTestCase extends ValueHolderTestCaseBase { + + // ---------------------------------------------------- Overall Test Methods + // Set up instance variables required by this test case. + @Override + @BeforeEach + public void setUp() throws Exception { + super.setUp(); + component = new UIOutput(); + expectedFamily = UIOutput.COMPONENT_FAMILY; + expectedId = null; + expectedRendererType = "jakarta.faces.Text"; + } + + // ------------------------------------------------- Individual Test Methods + // Suppress lifecycle tests since we do not have a renderer + @Override + @Test + public void testLifecycleManagement() { + } + + // --------------------------------------------------------- Support Methods + // Create a pristine component of the type to be used in state holder tests + @Override + protected UIComponent createComponent() { + UIComponent component = new UIOutput(); + component.setRendererType(null); + return component; + } +} diff --git a/impl/src/test/java/jakarta/faces/component/UIPanelTestCase.java b/impl/src/test/java/jakarta/faces/component/UIPanelTestCase.java new file mode 100644 index 0000000000..d551eaaa97 --- /dev/null +++ b/impl/src/test/java/jakarta/faces/component/UIPanelTestCase.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces.component; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + *

+ * Unit tests for {@link UIPanel}. + *

+ */ +public class UIPanelTestCase extends UIComponentBaseTestCase { + + // ---------------------------------------------------- Overall Test Methods + // Set up instance variables required by this test case. + @Override + @BeforeEach + public void setUp() throws Exception { + super.setUp(); + component = new UIPanel(); + expectedFamily = UIPanel.COMPONENT_FAMILY; + expectedId = null; + expectedRendererType = null; + expectedRendersChildren = false; + } + + // ------------------------------------------------- Individual Test Methods + // Suppress lifecycle tests since we do not have a renderer + @Override + @Test + public void testLifecycleManagement() { + } + + // --------------------------------------------------------- Support Methods + // Create a pristine component of the type to be used in state holder tests + @Override + protected UIComponent createComponent() { + UIComponent component = new UIPanel(); + component.setRendererType(null); + return component; + } +} diff --git a/impl/src/test/java/jakarta/faces/component/UIParameterTest.java b/impl/src/test/java/jakarta/faces/component/UIParameterTest.java new file mode 100644 index 0000000000..e5f4c5f8c4 --- /dev/null +++ b/impl/src/test/java/jakarta/faces/component/UIParameterTest.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces.component; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.when; + +import java.lang.reflect.Method; + +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import jakarta.el.ELContext; +import jakarta.el.ValueExpression; +import jakarta.faces.context.FacesContext; + +public class UIParameterTest { + + /** + * Test isDisable method. + */ + @Test + public void testIsDisable() throws Exception { + FacesContext facesContext = Mockito.mock(FacesContext.class); + Method method = FacesContext.class.getDeclaredMethod("setCurrentInstance", FacesContext.class); + method.setAccessible(true); + method.invoke(null, facesContext); + UIParameter parameter = new UIParameter(); + parameter.setDisable(true); + assertTrue(parameter.isDisable()); + method.invoke(null, (FacesContext) null); + } + + /** + * Test isDisable method. + */ + @Test + public void testIsDisable2() throws Exception { + ELContext elContext = Mockito.mock(ELContext.class); + FacesContext facesContext = Mockito.mock(FacesContext.class); + ValueExpression valueExpression = Mockito.mock(ValueExpression.class); + Method method = FacesContext.class.getDeclaredMethod("setCurrentInstance", FacesContext.class); + method.setAccessible(true); + method.invoke(null, facesContext); + when(facesContext.getExternalContext()).thenReturn(null); + when(valueExpression.isLiteralText()).thenReturn(false); + when(facesContext.getELContext()).thenReturn(elContext); + when(valueExpression.getValue(elContext)).thenReturn(true); + UIParameter parameter = new UIParameter(); + parameter.setValueExpression("disable", valueExpression); + assertTrue(parameter.isDisable()); + method.invoke(null, (FacesContext) null); + } +} diff --git a/impl/src/test/java/jakarta/faces/component/UIParameterTestCase.java b/impl/src/test/java/jakarta/faces/component/UIParameterTestCase.java new file mode 100644 index 0000000000..a8b312a8cb --- /dev/null +++ b/impl/src/test/java/jakarta/faces/component/UIParameterTestCase.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces.component; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + *

+ * Unit tests for {@link UIParameter}. + *

+ */ +public class UIParameterTestCase extends UIComponentBaseTestCase { + + // ---------------------------------------------------- Overall Test Methods + // Set up instance variables required by this test case. + @Override + @BeforeEach + public void setUp() throws Exception { + super.setUp(); + component = new UIParameter(); + expectedFamily = UIParameter.COMPONENT_FAMILY; + expectedId = null; + expectedRendererType = null; + } + + // ------------------------------------------------- Individual Test Methods + // Test attribute-property transparency + @Override + @Test + public void testAttributesTransparency() { + super.testAttributesTransparency(); + UIParameter parameter = (UIParameter) component; + + assertEquals(parameter.getValue(), component.getAttributes().get("value")); + parameter.setValue("foo"); + assertEquals("foo", component.getAttributes().get("value")); + parameter.setValue(null); + assertNull(component.getAttributes().get("value")); + component.getAttributes().put("value", "bar"); + assertEquals("bar", parameter.getValue()); + component.getAttributes().put("value", null); + assertNull(parameter.getValue()); + + assertEquals(parameter.getName(), parameter.getAttributes().get("name")); + parameter.setName("foo"); + assertEquals("foo", parameter.getAttributes().get("name")); + parameter.setName(null); + assertNull(parameter.getAttributes().get("name")); + parameter.getAttributes().put("name", "bar"); + assertEquals("bar", parameter.getName()); + parameter.getAttributes().put("name", null); + assertNull(parameter.getName()); + } + + // Suppress lifecycle tests since we do not have a renderer + @Override + @Test + public void testLifecycleManagement() { + } + + // Test a pristine UIParameter instance + @Override + @Test + public void testPristine() { + super.testPristine(); + UIParameter parameter = (UIParameter) component; + + assertNull(parameter.getValue()); + assertNull(parameter.getName()); + } + + // Test setting properties to valid values + @Override + @Test + public void testPropertiesValid() throws Exception { + super.testPropertiesValid(); + UIParameter parameter = (UIParameter) component; + + // value + parameter.setValue("foo.bar"); + assertEquals("foo.bar", parameter.getValue()); + parameter.setValue(null); + assertNull(parameter.getValue()); + + parameter.setName("foo"); + assertEquals("foo", parameter.getName()); + parameter.setName(null); + assertNull(parameter.getName()); + } + + // --------------------------------------------------------- Support Methods + // Check that the properties on the specified components are equal + @Override + protected void checkProperties(UIComponent comp1, UIComponent comp2) { + super.checkProperties(comp1, comp2); + UIParameter p1 = (UIParameter) comp1; + UIParameter p2 = (UIParameter) comp2; + assertEquals(p1.getName(), p2.getName()); + } + + // Create a pristine component of the type to be used in state holder tests + @Override + protected UIComponent createComponent() { + UIComponent component = new UIParameter(); + component.setRendererType(null); + return component; + } +} diff --git a/impl/src/test/java/jakarta/faces/component/UISelectBooleanTestCase.java b/impl/src/test/java/jakarta/faces/component/UISelectBooleanTestCase.java new file mode 100644 index 0000000000..423bf4c708 --- /dev/null +++ b/impl/src/test/java/jakarta/faces/component/UISelectBooleanTestCase.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces.component; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + *

+ * Unit tests for {@link UISelectBoolean}. + *

+ */ +public class UISelectBooleanTestCase extends UIInputTestCase { + + // ---------------------------------------------------- Overall Test Methods + // Set up instance variables required by this test case. + @Override + @BeforeEach + public void setUp() throws Exception { + super.setUp(); + component = new UISelectBoolean(); + expectedFamily = UISelectBoolean.COMPONENT_FAMILY; + expectedRendererType = "jakarta.faces.Checkbox"; + } + + // ------------------------------------------------- Individual Test Methods + // Test attribute-property transparency + @Override + @Test + public void testAttributesTransparency() { + super.testAttributesTransparency(); + UISelectBoolean selectBoolean = (UISelectBoolean) component; + + selectBoolean.setSelected(false); + assertEquals(Boolean.FALSE, selectBoolean.getAttributes().get("selected")); + selectBoolean.setSelected(true); + assertEquals(Boolean.TRUE, selectBoolean.getAttributes().get("selected")); + selectBoolean.getAttributes().put("selected", Boolean.FALSE); + assertTrue(!selectBoolean.isSelected()); + selectBoolean.getAttributes().put("selected", Boolean.TRUE); + assertTrue(selectBoolean.isSelected()); + } + + // Test a pristine UISelectBoolean instance + @Override + @Test + public void testPristine() { + super.testPristine(); + UISelectBoolean selectBoolean = (UISelectBoolean) component; + assertTrue(!selectBoolean.isSelected()); + } + + // --------------------------------------------------------- Support Methods + // Create a pristine component of the type to be used in state holder tests + @Override + protected UIComponent createComponent() { + UIComponent component = new UISelectBoolean(); + component.setRendererType(null); + return component; + } +} diff --git a/impl/src/test/java/jakarta/faces/component/UISelectItemSub.java b/impl/src/test/java/jakarta/faces/component/UISelectItemSub.java new file mode 100644 index 0000000000..bdb949ae80 --- /dev/null +++ b/impl/src/test/java/jakarta/faces/component/UISelectItemSub.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces.component; + +public class UISelectItemSub extends UISelectItem { + + public UISelectItemSub(String value, String label, String description) { + setItemValue(value); + setItemLabel(label); + setItemDescription(description); + } +} diff --git a/impl/src/test/java/jakarta/faces/component/UISelectItemTestCase.java b/impl/src/test/java/jakarta/faces/component/UISelectItemTestCase.java new file mode 100644 index 0000000000..4cb3712e38 --- /dev/null +++ b/impl/src/test/java/jakarta/faces/component/UISelectItemTestCase.java @@ -0,0 +1,185 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces.component; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import jakarta.faces.model.SelectItem; + +/** + *

+ * Unit tests for {@link UISelectItem}. + *

+ */ +public class UISelectItemTestCase extends UIComponentBaseTestCase { + + // ---------------------------------------------------- Overall Test Methods + // Set up instance variables required by this test case. + @Override + @BeforeEach + public void setUp() throws Exception { + super.setUp(); + component = new UISelectItem(); + expectedFamily = UISelectItem.COMPONENT_FAMILY; + expectedId = null; + expectedRendererType = null; + } + + // ------------------------------------------------- Individual Test Methods + // Suppress lifecycle tests since we do not have a renderer + @Override + @Test + public void testLifecycleManagement() { + } + + // Test attribute-property transparency + @Override + @Test + public void testAttributesTransparency() { + super.testAttributesTransparency(); + UISelectItem selectItem = (UISelectItem) component; + + assertEquals(selectItem.getValue(), component.getAttributes().get("value")); + SelectItem item = new SelectItem("foo"); + selectItem.setValue(item); + assertEquals(item, component.getAttributes().get("value")); + selectItem.setValue(null); + + assertNull(component.getAttributes().get("value")); + component.getAttributes().put("value", "bar"); + assertEquals("bar", selectItem.getValue()); + component.getAttributes().put("value", null); + assertNull(selectItem.getValue()); + + assertEquals(selectItem.getItemDescription(), selectItem.getAttributes().get("itemDescription")); + selectItem.setItemDescription("foo"); + assertEquals("foo", selectItem.getAttributes().get("itemDescription")); + selectItem.setItemDescription(null); + assertNull(selectItem.getAttributes().get("itemDescription")); + selectItem.getAttributes().put("itemDescription", "bar"); + assertEquals("bar", selectItem.getItemDescription()); + selectItem.getAttributes().put("itemDescription", null); + assertNull(selectItem.getItemDescription()); + + assertEquals(selectItem.isItemDisabled(), ((Boolean) selectItem.getAttributes().get("itemDisabled")).booleanValue()); + selectItem.setItemDisabled(true); + assertTrue(((Boolean) selectItem.getAttributes().get("itemDisabled")).booleanValue()); + selectItem.setItemDisabled(false); + assertFalse(((Boolean) selectItem.getAttributes().get("itemDisabled")).booleanValue()); + selectItem.getAttributes().put("itemDisabled", Boolean.FALSE); + assertFalse(selectItem.isItemDisabled()); + selectItem.getAttributes().put("itemDisabled", Boolean.TRUE); + assertTrue(selectItem.isItemDisabled()); + + assertEquals(selectItem.getItemLabel(), selectItem.getAttributes().get("itemLabel")); + selectItem.setItemLabel("foo"); + assertEquals("foo", selectItem.getAttributes().get("itemLabel")); + selectItem.setItemLabel(null); + assertNull(selectItem.getAttributes().get("itemLabel")); + selectItem.getAttributes().put("itemLabel", "bar"); + assertEquals("bar", selectItem.getItemLabel()); + selectItem.getAttributes().put("itemLabel", null); + assertNull(selectItem.getItemLabel()); + + assertEquals(selectItem.getItemValue(), selectItem.getAttributes().get("itemValue")); + selectItem.setItemValue("foo"); + assertEquals("foo", selectItem.getAttributes().get("itemValue")); + selectItem.setItemValue(null); + assertNull(selectItem.getAttributes().get("itemValue")); + selectItem.getAttributes().put("itemValue", "bar"); + assertEquals("bar", selectItem.getItemValue()); + selectItem.getAttributes().put("itemValue", null); + assertNull(selectItem.getItemValue()); + } + + // Test a pristine UISelectItem instance + @Override + @Test + public void testPristine() { + super.testPristine(); + UISelectItem selectItem = (UISelectItem) component; + + assertNull(selectItem.getValue()); + assertNull(selectItem.getItemDescription()); + assertFalse(selectItem.isItemDisabled()); + assertNull(selectItem.getItemLabel()); + assertNull(selectItem.getItemValue()); + } + + // Test setting properties to valid values + @Override + @Test + public void testPropertiesValid() throws Exception { + super.testPropertiesValid(); + UISelectItem selectItem = (UISelectItem) component; + + // value + SelectItem item = new SelectItem("foo"); + selectItem.setValue(item); + assertEquals(item, selectItem.getValue()); + selectItem.setValue(null); + assertNull(selectItem.getValue()); + + selectItem.setItemDescription("foo"); + assertEquals("foo", selectItem.getItemDescription()); + selectItem.setItemDescription(null); + assertNull(selectItem.getItemDescription()); + + selectItem.setItemDisabled(false); + assertFalse(selectItem.isItemDisabled()); + selectItem.setItemDisabled(true); + assertTrue(selectItem.isItemDisabled()); + + selectItem.setItemLabel("foo"); + assertEquals("foo", selectItem.getItemLabel()); + selectItem.setItemLabel(null); + assertNull(selectItem.getItemLabel()); + + selectItem.setItemValue("foo"); + assertEquals("foo", selectItem.getItemValue()); + selectItem.setItemValue(null); + assertNull(selectItem.getItemValue()); + } + + // --------------------------------------------------------- Support Methods + // Check that the properties on the specified components are equal + @Override + protected void checkProperties(UIComponent comp1, UIComponent comp2) { + super.checkProperties(comp1, comp2); + UISelectItem si1 = (UISelectItem) comp1; + UISelectItem si2 = (UISelectItem) comp2; + assertEquals(si1.getItemDescription(), si2.getItemDescription()); + assertEquals(si1.isItemDisabled(), si2.isItemDisabled()); + assertEquals(si1.getItemLabel(), si2.getItemLabel()); + assertEquals(si1.getItemValue(), si2.getItemValue()); + } + + // Create a pristine component of the type to be used in state holder tests + @Override + protected UIComponent createComponent() { + UIComponent component = new UISelectItem(); + component.setRendererType(null); + return component; + } + +} diff --git a/impl/src/test/java/jakarta/faces/component/UISelectItemsTestCase.java b/impl/src/test/java/jakarta/faces/component/UISelectItemsTestCase.java new file mode 100644 index 0000000000..cde6a1e6b1 --- /dev/null +++ b/impl/src/test/java/jakarta/faces/component/UISelectItemsTestCase.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces.component; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import jakarta.faces.model.SelectItem; + +/** + *

+ * Unit tests for {@link UISelectItems}. + *

+ */ +public class UISelectItemsTestCase extends UIComponentBaseTestCase { + + // ---------------------------------------------------- Overall Test Methods + // Set up instance variables required by this test case. + @Override + @BeforeEach + public void setUp() throws Exception { + super.setUp(); + component = new UISelectItems(); + expectedFamily = UISelectItems.COMPONENT_FAMILY; + expectedId = null; + expectedRendererType = null; + } + + // ------------------------------------------------- Individual Test Methods + // Test attribute-property transparency + @Override + @Test + public void testAttributesTransparency() { + super.testAttributesTransparency(); + UISelectItems selectItems = (UISelectItems) component; + + assertEquals(selectItems.getValue(), component.getAttributes().get("value")); + SelectItem item = new SelectItem("foo"); + selectItems.setValue(item); + assertEquals(item, component.getAttributes().get("value")); + selectItems.setValue(null); + assertNull(component.getAttributes().get("value")); + component.getAttributes().put("value", "bar"); + assertEquals("bar", selectItems.getValue()); + component.getAttributes().put("value", null); + assertNull(selectItems.getValue()); + } + + // Suppress lifecycle tests since we do not have a renderer + @Override + @Test + public void testLifecycleManagement() { + } + + // Test a pristine UISelectItems instance + @Override + @Test + public void testPristine() { + super.testPristine(); + UISelectItems selectItems = (UISelectItems) component; + assertNull(selectItems.getValue()); + } + + // Test setting properties to valid values + @Override + @Test + public void testPropertiesValid() throws Exception { + super.testPropertiesValid(); + UISelectItems selectItems = (UISelectItems) component; + + // value + SelectItem item = new SelectItem("foo"); + selectItems.setValue(item); + assertEquals(item, selectItems.getValue()); + selectItems.setValue(null); + assertNull(selectItems.getValue()); + } + + // --------------------------------------------------------- Support Methods + // Create a pristine component of the type to be used in state holder tests + @Override + protected UIComponent createComponent() { + UIComponent component = new UISelectItems(); + component.setRendererType(null); + return component; + } +} diff --git a/impl/src/test/java/jakarta/faces/component/UISelectManyTestCase.java b/impl/src/test/java/jakarta/faces/component/UISelectManyTestCase.java new file mode 100644 index 0000000000..019f132a87 --- /dev/null +++ b/impl/src/test/java/jakarta/faces/component/UISelectManyTestCase.java @@ -0,0 +1,526 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces.component; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.sun.faces.renderkit.SelectItemsIterator; + +import jakarta.faces.application.FacesMessage; +import jakarta.faces.model.ListDataModel; +import jakarta.faces.model.SelectItem; +import jakarta.faces.model.SelectItemGroup; + +/** + *

+ * Unit tests for {@link UISelectMany}. + *

+ */ +public class UISelectManyTestCase extends UIInputTestCase { + + // ---------------------------------------------------- Overall Test Methods + // Set up instance variables required by this test case. + @Override + @BeforeEach + public void setUp() throws Exception { + super.setUp(); + component = new UISelectMany(); + expectedFamily = UISelectMany.COMPONENT_FAMILY; + expectedRendererType = "jakarta.faces.Listbox"; + } + + // ------------------------------------------------- Individual Test Methods + // Test the compareValues() method + @Override + @Test + public void testCompareValues() { + SelectManyTestImpl selectMany = new SelectManyTestImpl(); + Object values1a[] = new Object[] { "foo", "bar", "baz" }; + Object values1b[] = new Object[] { "foo", "baz", "bar" }; + Object values1c[] = new Object[] { "baz", "foo", "bar" }; + Object values2[] = new Object[] { "foo", "bar" }; + Object values3[] = new Object[] { "foo", "bar", "baz", "bop" }; + Object values4[] = null; + + assertTrue(!selectMany.compareValues(values1a, values1a)); + assertTrue(!selectMany.compareValues(values1a, values1b)); + assertTrue(!selectMany.compareValues(values1a, values1c)); + assertTrue(!selectMany.compareValues(values2, values2)); + assertTrue(!selectMany.compareValues(values3, values3)); + assertTrue(!selectMany.compareValues(values4, values4)); + + assertTrue(selectMany.compareValues(values1a, values2)); + assertTrue(selectMany.compareValues(values1a, values3)); + assertTrue(selectMany.compareValues(values1a, values4)); + assertTrue(selectMany.compareValues(values2, values3)); + assertTrue(selectMany.compareValues(values2, values4)); + assertTrue(selectMany.compareValues(values4, values1a)); + assertTrue(selectMany.compareValues(values4, values2)); + assertTrue(selectMany.compareValues(values4, values3)); + } + + // Test a pristine UISelectMany instance + @Override + @Test + public void testPristine() { + super.testPristine(); + UISelectMany selectMany = (UISelectMany) component; + + assertNull(selectMany.getSelectedValues()); + } + + // Test setting properties to invalid values + @Override + @Test + public void testPropertiesInvalid() throws Exception { + super.testPropertiesInvalid(); + } + + // Test validation of value against the valid list + @Test + public void testValidation() throws Exception { + // Put our component under test in a tree under a UIViewRoot + UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null); + root.getChildren().add(component); + + // Add valid options to the component under test + UISelectMany selectMany = (UISelectMany) component; + selectMany.getChildren().add(new UISelectItemSub("foo", null, null)); + selectMany.getChildren().add(new UISelectItemSub("bar", null, null)); + selectMany.getChildren().add(new UISelectItemSub("baz", null, null)); + + // Validate two values that are on the list + selectMany.setValid(true); + selectMany.setSubmittedValue(new Object[] { "foo", "baz" }); + selectMany.validate(facesContext); + assertTrue(selectMany.isValid()); + + // Validate one value on the list and one not on the list + selectMany.getAttributes().put("label", "mylabel"); + selectMany.setValid(true); + selectMany.setSubmittedValue(new Object[] { "bar", "bop" }); + selectMany.setRendererType(null); // We don't have any renderers + selectMany.validate(facesContext); + assertTrue(!selectMany.isValid()); + + Iterator messages = facesContext.getMessages(); + while (messages.hasNext()) { + FacesMessage message = messages.next(); + assertTrue(message.getSummary().indexOf("mylabel") >= 0); + } + } + + // Test validation of component with UISelectItems pointing to map + @Test + public void testValidation2() throws Exception { + // Put our component under test in a tree under a UIViewRoot + UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null); + root.getChildren().add(component); + + // Add valid options to the component under test + Map map = new HashMap<>(); + map.put("key_foo", "foo"); + map.put("key_bar", "bar"); + map.put("key_baz", "baz"); + UISelectItems items = new UISelectItems(); + items.setValue(map); + UISelectMany selectMany = (UISelectMany) component; + selectMany.getChildren().add(items); + + // Validate two values that are on the list + selectMany.setValid(true); + selectMany.setSubmittedValue(new Object[] { "foo", "baz" }); + selectMany.validate(facesContext); + assertTrue(selectMany.isValid()); + + // Validate one value on the list and one not on the list + selectMany.setValid(true); + selectMany.setSubmittedValue(new Object[] { "bar", "bop" }); + selectMany.setRendererType(null); // We don't have any renderers + selectMany.validate(facesContext); + assertTrue(!selectMany.isValid()); + } + + // Test validation of component with UISelectItems pointing to Set and the + // value of the component is Set + @Test + public void testValidation3() throws Exception { + Set items = new HashSet<>(); + items.add(new SelectItem("foo")); + items.add(new SelectItem("bar")); + items.add(new SelectItem("baz")); + Set submittedValues = new HashSet<>(); + submittedValues.add("bar"); + submittedValues.add("baz"); + Set invalidValues = new HashSet<>(); + invalidValues.add("bar"); + invalidValues.add("car"); + testValidateWithCollection(items, submittedValues, invalidValues); + } + + // Test validation of component with UISelectItems pointing to List + @Test + public void testValidation4() throws Exception { + List items = new ArrayList<>(); + items.add(new SelectItem("foo")); + items.add(new SelectItem("bar")); + items.add(new SelectItem("baz")); + List submittedValues = new ArrayList<>(); + submittedValues.add("bar"); + submittedValues.add("baz"); + ArrayList invalidValues = new ArrayList<>(); + invalidValues.add("bar"); + invalidValues.add("car"); + testValidateWithCollection(items, submittedValues, invalidValues); + } + + // Test validation of component with UISelectItems pointing to an Array + @Test + public void testValidation5() throws Exception { + // Put our component under test in a tree under a UIViewRoot + UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null); + root.getChildren().add(component); + + // Add valid options to the component under test + SelectItem[] itemsArray = { new SelectItem("foo"), new SelectItem("bar"), new SelectItem("baz") }; + UISelectItems items = new UISelectItems(); + items.setValue(itemsArray); + UISelectMany selectMany = (UISelectMany) component; + selectMany.getChildren().add(items); + + selectMany.setValid(true); + selectMany.setSubmittedValue(new String[] { "bar", "baz" }); + selectMany.validate(facesContext); + assertTrue(selectMany.isValid()); + + // Validate one value on the list and one not on the list + selectMany.setValid(true); + selectMany.setSubmittedValue(new String[] { "bar", "car" }); + selectMany.setRendererType(null); // We don't have any renderers + selectMany.validate(facesContext); + assertTrue(!selectMany.isValid()); + } + + private void testValidateWithCollection(Collection selectItems, Object validValues, Object invalidValues) throws Exception { + UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null); + root.getChildren().add(component); + + UISelectItems itemsComponent = new UISelectItems(); + itemsComponent.setValue(selectItems); + UISelectMany selectMany = (UISelectMany) component; + selectMany.setRendererType(null); + selectMany.getChildren().add(itemsComponent); + + selectMany.setValue(true); + selectMany.setSubmittedValue(validValues); + selectMany.validate(facesContext); + assertTrue(selectMany.isValid()); + selectMany.updateModel(facesContext); + + selectMany.setValid(true); + selectMany.setSubmittedValue(invalidValues); + selectMany.validate(facesContext); + assertTrue(!selectMany.isValid()); + + } + + private String legalValues[] = { "A1", "A2", "A3", "B1", "B2", "B3", "C1", "C2", "C3" }; + + private String illegalValues[] = { "D1", "D2", "Group A", "Group B", "Group C" }; + + // Test validation against a nested list of available options + @Test + public void testValidateNested() throws Exception { + // Set up UISelectMany with nested UISelectItems + UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null); + root.getChildren().add(component); + UISelectMany selectMany = (UISelectMany) component; + UISelectItems selectItems = new UISelectItems(); + selectItems.setValue(setupOptions()); + selectMany.getChildren().add(selectItems); + selectMany.setRequired(true); + checkMessages(0); + + // Verify that all legal values will validate + for (int i = 0; i < legalValues.length; i++) { + selectMany.setValid(true); + selectMany.setSubmittedValue(new Object[] { legalValues[0], legalValues[i] }); + selectMany.validate(facesContext); + assertTrue(selectMany.isValid(), "Value '" + legalValues[i] + "' found"); + checkMessages(0); + } + + // Verify that illegal values will not validate + for (int i = 0; i < illegalValues.length; i++) { + selectMany.setValid(true); + selectMany.setSubmittedValue(new Object[] { legalValues[0], illegalValues[i] }); + selectMany.validate(facesContext); + assertTrue(!selectMany.isValid(), "Value '" + illegalValues[i] + "' not found"); + checkMessages(i + 1); + } + } + + // Test validation against a nested Set of available options + @Test + public void testValidateNestedSet() throws Exception { + // Set up UISelectMany with nested UISelectItems + UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null); + root.getChildren().add(component); + UISelectMany selectMany = (UISelectMany) component; + UISelectItems selectItems = new UISelectItems(); + selectItems.setValue(setupOptionsSet()); + selectMany.getChildren().add(selectItems); + selectMany.setRequired(true); + checkMessages(0); + + // Verify that all legal values will validate + for (int i = 0; i < legalValues.length; i++) { + selectMany.setValid(true); + selectMany.setSubmittedValue(new Object[] { legalValues[0], legalValues[i] }); + selectMany.validate(facesContext); + assertTrue(selectMany.isValid(), "Value '" + legalValues[i] + "' found"); + checkMessages(0); + } + + // Verify that illegal values will not validate + for (int i = 0; i < illegalValues.length; i++) { + selectMany.setValid(true); + selectMany.setSubmittedValue(new Object[] { legalValues[0], illegalValues[i] }); + selectMany.validate(facesContext); + assertTrue(!selectMany.isValid(), "Value '" + illegalValues[i] + "' not found"); + checkMessages(i + 1); + } + } + + // Test validation of a required field + @Override + @Test + public void testValidateRequired() throws Exception { + UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null); + root.getChildren().add(component); + UISelectMany selectMany = (UISelectMany) component; + selectMany.getChildren().add(new UISelectItemSub("foo", null, null)); + selectMany.getChildren().add(new UISelectItemSub("bar", null, null)); + selectMany.getChildren().add(new UISelectItemSub("baz", null, null)); + selectMany.setRequired(true); + checkMessages(0); + + selectMany.setValid(true); + selectMany.setSubmittedValue(new Object[] { "foo" }); + selectMany.validate(facesContext); + checkMessages(0); + assertTrue(selectMany.isValid()); + + selectMany.setValid(true); + selectMany.setSubmittedValue(new Object[] { "" }); + selectMany.validate(facesContext); + checkMessages(1); + assertTrue(!selectMany.isValid()); + + selectMany.setValid(true); + selectMany.setSubmittedValue(null); + // this execution of validate shouldn't add any messages to the + // queue, since a value of null means "don't validate". This is + // different behavior than in previous versions of this + // testcase, which expected the UISelectMany.validate() to + // operate on the previously validated value, which is not + // correct. + selectMany.validate(facesContext); + checkMessages(1); + // since we're setting the submitted value to null, we don't + // want validation to occurr, therefore, the valid state of the + // componet should be as we left it. + assertTrue(selectMany.isValid()); + } + + @Test + public void testSelectItemsIterator() { + // sub test 1: non-selectitem at end + UISelectMany selectMany = (UISelectMany) component; + selectMany.getChildren().add(new UISelectItemSub("orr", null, null)); + UIParameter param = new UIParameter(); + param.setName("param"); + param.setValue("paramValue"); + selectMany.getChildren().add(param); + selectMany.getChildren().add(new UISelectItemSub("esposito", null, null)); + Iterator iter = new SelectItemsIterator<>(facesContext, selectMany); + while (iter.hasNext()) { + Object object = iter.next(); + assertTrue(object instanceof jakarta.faces.model.SelectItem); + assertTrue(((SelectItem) object).getValue().equals("orr") || ((SelectItem) object).getValue().equals("esposito")); + } + + // sub test 2: non-selectitem in middle + selectMany = new UISelectMany(); + selectMany.getChildren().add(new UISelectItemSub("gretsky", null, null)); + selectMany.getChildren().add(param); + selectMany.getChildren().add(new UISelectItemSub("howe", null, null)); + iter = new SelectItemsIterator<>(facesContext, selectMany); + while (iter.hasNext()) { + Object object = iter.next(); + assertTrue(object instanceof jakarta.faces.model.SelectItem); + assertTrue(((SelectItem) object).getValue().equals("gretsky") || ((SelectItem) object).getValue().equals("howe")); + } + + // sub test 3: Empty collection + selectMany = new UISelectMany(); + UISelectItems items = new UISelectItems(); + items.setValue(Collections.emptyList()); + selectMany.getChildren().add(items); + iter = new SelectItemsIterator<>(facesContext, selectMany); + assertTrue(!iter.hasNext()); + try { + iter.next(); + assertTrue(false); + } catch (NoSuchElementException nsee) { + // expected + } + + // sub test 4: items exposed as generic collection of non-SelectItem + // instances + Collection cItems = new ArrayList<>(5); + Collections.addAll(cItems, 0, 1, 2, 3, 4); + selectMany = new UISelectMany(); + items = new UISelectItems(); + items.setValue(cItems); + selectMany.getChildren().add(items); + iter = new SelectItemsIterator<>(facesContext, selectMany); + SelectItem previous = null; + for (int i = 0, len = cItems.size(); i < len; i++) { + assertTrue(iter.hasNext()); + SelectItem item = iter.next(); + assertNotNull(item); + assertEquals(i, item.getValue()); + assertEquals(Integer.toString(i), item.getLabel()); + assertNull(item.getDescription()); + assertFalse(item.isDisabled()); + assertTrue(item.isEscape()); + if (previous != null) { + // using fly-weight pattern make sure we use the same + // instance through out the iteration + assertTrue(item == previous); + } + previous = item; + } + assertFalse(iter.hasNext()); + try { + iter.next(); + assertTrue(false); + } catch (NoSuchElementException nsee) { + // expected + } + + // sub-test 5: DataModel providing the instances to produce + // SelectItems from + selectMany = new UISelectMany(); + items = new UISelectItems(); + items.setValue(new ListDataModel<>((List) cItems)); + selectMany.getChildren().add(items); + iter = new SelectItemsIterator<>(facesContext, selectMany); + previous = null; + for (int i = 0, len = cItems.size(); i < len; i++) { + assertTrue(iter.hasNext()); + SelectItem item = iter.next(); + assertNotNull(item); + assertEquals(i, item.getValue()); + assertEquals(Integer.toString(i), item.getLabel()); + assertNull(item.getDescription()); + assertFalse(item.isDisabled()); + assertTrue(item.isEscape()); + if (previous != null) { + // using fly-weight pattern make sure we use the same + // instance through out the iteration + assertTrue(item == previous); + } + previous = item; + } + assertFalse(iter.hasNext()); + try { + iter.next(); + assertTrue(false); + } catch (NoSuchElementException nsee) { + // expected + } + } + + // --------------------------------------------------------- Support Methods + // Create a pristine component of the type to be used in state holder tests + @Override + protected UIComponent createComponent() { + UIComponent component = new UISelectMany(); + component.setRendererType(null); + return component; + } + + @Override + protected void setupNewValue(UIInput input) { + input.setSubmittedValue(new Object[] { "foo" }); + UISelectItem si = new UISelectItem(); + si.setItemValue("foo"); + si.setItemLabel("foo label"); + input.getChildren().add(si); + } + + // Create an options list with nested groups + protected List setupOptions() { + SelectItemGroup group, subgroup; + subgroup = new SelectItemGroup("Group C"); + subgroup.setSelectItems(new SelectItem[] { new SelectItem("C1"), new SelectItem("C2"), new SelectItem("C3") }); + List options = new ArrayList<>(); + options.add(new SelectItem("A1")); + group = new SelectItemGroup("Group B"); + group.setSelectItems(new SelectItem[] { new SelectItem("B1"), subgroup, new SelectItem("B2"), new SelectItem("B3") }); + + options.add(group); + options.add(new SelectItem("A2")); + options.add(new SelectItem("A3")); + return options; + } + + // Create an options list with nested groups + protected Set setupOptionsSet() { + SelectItemGroup group, subgroup; + subgroup = new SelectItemGroup("Group C"); + subgroup.setSelectItems(new SelectItem[] { new SelectItem("C1"), new SelectItem("C2"), new SelectItem("C3") }); + Set options = new HashSet<>(); + options.add(new SelectItem("A1")); + group = new SelectItemGroup("Group B"); + group.setSelectItems(new SelectItem[] { new SelectItem("B1"), subgroup, new SelectItem("B2"), new SelectItem("B3") }); + options.add(group); + options.add(new SelectItem("A2")); + options.add(new SelectItem("A3")); + return options; + } +} diff --git a/impl/src/test/java/jakarta/faces/component/UISelectOneTestCase.java b/impl/src/test/java/jakarta/faces/component/UISelectOneTestCase.java new file mode 100644 index 0000000000..dae1499e7f --- /dev/null +++ b/impl/src/test/java/jakarta/faces/component/UISelectOneTestCase.java @@ -0,0 +1,319 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces.component; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import jakarta.faces.application.FacesMessage; +import jakarta.faces.model.SelectItem; +import jakarta.faces.model.SelectItemGroup; + +/** + *

+ * Unit tests for {@link UISelectOne}. + *

+ */ +public class UISelectOneTestCase extends UIInputTestCase { + + // ---------------------------------------------------- Overall Test Methods + // Set up instance variables required by this test case. + @Override + @BeforeEach + public void setUp() throws Exception { + super.setUp(); + component = new UISelectOne(); + expectedFamily = UISelectOne.COMPONENT_FAMILY; + expectedRendererType = "jakarta.faces.Menu"; + } + + // ------------------------------------------------- Individual Test Methods + // Test validation of value against the valid list + @Test + public void testValidation() throws Exception { + // Put our component under test in a tree under a UIViewRoot + UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null); + root.getChildren().add(component); + + // Add valid options to the component under test + UISelectOne selectOne = (UISelectOne) component; + selectOne.getChildren().add(new UISelectItemSub("foo", null, null)); + selectOne.getChildren().add(new UISelectItemSub("bar", null, null)); + selectOne.getChildren().add(new UISelectItemSub("baz", null, null)); + + // Validate a value that is on the list + selectOne.setValid(true); + selectOne.setSubmittedValue("bar"); + selectOne.setRendererType(null); // We don't have any renderers + selectOne.validate(facesContext); + assertTrue(selectOne.isValid()); + + // Validate a value that is not on the list + selectOne.getAttributes().put("label", "mylabel"); + selectOne.setValid(true); + selectOne.setSubmittedValue("bop"); + selectOne.validate(facesContext); + assertTrue(!selectOne.isValid()); + Iterator messages = facesContext.getMessages(); + while (messages.hasNext()) { + FacesMessage message = messages.next(); + assertTrue(message.getSummary().indexOf("mylabel") >= 0); + } + } + + // Test validation of component with UISelectItems pointing to map + @Test + public void testValidation2() throws Exception { + // Put our component under test in a tree under a UIViewRoot + UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null); + root.getChildren().add(component); + + // Add valid options to the component under test + Map map = new HashMap<>(); + map.put("key_foo", "foo"); + map.put("key_bar", "bar"); + map.put("key_baz", "baz"); + UISelectItems items = new UISelectItems(); + items.setValue(map); + UISelectOne selectOne = (UISelectOne) component; + selectOne.getChildren().add(items); + + selectOne.setValid(true); + selectOne.setSubmittedValue("foo"); + selectOne.validate(facesContext); + assertTrue(selectOne.isValid()); + + // Validate one value on the list and one not on the list + selectOne.setValid(true); + selectOne.setSubmittedValue("car"); + selectOne.setRendererType(null); // We don't have any renderers + selectOne.validate(facesContext); + assertTrue(!selectOne.isValid()); + } + + // Test validation of component with UISelectItems pointing to Set + @Test + public void testValidation3() throws Exception { + Set items = new HashSet<>(); + items.add(new SelectItem("foo")); + items.add(new SelectItem("bar")); + items.add(new SelectItem("baz")); + + testValidateWithCollection(items, "bar", "car"); + } + + // Test validation of component with UISelectItems pointing to List + @Test + public void testValidation4() throws Exception { + List items = new ArrayList<>(); + items.add(new SelectItem("foo")); + items.add(new SelectItem("bar")); + items.add(new SelectItem("baz")); + + testValidateWithCollection(items, "bar", "car"); + } + + // Test validation of component with UISelectItems pointing to an Array + @Test + public void testValidation5() throws Exception { + // Put our component under test in a tree under a UIViewRoot + UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null); + root.getChildren().add(component); + + // Add valid options to the component under test + SelectItem[] itemsArray = { new SelectItem("foo"), new SelectItem("bar"), new SelectItem("baz") }; + UISelectItems items = new UISelectItems(); + items.setValue(itemsArray); + UISelectOne selectOne = (UISelectOne) component; + selectOne.getChildren().add(items); + + selectOne.setValid(true); + selectOne.setSubmittedValue("foo"); + selectOne.validate(facesContext); + assertTrue(selectOne.isValid()); + + // Validate one value on the list and one not on the list + selectOne.setValid(true); + selectOne.setSubmittedValue("car"); + selectOne.setRendererType(null); // We don't have any renderers + selectOne.validate(facesContext); + assertTrue(!selectOne.isValid()); + } + + private void testValidateWithCollection(Collection selectItems, String validValue, String invalidValue) throws Exception { + UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null); + root.getChildren().add(component); + + UISelectItems itemsComponent = new UISelectItems(); + itemsComponent.setValue(selectItems); + UISelectOne selectOne = (UISelectOne) component; + selectOne.setRendererType(null); + selectOne.getChildren().add(itemsComponent); + + selectOne.setValue(true); + selectOne.setSubmittedValue(validValue); + selectOne.validate(facesContext); + assertTrue(selectOne.isValid()); + selectOne.updateModel(facesContext); + + selectOne.setValid(true); + selectOne.setSubmittedValue(invalidValue); + selectOne.validate(facesContext); + assertTrue(!selectOne.isValid()); + } + + private String legalValues[] = { "A1", "A2", "A3", "B1", "B2", "B3", "C1", "C2", "C3" }; + + private String illegalValues[] = { "D1", "D2", "Group A", "Group B", "Group C" }; + + // Test validation against a nested list of available options + @Test + public void testValidateNested() throws Exception { + // Set up UISelectOne with nested UISelectItems + UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null); + root.getChildren().add(component); + UISelectOne selectOne = (UISelectOne) component; + UISelectItems selectItems = new UISelectItems(); + selectItems.setValue(setupOptions()); + selectOne.getChildren().add(selectItems); + selectOne.setRequired(true); + checkMessages(0); + + // Verify that all legal values will validate + for (int i = 0; i < legalValues.length; i++) { + selectOne.setValid(true); + selectOne.setSubmittedValue(legalValues[i]); + selectOne.validate(facesContext); + assertTrue(selectOne.isValid(), "Value '" + legalValues[i] + "' found"); + checkMessages(0); + } + + // Verify that illegal values will not validate + for (int i = 0; i < illegalValues.length; i++) { + selectOne.setValid(true); + selectOne.setSubmittedValue(illegalValues[i]); + selectOne.validate(facesContext); + assertTrue(!selectOne.isValid(), "Value '" + illegalValues[i] + "' not found"); + checkMessages(i + 1); + } + } + + // Test validation of a required field + @Override + @Test + public void testValidateRequired() throws Exception { + UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null); + root.getChildren().add(component); + UISelectOne selectOne = (UISelectOne) component; + selectOne.getChildren().add(new UISelectItemSub("foo", null, null)); + selectOne.getChildren().add(new UISelectItemSub("bar", null, null)); + selectOne.getChildren().add(new UISelectItemSub("baz", null, null)); + selectOne.setRequired(true); + checkMessages(0); + + selectOne.setValid(true); + selectOne.setSubmittedValue("foo"); + selectOne.validate(facesContext); + checkMessages(0); + assertTrue(selectOne.isValid()); + + selectOne.setValid(true); + selectOne.setSubmittedValue(""); + selectOne.validate(facesContext); + checkMessages(1); + assertTrue(!selectOne.isValid()); + + selectOne.setValid(true); + selectOne.setSubmittedValue(null); + // awiner: see UIInputTestCase + selectOne.validate(facesContext); + checkMessages(1); + assertTrue(selectOne.isValid()); + } + + @Test + public void testSelectItemsIterator() { + // sub test 1 : non-selectItem at end + UISelectOne selectOne = (UISelectOne) component; + selectOne.getChildren().add(new UISelectItemSub("orr", null, null)); + selectOne.getChildren().add(new UISelectItemSub("esposito", null, null)); + UIParameter param = new UIParameter(); + param.setName("param"); + param.setValue("paramValue"); + selectOne.getChildren().add(param); + Iterator iter = new SelectItemsIterator(facesContext, selectOne); + while (iter.hasNext()) { + SelectItem selectItem = iter.next(); + assertTrue(selectItem.getValue().equals("orr") || selectItem.getValue().equals("esposito")); + } + + // sub test 2: non-selectitem in middle + selectOne = new UISelectOne(); + selectOne.getChildren().add(new UISelectItemSub("gretsky", null, null)); + selectOne.getChildren().add(param); + selectOne.getChildren().add(new UISelectItemSub("howe", null, null)); + iter = new SelectItemsIterator(facesContext, selectOne); + while (iter.hasNext()) { + SelectItem selectItem = iter.next(); + assertTrue(selectItem.getValue().equals("gretsky") || selectItem.getValue().equals("howe")); + } + } + + // --------------------------------------------------------- Support Methods + // Create a pristine component of the type to be used in state holder tests + @Override + protected UIComponent createComponent() { + UIComponent component = new UISelectOne(); + component.setRendererType(null); + return component; + } + + @Override + protected void setupNewValue(UIInput input) { + input.setSubmittedValue("foo"); + UISelectItem si = new UISelectItem(); + si.setItemValue("foo"); + si.setItemLabel("foo label"); + input.getChildren().add(si); + } + + // Create an options list with nested groups + protected List setupOptions() { + SelectItemGroup group, subgroup; + subgroup = new SelectItemGroup("Group C"); + subgroup.setSelectItems(new SelectItem[] { new SelectItem("C1"), new SelectItem("C2"), new SelectItem("C3") }); + List options = new ArrayList<>(); + options.add(new SelectItem("A1")); + group = new SelectItemGroup("Group B"); + group.setSelectItems(new SelectItem[] { new SelectItem("B1"), subgroup, new SelectItem("B2"), new SelectItem("B3") }); + options.add(group); + options.add(new SelectItem("A2")); + options.add(new SelectItem("A3")); + return options; + } +} diff --git a/impl/src/test/java/jakarta/faces/component/UIViewRootTest.java b/impl/src/test/java/jakarta/faces/component/UIViewRootTest.java new file mode 100644 index 0000000000..e6b4d66545 --- /dev/null +++ b/impl/src/test/java/jakarta/faces/component/UIViewRootTest.java @@ -0,0 +1,145 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces.component; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.Mockito.when; + +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import jakarta.faces.application.Application; +import jakarta.faces.application.ProjectStage; +import jakarta.faces.context.ExternalContext; +import jakarta.faces.context.FacesContext; +import jakarta.faces.event.PostConstructViewMapEvent; +import jakarta.faces.event.PreDestroyViewMapEvent; +import jakarta.servlet.http.HttpSession; + +public class UIViewRootTest { + + @Test + public void testViewMapPostConstructViewMapEvent() { + FacesContext facesContext = Mockito.mock(FacesContext.class); + Application application = Mockito.mock(Application.class); + ExternalContext externalContext = Mockito.mock(ExternalContext.class); + HttpSession httpSession = Mockito.mock(HttpSession.class); + + setFacesContext(facesContext); + + when(facesContext.getExternalContext()).thenReturn(externalContext); + when(externalContext.getApplicationMap()).thenReturn(null); + UIViewRoot viewRoot = new UIViewRoot(); + + when(facesContext.getApplication()).thenReturn(application); + when(application.getProjectStage()).thenReturn(ProjectStage.UnitTest); + application.publishEvent(facesContext, PostConstructViewMapEvent.class, UIViewRoot.class, viewRoot); + Map viewMap = viewRoot.getViewMap(); + assertNotNull(viewMap); + + setFacesContext(null); + } + + @Test + public void testViewMapPreDestroyViewMapEvent() { + FacesContext facesContext = Mockito.mock(FacesContext.class); + Application application = Mockito.mock(Application.class); + ExternalContext externalContext = Mockito.mock(ExternalContext.class); + HttpSession httpSession = Mockito.mock(HttpSession.class); + + setFacesContext(facesContext); + + when(facesContext.getExternalContext()).thenReturn(externalContext); + when(externalContext.getApplicationMap()).thenReturn(null); + UIViewRoot viewRoot = new UIViewRoot(); + + when(facesContext.getApplication()).thenReturn(application); + when(application.getProjectStage()).thenReturn(ProjectStage.UnitTest); + application.publishEvent(facesContext, PostConstructViewMapEvent.class, UIViewRoot.class, viewRoot); + when(facesContext.getViewRoot()).thenReturn(viewRoot); + application.publishEvent(facesContext, PreDestroyViewMapEvent.class, UIViewRoot.class, viewRoot); + when(facesContext.getViewRoot()).thenReturn(viewRoot); + application.publishEvent(facesContext, PreDestroyViewMapEvent.class, UIViewRoot.class, viewRoot); + + + Map viewMap = viewRoot.getViewMap(); + assertNotNull(viewMap); + viewRoot.getViewMap().clear(); + viewRoot.getViewMap().clear(); + + + setFacesContext(null); + } + + @Test + public void testViewMapSaveAndRestoreState() { + FacesContext facesContext = Mockito.mock(FacesContext.class); + Application application = Mockito.mock(Application.class); + ExternalContext externalContext = Mockito.mock(ExternalContext.class); + HttpSession httpSession = Mockito.mock(HttpSession.class); + HashMap attributes = new HashMap<>(); + HashMap sessionMap = new HashMap<>(); + + setFacesContext(facesContext); + + when(facesContext.getExternalContext()).thenReturn(externalContext); + when(externalContext.getApplicationMap()).thenReturn(null); + UIViewRoot viewRoot1 = new UIViewRoot(); + + when(facesContext.getExternalContext()).thenReturn(externalContext); + when(externalContext.getApplicationMap()).thenReturn(null); + UIViewRoot viewRoot2 = new UIViewRoot(); + + when(facesContext.getAttributes()).thenReturn(attributes); + when(facesContext.getApplication()).thenReturn(application); + when(application.getProjectStage()).thenReturn(ProjectStage.UnitTest); + when(facesContext.getExternalContext()).thenReturn(externalContext); + when(externalContext.getSessionMap()).thenReturn(sessionMap); + application.publishEvent(facesContext, PostConstructViewMapEvent.class, UIViewRoot.class, viewRoot1); + Map viewMap = viewRoot1.getViewMap(); + viewMap.put("one", "one"); + Object saved = viewRoot1.saveState(facesContext); + + /* + * Simulate our ViewMapListener. + */ + Map viewMaps = new HashMap<>(); + viewMaps.put((String) viewRoot1.getTransientStateHelper().getTransient("com.sun.faces.application.view.viewMapId"), viewMap); + sessionMap.put("com.sun.faces.application.view.activeViewMaps", viewMaps); + + viewRoot2.restoreState(facesContext, saved); + viewMap = viewRoot2.getViewMap(); + assertEquals("one", viewMap.get("one")); + + setFacesContext(null); + } + + private void setFacesContext(FacesContext facesContext) { + try { + Method method = FacesContext.class.getDeclaredMethod("setCurrentInstance", FacesContext.class); + method.setAccessible(true); + method.invoke(null, facesContext); + } catch (Exception exception) { + exception.printStackTrace(System.err); + } + } +} diff --git a/impl/src/test/java/jakarta/faces/component/UIViewRootTestCase.java b/impl/src/test/java/jakarta/faces/component/UIViewRootTestCase.java new file mode 100644 index 0000000000..8f4b1b8972 --- /dev/null +++ b/impl/src/test/java/jakarta/faces/component/UIViewRootTestCase.java @@ -0,0 +1,889 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces.component; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.List; +import java.util.Locale; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.sun.faces.mock.MockRenderKit; + +import jakarta.el.MethodExpression; +import jakarta.el.ValueExpression; +import jakarta.faces.FactoryFinder; +import jakarta.faces.application.ResourceHandler; +import jakarta.faces.context.FacesContext; +import jakarta.faces.event.PhaseEvent; +import jakarta.faces.event.PhaseId; +import jakarta.faces.event.PhaseListener; +import jakarta.faces.event.PostRestoreStateEvent; +import jakarta.faces.render.RenderKit; +import jakarta.faces.render.RenderKitFactory; + +/** + *

+ * Test case for the jakarta.faces.UIViewRoot concrete class. + *

+ */ +public class UIViewRootTestCase extends UIComponentBaseTestCase { + + public static String FACTORIES[][] = { { FactoryFinder.APPLICATION_FACTORY, "com.sun.faces.mock.MockApplicationFactory" }, + { FactoryFinder.FACES_CONTEXT_FACTORY, "com.sun.faces.mock.MockFacesContextFactory" }, + { FactoryFinder.LIFECYCLE_FACTORY, "com.sun.faces.mock.MockLifecycleFactory" }, + { FactoryFinder.RENDER_KIT_FACTORY, "com.sun.faces.mock.MockRenderKitFactory" } }; + + @Override + @BeforeEach + public void setUp() throws Exception { + FactoryFinder.releaseFactories(); + super.setUp(); + for (int i = 0, len = FACTORIES.length; i < len; i++) { + System.getProperties().remove(FACTORIES[i][0]); + } + + FactoryFinder.releaseFactories(); + int len, i = 0; + + // simulate the "faces implementation specific" part + for (i = 0, len = FACTORIES.length; i < len; i++) { + FactoryFinder.setFactory(FACTORIES[i][0], FACTORIES[i][1]); + } + + UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null); + root.setViewId("/viewId"); + facesContext.setViewRoot(root); + RenderKitFactory renderKitFactory = (RenderKitFactory) FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY); + RenderKit renderKit = new MockRenderKit(); + try { + renderKitFactory.addRenderKit(RenderKitFactory.HTML_BASIC_RENDER_KIT, renderKit); + } catch (IllegalArgumentException e) { + } + } + + /** + * Tear down instance variables required by this test case. + * + * @throws java.lang.Exception + */ + @Override + @AfterEach + public void tearDown() throws Exception { + component = null; + super.tearDown(); + } + + // ------------------------------------------------- Individual Test Methods + @Test + public void testAddGetComponentResources() { + application.addComponent("jakarta.faces.ComponentResourceContainer", Container.class.getName()); + UIViewRoot root = new UIViewRoot(); + UIOutput resource = new UIOutput(); + + // no target argument should result in target being head + root.addComponentResource(facesContext, resource); + List components = root.getComponentResources(facesContext, "head"); + assertNotNull(components); + assertTrue(components.size() == 1); + assertTrue(components.get(0) == resource); + UIOutput resource2 = new UIOutput(); + root.addComponentResource(facesContext, resource2); + assertTrue(components.size() == 2); + assertTrue(components.get(1) == resource2); + root.addComponentResource(facesContext, resource2, "form"); + components = root.getComponentResources(facesContext, "form"); + assertTrue(components.size() == 1); + root.addComponentResource(facesContext, resource2, "body"); + components = root.getComponentResources(facesContext, "body"); + assertTrue(components.size() == 1); + + // the default implementation masks the facet name values + // of head and form to ensure there are no collisions with valid + // facets by the name. Calling UIViewRoot.getFacet("head") or + // get("form") will return null. + assertNull(root.getFacet("head")); + assertNull(root.getFacet("form")); + assertNull(root.getFacet("body")); + assertNotNull(root.getFacet("jakarta_faces_location_HEAD")); + assertNotNull(root.getFacet("jakarta_faces_location_FORM")); + assertNotNull(root.getFacet("jakarta_faces_location_BODY")); + + // custom locations will also be masked + root.addComponentResource(facesContext, resource2, "gt"); + assertNotNull(root.getFacet("jakarta_faces_location_gt")); + components = root.getComponentResources(facesContext, "gt"); + assertTrue(components.size() == 1); + } + + // Tests that the resources are rendered again if renderall, @all, is set + @Test + public void testRenderAllComponentResources() { + application.addComponent("jakarta.faces.ComponentResourceContainer", Container.class.getName()); + UIViewRoot previousRoot = facesContext.getViewRoot(); + UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null); + // from partial-request we get the resources added to the ViewRoot + facesContext.setViewRoot(root); + UIOutput script = new UIOutput(); + script.getAttributes().putIfAbsent("name", "scriptTest"); + script.getAttributes().putIfAbsent("library", "scriptLibrary"); + + root.addComponentResource(facesContext, script); + + facesContext.getPartialViewContext().setPartialRequest(true); + // update @all + facesContext.getPartialViewContext().setRenderAll(true); + + final PostRestoreStateEvent postRestoreStateEvent = new PostRestoreStateEvent(root); + ResourceHandler resourceHandler = facesContext.getApplication().getResourceHandler(); + + postRestoreStateEvent.setComponent(root); + root.processEvent(postRestoreStateEvent); + + assertFalse(resourceHandler.isResourceRendered(facesContext, "scriptTest", "scriptLibrary")); + + // partial update i.e. @form + facesContext.getPartialViewContext().setRenderAll(false); + root.processEvent(postRestoreStateEvent); + + assertTrue(resourceHandler.isResourceRendered(facesContext, "scriptTest", "scriptLibrary")); + + facesContext.getPartialViewContext().setPartialRequest(false); + facesContext.getPartialViewContext().setRenderAll(false); + facesContext.setViewRoot(previousRoot); + + } + + // Test AbortProcessingException support + @Test + public void testAbortProcessingException() { + // Register three listeners, with the second one set to abort + UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null); + facesContext.setViewRoot(root); + root.addFacesListener(new ListenerTestImpl("a", false)); + root.addFacesListener(new ListenerTestImpl("b", true)); + root.addFacesListener(new ListenerTestImpl("c", false)); + + // Queue two event and check the results + ListenerTestImpl.trace(null); + EventTestImpl event1 = new EventTestImpl(root, "1"); + event1.setPhaseId(PhaseId.APPLY_REQUEST_VALUES); + root.queueEvent(event1); + EventTestImpl event2 = new EventTestImpl(root, "2"); + event2.setPhaseId(PhaseId.APPLY_REQUEST_VALUES); + root.queueEvent(event2); + root.processDecodes(facesContext); + assertEquals("/a/1/b/1/a/2/b/2", ListenerTestImpl.trace()); + } + + // Test event queuing and dequeuing during broadcasting + @Test + public void testEventBroadcasting() { + // Register a listener that will conditionally queue a new event + UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null); + facesContext.setViewRoot(root); + + root.addFacesListener(new ListenerTestImpl("t", "2", "4")); + ListenerTestImpl.trace(null); + + // Queue some events, including the trigger one + EventTestImpl event = new EventTestImpl(root, "1"); + event.setPhaseId(PhaseId.APPLY_REQUEST_VALUES); + root.queueEvent(event); + event = new EventTestImpl(root, "2"); + event.setPhaseId(PhaseId.APPLY_REQUEST_VALUES); + root.queueEvent(event); + event = new EventTestImpl(root, "3"); + event.setPhaseId(PhaseId.APPLY_REQUEST_VALUES); + root.queueEvent(event); + + // Simulate the Apply Request Values phase + root.processDecodes(facesContext); + + // Validate the results (expect 4th event to also be queued) + String expected = "/t/1/t/2/t/3/t/4"; + assertEquals(expected, ListenerTestImpl.trace()); + } + + // Test event queuing and broadcasting + @Test + public void testEventQueuing() { + // Check for correct ifecycle management processing of event broadcast + checkEventQueueing(PhaseId.APPLY_REQUEST_VALUES); + checkEventQueueing(PhaseId.PROCESS_VALIDATIONS); + checkEventQueueing(PhaseId.UPDATE_MODEL_VALUES); + checkEventQueueing(PhaseId.INVOKE_APPLICATION); + checkEventQueueing(PhaseId.ANY_PHASE); + } + + @Test + public void testLocaleFromVB() throws Exception { + UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null); + facesContext.setViewRoot(root); + ValueExpression expression = application.getExpressionFactory().createValueExpression(facesContext.getELContext(), "#{locale}", Object.class); + request.setAttribute("locale", Locale.CHINESE); + assertEquals(Locale.getDefault(), root.getLocale()); + root.setValueExpression("locale", expression); + assertEquals(Locale.CHINESE, root.getLocale()); + + // test locale from String + request.setAttribute("locale", "en"); + assertEquals(new Locale("en"), root.getLocale()); + request.setAttribute("locale", "en_IE"); + assertEquals(new Locale("en", "IE"), root.getLocale()); + request.setAttribute("locale", "en_IE_EURO"); + assertEquals(new Locale("en", "IE", "EURO"), root.getLocale()); + + root.setLocale(Locale.CANADA_FRENCH); + assertEquals(Locale.CANADA_FRENCH, root.getLocale()); + } + + @Test + public void testUninitializedInstance() throws Exception { + UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null); + facesContext.setViewRoot(root); + assertEquals(jakarta.faces.render.RenderKitFactory.HTML_BASIC_RENDER_KIT, root.getRenderKitId()); + assertEquals(Locale.getDefault(), root.getLocale()); + } + + @Test + public void testPhaseMethExpression() throws Exception { + UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null); + facesContext.setViewRoot(root); + doTestPhaseMethodExpression(root, false); + } + + @Test + public void testPhaseMethExpressionSkipping() throws Exception { + UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null); + facesContext.setViewRoot(root); + doTestPhaseMethodExpression(root, true); + } + + @Test + public void testPhaseListener() throws Exception { + UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null); + facesContext.setViewRoot(root); + doTestPhaseListener(root, false); + } + + @Test + public void testPhaseListenerSkipping() throws Exception { + UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null); + facesContext.setViewRoot(root); + doTestPhaseListener(root, true); + } + + @Test + public void testPhaseMethodExpressionAndListener() throws Exception { + UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null); + facesContext.setViewRoot(root); + doTestPhaseMethodExpressionAndListener(root, false); + } + + @Test + public void testPhaseMethodExpressionAndListenerSkipping() throws Exception { + UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null); + facesContext.setViewRoot(root); + doTestPhaseMethodExpressionAndListener(root, true); + } + + @Test + public void testPhaseMethExpressionState() throws Exception { + UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null); + facesContext.setViewRoot(root); + Object state = root.saveState(facesContext); + root = facesContext.getApplication().getViewHandler().createView(facesContext, null); + facesContext.setViewRoot(root); + root.restoreState(facesContext, state); + + doTestPhaseMethodExpression(root, false); + } + + @Test + public void testPhaseListenerState() throws Exception { + UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null); + facesContext.setViewRoot(root); + Object state = root.saveState(facesContext); + root = facesContext.getApplication().getViewHandler().createView(facesContext, null); + facesContext.setViewRoot(root); + root.restoreState(facesContext, state); + + doTestPhaseListener(root, false); + } + + @Test + public void testPhaseMethodExpressionAndListenerState() throws Exception { + UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null); + facesContext.setViewRoot(root); + Object state = root.saveState(facesContext); + root = facesContext.getApplication().getViewHandler().createView(facesContext, null); + facesContext.setViewRoot(root); + root.restoreState(facesContext, state); + + doTestPhaseMethodExpressionAndListener(root, false); + } + + @Test + public void testPhaseListenerExceptions() throws Exception { + PhaseId[] ids = { PhaseId.APPLY_REQUEST_VALUES, PhaseId.PROCESS_VALIDATIONS, PhaseId.UPDATE_MODEL_VALUES, PhaseId.INVOKE_APPLICATION, + PhaseId.RENDER_RESPONSE }; + Class[] args = new Class[] { PhaseEvent.class }; + MethodExpression beforeExpression = facesContext.getApplication().getExpressionFactory().createMethodExpression(facesContext.getELContext(), + "#{bean.beforePhase}", null, args); + MethodExpression afterExpression = facesContext.getApplication().getExpressionFactory().createMethodExpression(facesContext.getELContext(), + "#{bean.afterPhase}", null, args); + for (PhaseId id : ids) { + UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null); + PhaseListenerBean bean = new PhaseListenerBean(id, true, false); + PhaseListenerBean pl1 = new PhaseListenerBean(id); + PhaseListenerBean pl2 = new PhaseListenerBean(id); + facesContext.getExternalContext().getRequestMap().put("bean", bean); + root.setBeforePhaseListener(beforeExpression); + root.setAfterPhaseListener(afterExpression); + root.addPhaseListener(pl1); + root.addPhaseListener(pl2); + + // validate behavior + callRightLifecycleMethodGivenPhaseId(root, id); + assertTrue(bean.isBeforePhaseCalled()); + assertTrue(!bean.isAfterPhaseCalled()); + assertTrue(!pl1.isBeforePhaseCalled()); + assertTrue(!pl1.isAfterPhaseCalled()); + assertTrue(!pl2.isBeforePhaseCalled()); + assertTrue(!pl2.isAfterPhaseCalled()); + + // ensure PLs are invoked properly in the case of exceptions + root = facesContext.getApplication().getViewHandler().createView(facesContext, null); + bean = new PhaseListenerBean(id); + pl1 = new PhaseListenerBean(id, true, false); + pl2 = new PhaseListenerBean(id); + facesContext.getExternalContext().getRequestMap().put("bean", bean); + root.setBeforePhaseListener(beforeExpression); + root.setAfterPhaseListener(afterExpression); + root.addPhaseListener(pl1); + root.addPhaseListener(pl2); + + // validate behavior + callRightLifecycleMethodGivenPhaseId(root, id); + assertTrue(bean.isBeforePhaseCalled()); + assertTrue(bean.isAfterPhaseCalled()); + assertTrue(pl1.isBeforePhaseCalled()); + assertTrue(!pl1.isAfterPhaseCalled()); + assertTrue(!pl2.isBeforePhaseCalled()); + assertTrue(!pl2.isAfterPhaseCalled()); + } + } + + public void doTestPhaseMethodExpression(UIViewRoot root, boolean skipping) throws Exception { + PhaseSkipTestComponent comp = null; + if (skipping) { + comp = new PhaseSkipTestComponent(); + root.getChildren().add(comp); + facesContext.responseComplete(); + } + doTestPhaseMethodExpressionWithPhaseId(root, PhaseId.APPLY_REQUEST_VALUES); + if (skipping) { + assertTrue(!comp.isDecodeCalled()); + } + doTestPhaseMethodExpressionWithPhaseId(root, PhaseId.PROCESS_VALIDATIONS); + if (skipping) { + assertTrue(!comp.isProcessValidatorsCalled()); + } + doTestPhaseMethodExpressionWithPhaseId(root, PhaseId.UPDATE_MODEL_VALUES); + if (skipping) { + assertTrue(!comp.isProcessUpdatesCalled()); + } + doTestPhaseMethodExpressionWithPhaseId(root, PhaseId.INVOKE_APPLICATION); + doTestPhaseMethodExpressionWithPhaseId(root, PhaseId.RENDER_RESPONSE); + if (skipping) { + assertTrue(!comp.isEncodeBeginCalled()); + } + } + + public void doTestPhaseMethodExpressionWithPhaseId(UIViewRoot root, PhaseId phaseId) throws Exception { + PhaseListenerBean phaseListenerBean = new PhaseListenerBean(phaseId); + facesContext.getExternalContext().getRequestMap().put("bean", phaseListenerBean); + Class[] args = new Class[] { PhaseEvent.class }; + MethodExpression beforeExpression = facesContext.getApplication().getExpressionFactory().createMethodExpression(facesContext.getELContext(), + "#{bean.beforePhase}", null, args), + afterExpression = facesContext.getApplication().getExpressionFactory().createMethodExpression(facesContext.getELContext(), "#{bean.afterPhase}", + null, args); + root.setBeforePhaseListener(beforeExpression); + root.setAfterPhaseListener(afterExpression); + + callRightLifecycleMethodGivenPhaseId(root, phaseId); + + assertTrue(phaseListenerBean.isBeforePhaseCalled()); + assertTrue(phaseListenerBean.isAfterPhaseCalled()); + } + + public void doTestPhaseListener(UIViewRoot root, boolean skipping) throws Exception { + PhaseSkipTestComponent comp = null; + if (skipping) { + comp = new PhaseSkipTestComponent(); + root.getChildren().add(comp); + facesContext.responseComplete(); + } + doTestPhaseListenerWithPhaseId(root, PhaseId.APPLY_REQUEST_VALUES); + if (skipping) { + assertTrue(!comp.isDecodeCalled()); + } + doTestPhaseListenerWithPhaseId(root, PhaseId.PROCESS_VALIDATIONS); + if (skipping) { + assertTrue(!comp.isProcessValidatorsCalled()); + } + doTestPhaseListenerWithPhaseId(root, PhaseId.UPDATE_MODEL_VALUES); + if (skipping) { + assertTrue(!comp.isProcessUpdatesCalled()); + } + doTestPhaseListenerWithPhaseId(root, PhaseId.INVOKE_APPLICATION); + doTestPhaseListenerWithPhaseId(root, PhaseId.RENDER_RESPONSE); + if (skipping) { + assertTrue(!comp.isEncodeBeginCalled()); + } + } + + public void doTestPhaseListenerWithPhaseId(UIViewRoot root, PhaseId phaseId) throws Exception { + PhaseListenerBean phaseListener = new PhaseListenerBean(phaseId); + root.addPhaseListener(phaseListener); + + callRightLifecycleMethodGivenPhaseId(root, phaseId); + + assertTrue(phaseListener.isBeforePhaseCalled()); + assertTrue(phaseListener.isAfterPhaseCalled()); + } + + public void doTestPhaseMethodExpressionAndListener(UIViewRoot root, boolean skipping) throws Exception { + PhaseSkipTestComponent comp = null; + if (skipping) { + comp = new PhaseSkipTestComponent(); + root.getChildren().add(comp); + facesContext.responseComplete(); + } + doTestPhaseMethodExpressionAndListenerWithPhaseId(root, PhaseId.APPLY_REQUEST_VALUES); + if (skipping) { + assertTrue(!comp.isDecodeCalled()); + } + doTestPhaseMethodExpressionAndListenerWithPhaseId(root, PhaseId.PROCESS_VALIDATIONS); + if (skipping) { + assertTrue(!comp.isProcessValidatorsCalled()); + } + doTestPhaseMethodExpressionAndListenerWithPhaseId(root, PhaseId.UPDATE_MODEL_VALUES); + if (skipping) { + assertTrue(!comp.isProcessUpdatesCalled()); + } + doTestPhaseMethodExpressionAndListenerWithPhaseId(root, PhaseId.INVOKE_APPLICATION); + doTestPhaseMethodExpressionAndListenerWithPhaseId(root, PhaseId.RENDER_RESPONSE); + if (skipping) { + assertTrue(!comp.isEncodeBeginCalled()); + } + } + + public void doTestPhaseMethodExpressionAndListenerWithPhaseId(UIViewRoot root, PhaseId phaseId) throws Exception { + PhaseListenerBean phaseListener = new PhaseListenerBean(phaseId); + PhaseListenerBean phaseListenerBean = new PhaseListenerBean(phaseId); + facesContext.getExternalContext().getRequestMap().put("bean", phaseListenerBean); + Class[] args = new Class[] { PhaseEvent.class }; + MethodExpression beforeExpression = facesContext.getApplication().getExpressionFactory().createMethodExpression(facesContext.getELContext(), + "#{bean.beforePhase}", null, args), + afterExpression = facesContext.getApplication().getExpressionFactory().createMethodExpression(facesContext.getELContext(), "#{bean.afterPhase}", + null, args); + root.setBeforePhaseListener(beforeExpression); + root.setAfterPhaseListener(afterExpression); + root.addPhaseListener(phaseListener); + + callRightLifecycleMethodGivenPhaseId(root, phaseId); + + assertTrue(phaseListenerBean.isBeforePhaseCalled()); + assertTrue(phaseListenerBean.isAfterPhaseCalled()); + assertTrue(phaseListener.isBeforePhaseCalled()); + assertTrue(phaseListener.isAfterPhaseCalled()); + } + + private void checkEventQueuesSizes(List events, int applyEventsSize, int valEventsSize, int updateEventsSize, + int appEventsSize) { + List applyEvents = events.get(PhaseId.APPLY_REQUEST_VALUES.getOrdinal()); + assertEquals(applyEventsSize, applyEvents.size()); + List valEvents = events.get(PhaseId.PROCESS_VALIDATIONS.getOrdinal()); + assertEquals(valEventsSize, valEvents.size()); + List updateEvents = events.get(PhaseId.UPDATE_MODEL_VALUES.getOrdinal()); + assertEquals(updateEventsSize, updateEvents.size()); + List appEvents = events.get(PhaseId.INVOKE_APPLICATION.getOrdinal()); + assertEquals(appEventsSize, appEvents.size()); + } + + // Test Events List Clearing + @Test + public void testEventsListClear() { + UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null); + facesContext.setViewRoot(root); + EventTestImpl event1, event2, event3, event4 = null; + event1 = new EventTestImpl(root, "1"); + event1.setPhaseId(PhaseId.APPLY_REQUEST_VALUES); + root.queueEvent(event1); + event2 = new EventTestImpl(root, "2"); + event2.setPhaseId(PhaseId.PROCESS_VALIDATIONS); + root.queueEvent(event2); + event3 = new EventTestImpl(root, "3"); + event3.setPhaseId(PhaseId.UPDATE_MODEL_VALUES); + root.queueEvent(event3); + event4 = new EventTestImpl(root, "4"); + event4.setPhaseId(PhaseId.INVOKE_APPLICATION); + root.queueEvent(event4); + final Field fields[] = UIViewRoot.class.getDeclaredFields(); + Field field = null; + List events = null; + for (int i = 0; i < fields.length; ++i) { + if ("events".equals(fields[i].getName())) { + field = fields[i]; + field.setAccessible(true); + try { + events = TypedCollections.dynamicallyCastList((List) field.get(root), List.class); + } catch (Exception e) { + assertTrue(false); + } + break; + } + } + // CASE: renderReponse not set; responseComplete not set; + // check for existence of events before processDecodes + checkEventQueuesSizes(events, 1, 1, 1, 1); + root.processDecodes(facesContext); + // there should be no events + checkEventQueuesSizes(events, 0, 1, 1, 1); + + // requeue apply request event + root.queueEvent(event1); + // CASE: renderReponse set; + // check for existence of events before processValidators + checkEventQueuesSizes(events, 1, 1, 1, 1); + facesContext.renderResponse(); + root.processValidators(facesContext); + // there should be no events + checkEventQueuesSizes(events, 0, 0, 0, 0); + + // reset FacesContext + facesContext.setRenderResponse(false); + facesContext.setResponseComplete(false); + // requeue all events + root.queueEvent(event1); + root.queueEvent(event2); + root.queueEvent(event3); + root.queueEvent(event4); + try { + events = TypedCollections.dynamicallyCastList((List) field.get(root), List.class); + } catch (Exception e) { + assertTrue(false); + } + // CASE: response set; + // check for existence of events before processValidators + checkEventQueuesSizes(events, 1, 1, 1, 1); + facesContext.renderResponse(); + root.processValidators(facesContext); + // there should be no events + checkEventQueuesSizes(events, 0, 0, 0, 0); + + // reset FacesContext + facesContext.setRenderResponse(false); + facesContext.setResponseComplete(false); + // requeue all events + root.queueEvent(event1); + root.queueEvent(event2); + root.queueEvent(event3); + root.queueEvent(event4); + try { + events = TypedCollections.dynamicallyCastList((List) field.get(root), List.class); + } catch (Exception e) { + assertTrue(false); + } + // CASE: response complete; + // check for existence of events before processUpdates + checkEventQueuesSizes(events, 1, 1, 1, 1); + facesContext.responseComplete(); + root.processUpdates(facesContext); + // there should be no events + checkEventQueuesSizes(events, 0, 0, 0, 0); + + // reset FacesContext + facesContext.setRenderResponse(false); + facesContext.setResponseComplete(false); + // requeue all events + root.queueEvent(event1); + root.queueEvent(event2); + root.queueEvent(event3); + root.queueEvent(event4); + try { + events = TypedCollections.dynamicallyCastList((List) field.get(root), List.class); + } catch (Exception e) { + assertTrue(false); + } + // CASE: response complete; + // check for existence of events before processApplication + checkEventQueuesSizes(events, 1, 1, 1, 1); + facesContext.responseComplete(); + root.processApplication(facesContext); + // there should be no events + checkEventQueuesSizes(events, 0, 0, 0, 0); + + // finally, get the internal events list one more time + // to make sure it is null + try { + events = TypedCollections.dynamicallyCastList((List) field.get(root), List.class); + } catch (Exception e) { + assertTrue(false); + } + assertNull(events); + } + + private void callRightLifecycleMethodGivenPhaseId(UIViewRoot root, PhaseId phaseId) throws Exception { + if (phaseId.getOrdinal() == PhaseId.APPLY_REQUEST_VALUES.getOrdinal()) { + root.processDecodes(facesContext); + } else if (phaseId.getOrdinal() == PhaseId.PROCESS_VALIDATIONS.getOrdinal()) { + root.processValidators(facesContext); + } else if (phaseId.getOrdinal() == PhaseId.UPDATE_MODEL_VALUES.getOrdinal()) { + root.processUpdates(facesContext); + } else if (phaseId.getOrdinal() == PhaseId.INVOKE_APPLICATION.getOrdinal()) { + root.processApplication(facesContext); + } else if (phaseId.getOrdinal() == PhaseId.RENDER_RESPONSE.getOrdinal()) { + root.encodeBegin(facesContext); + root.encodeEnd(facesContext); + } + } + + // --------------------------------------------------------- Support Methods + private void checkEventQueueing(PhaseId phaseId) { + + // NOTE: Current semantics for ANY_PHASE listeners is that + // the event should be delivered exactly once, so the existence + // of such a listener does not cause the event to remain queued. + // Therefore, the expected string is the same as for any + // phase-specific listener, and it should get matched after + // Apply Request Values processing since that is first phase + // for which events are fired + // Register an event listener for the specified phase id + UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null); + facesContext.setViewRoot(root); + EventTestImpl event = null; + ListenerTestImpl listener = new ListenerTestImpl("t"); + root.addFacesListener(listener); + + // Queue some events to be processed + event = new EventTestImpl(root, "1"); + event.setPhaseId(phaseId); + root.queueEvent(event); + event = new EventTestImpl(root, "2"); + event.setPhaseId(phaseId); + root.queueEvent(event); + String expected = "/t/1/t/2"; + + // Fire off the relevant lifecycle methods and check expected results + ListenerTestImpl.trace(null); + assertEquals("", ListenerTestImpl.trace()); + root.processDecodes(facesContext); + if (PhaseId.APPLY_REQUEST_VALUES.equals(phaseId) || PhaseId.ANY_PHASE.equals(phaseId)) { + assertEquals(expected, ListenerTestImpl.trace()); + } else { + assertEquals("", ListenerTestImpl.trace()); + } + root.processValidators(facesContext); + if (PhaseId.PROCESS_VALIDATIONS.equals(phaseId) || PhaseId.APPLY_REQUEST_VALUES.equals(phaseId) || PhaseId.APPLY_REQUEST_VALUES.equals(phaseId) + || PhaseId.ANY_PHASE.equals(phaseId)) { + assertEquals(expected, ListenerTestImpl.trace()); + } else { + assertEquals("", ListenerTestImpl.trace()); + } + root.processUpdates(facesContext); + if (PhaseId.UPDATE_MODEL_VALUES.equals(phaseId) || PhaseId.PROCESS_VALIDATIONS.equals(phaseId) || PhaseId.APPLY_REQUEST_VALUES.equals(phaseId) + || PhaseId.ANY_PHASE.equals(phaseId)) { + assertEquals(expected, ListenerTestImpl.trace()); + } else { + assertEquals("", ListenerTestImpl.trace()); + } + root.processApplication(facesContext); + assertEquals(expected, ListenerTestImpl.trace()); + } + + // These overrides are necessary because our normal setup + // calls releaseFactories, which makes it impossible to get clientIds. + @Override + @Test + public void testInvokeOnComponentPositive() throws Exception { + super.setUp(); + super.testInvokeOnComponentPositive(); + } + + @Override + @Test + public void testInvokeOnComponentNegative() throws Exception { + super.setUp(); + super.testInvokeOnComponentNegative(); + } + + @Override + @Test + public void testInvokeOnComponentWithPrependId() throws Exception { + super.setUp(); + super.testInvokeOnComponentWithPrependId(); + } + + @Override + @Test + public void testChildrenListAfterAddViewPublish() { + // overridding to do nothing. UIViewRoot is a special cases + // and there should always only be on UIViewRoot in a tree + } + + @Override + @Test + public void testFacetMapAfterAddViewPublish() { + // overridding to do nothing. UIViewRoot is a special cases + // and there should always only be on UIViewRoot in a tree + } + + // Check that the properties on the specified components are equal + @Override + protected void checkProperties(UIComponent comp1, UIComponent comp2) { + super.checkProperties(comp1, comp2); + UIViewRoot vr1 = (UIViewRoot) comp1; + UIViewRoot vr2 = (UIViewRoot) comp2; + assertEquals(vr2.getRenderKitId(), vr2.getRenderKitId()); + assertEquals(vr1.getViewId(), vr2.getViewId()); + assertEquals(vr1.getLocale(), vr2.getLocale()); + } + + // Create a pristine component of the type to be used in state holder tests + @Override + protected UIComponent createComponent() { + UIComponent component = new UIViewRoot(); + component.setRendererType(null); + return component; + } + + public static class PhaseListenerBean extends Object implements PhaseListener { + + private static final long serialVersionUID = 1L; + + private boolean beforePhaseCalled = false; + private boolean afterPhaseCalled = false; + private PhaseId phaseId = null; + private boolean exceptionBefore; + private boolean exceptionAfter; + + public PhaseListenerBean(PhaseId phaseId) { + this.phaseId = phaseId; + } + + public PhaseListenerBean(PhaseId phaseId, boolean exceptionBefore, boolean exceptionAfter) { + this(phaseId); + this.exceptionBefore = exceptionBefore; + this.exceptionAfter = exceptionAfter; + } + + public boolean isBeforePhaseCalled() { + return beforePhaseCalled; + } + + public boolean isAfterPhaseCalled() { + return afterPhaseCalled; + } + + @Override + public void beforePhase(PhaseEvent e) { + beforePhaseCalled = true; + if (exceptionBefore) { + throw new RuntimeException(); + } + } + + @Override + public void afterPhase(PhaseEvent e) { + afterPhaseCalled = true; + if (exceptionAfter) { + throw new RuntimeException(); + } + } + + @Override + public PhaseId getPhaseId() { + return phaseId; + } + } + + public static class PhaseSkipTestComponent extends UIInput { + + private boolean decodeCalled = false; + + @Override + public void decode(FacesContext context) { + decodeCalled = true; + } + + public boolean isDecodeCalled() { + return decodeCalled; + } + + private boolean encodeBeginCalled = false; + + @Override + public void encodeBegin(FacesContext context) throws IOException { + encodeBeginCalled = true; + } + + public boolean isEncodeBeginCalled() { + return encodeBeginCalled; + } + + private boolean processValidatorsCalled = false; + + @Override + public void processValidators(FacesContext context) { + processValidatorsCalled = true; + } + + public boolean isProcessValidatorsCalled() { + return processValidatorsCalled; + } + + private boolean processUpdatesCalled = false; + + @Override + public void processUpdates(FacesContext context) { + processUpdatesCalled = true; + } + + public boolean isProcessUpdatesCalled() { + return processUpdatesCalled; + } + } + + public static class Container extends UIPanel { + + @Override + public void encodeAll(FacesContext context) throws IOException { + } + } +} diff --git a/impl/src/test/java/jakarta/faces/component/ValueHolderTestCaseBase.java b/impl/src/test/java/jakarta/faces/component/ValueHolderTestCaseBase.java new file mode 100644 index 0000000000..0e7bda4afc --- /dev/null +++ b/impl/src/test/java/jakarta/faces/component/ValueHolderTestCaseBase.java @@ -0,0 +1,255 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces.component; + +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.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import jakarta.faces.component.html.HtmlInputText; +import jakarta.faces.convert.LongConverter; +import jakarta.faces.convert.NumberConverter; +import jakarta.faces.convert.ShortConverter; + +/** + *

+ * Unit tests for {@link ValueHolder}. Any test case for a component class that implements {@link ValueHolder} should + * extend this class. + *

+ */ +public abstract class ValueHolderTestCaseBase extends UIComponentBaseTestCase { + + // ---------------------------------------------------- Overall Test Methods + // Set up instance variables required by this test case. + @Override + @BeforeEach + public void setUp() throws Exception { + super.setUp(); + component = new UIOutput(); + expectedId = null; + expectedRendererType = "Text"; + } + + // ------------------------------------------------- Individual Test Methods + @Test + public void testAttributesTransparencyNonDeterministic() throws Exception { + final int numThreads = 30; + final Boolean outcomes[] = new Boolean[numThreads]; + Runnable runnables[] = new Runnable[numThreads]; + int i = 0; + + for (i = 0; i < outcomes.length; i++) { + outcomes[i] = null; + } + + for (i = 0; i < runnables.length; i++) { + runnables[i] = new Runnable() { + @Override + public void run() { + int threadNum = 0; + try { + threadNum = Integer.valueOf(Thread.currentThread().getName()).intValue(); + } catch (NumberFormatException ex) { + fail("Expected thread name to be an integer"); + } + // Even threadNums use HtmlInputText, odd use this component + boolean isEven = threadNum % 2 == 0; + ValueHolder vh = null; + UIComponent newComp = null; + if (isEven) { + newComp = new HtmlInputText(); + vh = (ValueHolder) newComp; + } else { + try { + newComp = ValueHolderTestCaseBase.this.component.getClass().getDeclaredConstructor() + .newInstance(); + vh = (ValueHolder) newComp; + + } catch (IllegalArgumentException | ReflectiveOperationException | SecurityException ex) { + fail("Can't instantiate class of " + ValueHolderTestCaseBase.this.component.getClass().getName()); + } + } + try { + boolean result = doTestAttributesTransparency(vh, newComp); + outcomes[threadNum] = Boolean.valueOf(result); + } catch (Throwable e) { + e.printStackTrace(); + outcomes[threadNum] = Boolean.FALSE; + } + } + }; + } +// clearDescriptors(); + Thread thread = null; + for (i = 0; i < runnables.length; i++) { + thread = new Thread(runnables[i], "" + i); + thread.start(); + } + + // Keep polling the outcomes array until there are no nulls. + boolean foundNull = false; + while (!foundNull) { + for (i = 0; i < outcomes.length; i++) { + if (null != outcomes[i]) { + foundNull = true; + } + } + Thread.sleep(500); + } + + for (i = 0; i < outcomes.length; i++) { + if (!outcomes[i].booleanValue()) { + fail("Thread " + i + " failed"); + } + } + } + + @Override + @Test + public void testAttributesTransparency() { + super.testAttributesTransparency(); + ValueHolder vh = (ValueHolder) component; + doTestAttributesTransparency(vh, component); + } + + // Test attribute-property transparency + public boolean doTestAttributesTransparency(ValueHolder vh, UIComponent newComp) { + assertEquals(vh.getValue(), newComp.getAttributes().get("value")); + vh.setValue("foo"); + assertEquals("foo", newComp.getAttributes().get("value")); + vh.setValue(null); + assertNull(newComp.getAttributes().get("value")); + newComp.getAttributes().put("value", "bar"); + assertEquals("bar", vh.getValue()); + newComp.getAttributes().put("value", null); + assertNull(vh.getValue()); + + assertEquals(vh.getConverter(), newComp.getAttributes().get("converter")); + vh.setConverter(new LongConverter()); + assertNotNull(newComp.getAttributes().get("converter")); + assertTrue(newComp.getAttributes().get("converter") instanceof LongConverter); + vh.setConverter(null); + assertNull(newComp.getAttributes().get("converter")); + newComp.getAttributes().put("converter", new ShortConverter()); + assertNotNull(vh.getConverter()); + assertTrue(vh.getConverter() instanceof ShortConverter); + newComp.getAttributes().put("converter", null); + assertNull(vh.getConverter()); + + return true; + } + + // Suppress lifecycle tests since we do not have a renderer + @Override + @Test + public void testLifecycleManagement() { + } + + // Test a pristine ValueHolderBase instance + @Override + @Test + public void testPristine() { + super.testPristine(); + ValueHolder vh = (ValueHolder) component; + + // Validate properties + assertNull(vh.getValue()); + assertNull(vh.getConverter()); + } + + // Test setting properties to valid values + @Override + @Test + public void testPropertiesValid() throws Exception { + super.testPropertiesValid(); + ValueHolder vh = (ValueHolder) component; + + // value + vh.setValue("foo.bar"); + assertEquals("foo.bar", vh.getValue()); + vh.setValue(null); + assertNull(vh.getValue()); + + // converter + vh.setConverter(new LongConverter()); + assertTrue(vh.getConverter() instanceof LongConverter); + vh.setConverter(null); + assertNull(vh.getConverter()); + } + + // --------------------------------------------------------- Support Methods + // Check that the properties of the NumberConverters are equal + protected void checkNumberConverter(NumberConverter nc1, NumberConverter nc2) { + assertEquals(nc1.getCurrencyCode(), nc2.getCurrencyCode()); + assertEquals(nc1.getCurrencySymbol(), nc2.getCurrencySymbol()); + assertEquals(nc1.isGroupingUsed(), nc2.isGroupingUsed()); + assertEquals(nc1.isIntegerOnly(), nc2.isIntegerOnly()); + assertEquals(nc1.getMaxFractionDigits(), nc2.getMaxFractionDigits()); + assertEquals(nc1.getMaxIntegerDigits(), nc2.getMaxIntegerDigits()); + assertEquals(nc1.getMinFractionDigits(), nc2.getMinFractionDigits()); + assertEquals(nc1.getMinIntegerDigits(), nc2.getMinIntegerDigits()); + assertEquals(nc1.getLocale(), nc2.getLocale()); + assertEquals(nc1.getPattern(), nc2.getPattern()); + assertEquals(nc1.getType(), nc2.getType()); + } + + // Check that the properties on the specified components are equal + @Override + protected void checkProperties(UIComponent comp1, UIComponent comp2) { + super.checkProperties(comp1, comp2); + ValueHolder vh1 = (ValueHolder) comp1; + ValueHolder vh2 = (ValueHolder) comp2; + assertEquals(vh1.getValue(), vh2.getValue()); + checkNumberConverter((NumberConverter) vh1.getConverter(), (NumberConverter) vh2.getConverter()); + } + + // Create and configure a NumberConverter + protected NumberConverter createNumberConverter() { + NumberConverter nc = new NumberConverter(); + nc.setCurrencyCode("USD"); + nc.setCurrencySymbol("$"); + nc.setGroupingUsed(false); + nc.setIntegerOnly(true); + nc.setMaxFractionDigits(2); + nc.setMaxIntegerDigits(10); + nc.setMinFractionDigits(2); + nc.setMinIntegerDigits(5); + nc.setType("currency"); + return nc; + } + + protected void checkNumberConverters(NumberConverter nc1, NumberConverter nc2) { + assertNotNull(nc1); + assertNotNull(nc2); + assertEquals(nc1.getCurrencyCode(), nc2.getCurrencyCode()); + assertEquals(nc1.getCurrencySymbol(), nc2.getCurrencySymbol()); + assertEquals(nc1.isGroupingUsed(), nc2.isGroupingUsed()); + assertEquals(nc1.isIntegerOnly(), nc2.isIntegerOnly()); + assertEquals(nc1.getMaxFractionDigits(), nc2.getMaxFractionDigits()); + assertEquals(nc1.getMaxIntegerDigits(), nc2.getMaxIntegerDigits()); + assertEquals(nc1.getMinFractionDigits(), nc2.getMinFractionDigits()); + assertEquals(nc1.getMinIntegerDigits(), nc2.getMinIntegerDigits()); + assertEquals(nc1.getLocale(), nc2.getLocale()); + assertEquals(nc1.getPattern(), nc2.getPattern()); + assertEquals(nc1.getType(), nc2.getType()); + } +} diff --git a/impl/src/test/java/jakarta/faces/convert/BigIntegerConverterTest.java b/impl/src/test/java/jakarta/faces/convert/BigIntegerConverterTest.java new file mode 100644 index 0000000000..70f8a6c468 --- /dev/null +++ b/impl/src/test/java/jakarta/faces/convert/BigIntegerConverterTest.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces.convert; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.math.BigInteger; + +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import jakarta.faces.component.UIPanel; +import jakarta.faces.context.FacesContext; + +/** + * The JUnit tests for the BigIntegerConverter class. + */ +public class BigIntegerConverterTest { + + /** + * Test getAsObject method. + */ + @Test + public void testGetAsObject() { + BigIntegerConverter converter = new BigIntegerConverter(); + assertThrows(NullPointerException.class, () -> converter.getAsObject(null, null, null)); + } + + /** + * Test getAsObject method. + */ + @Test + public void testGetAsObject2() { + BigIntegerConverter converter = new BigIntegerConverter(); + FacesContext facesContext = Mockito.mock(FacesContext.class); + assertNull(converter.getAsObject(facesContext, new UIPanel(), null)); + } + + /** + * Test getAsObject method. + */ + @Test + public void testGetAsObject3() { + BigIntegerConverter converter = new BigIntegerConverter(); + FacesContext facesContext = Mockito.mock(FacesContext.class); + assertNull(converter.getAsObject(facesContext, new UIPanel(), " ")); + } + + /** + * Test getAsObject method. + */ + @Test + public void testGetAsObject4() { + BigIntegerConverter converter = new BigIntegerConverter(); + FacesContext facesContext = Mockito.mock(FacesContext.class); + assertEquals(BigInteger.valueOf(123), converter.getAsObject(facesContext, new UIPanel(), "123")); + } + + /** + * Test getAsString method. + */ + @Test + public void testGetAsString() { + BigIntegerConverter converter = new BigIntegerConverter(); + assertThrows(NullPointerException.class, () -> converter.getAsString(null, null, null)); + } + + /** + * Test getAsString method. + */ + @Test + public void testGetAsString2() { + BigIntegerConverter converter = new BigIntegerConverter(); + FacesContext facesContext = Mockito.mock(FacesContext.class); + assertEquals("", converter.getAsString(facesContext, new UIPanel(), null)); + } + + /** + * Test getAsString method. + */ + @Test + public void testGetAsString3() { + BigIntegerConverter converter = new BigIntegerConverter(); + FacesContext facesContext = Mockito.mock(FacesContext.class); + assertEquals("123", converter.getAsString(facesContext, new UIPanel(), "123")); + } + + /** + * Test getAsString method. + */ + @Test + public void testGetAsString4() { + BigIntegerConverter converter = new BigIntegerConverter(); + FacesContext facesContext = Mockito.mock(FacesContext.class); + assertEquals("123", converter.getAsString(facesContext, new UIPanel(), BigInteger.valueOf(123))); + } +} diff --git a/impl/src/test/java/jakarta/faces/convert/DoubleConverterTest.java b/impl/src/test/java/jakarta/faces/convert/DoubleConverterTest.java new file mode 100644 index 0000000000..976600b158 --- /dev/null +++ b/impl/src/test/java/jakarta/faces/convert/DoubleConverterTest.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces.convert; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import jakarta.faces.component.UIPanel; +import jakarta.faces.context.FacesContext; + +/** + * The JUnit tests for the DoubleConverter class. + */ +public class DoubleConverterTest { + + /** + * Test getAsObject method. + */ + @Test + public void testGetAsObject() { + DoubleConverter converter = new DoubleConverter(); + assertThrows(NullPointerException.class, () -> converter.getAsObject(null, null, null)); + } + + /** + * Test getAsObject method. + */ + @Test + public void testGetAsObject2() { + DoubleConverter converter = new DoubleConverter(); + FacesContext facesContext = Mockito.mock(FacesContext.class); + assertNull(converter.getAsObject(facesContext, new UIPanel(), null)); + } + + /** + * Test getAsObject method. + */ + @Test + public void testGetAsObject3() { + DoubleConverter converter = new DoubleConverter(); + FacesContext facesContext = Mockito.mock(FacesContext.class); + assertNull(converter.getAsObject(facesContext, new UIPanel(), " ")); + } + + /** + * Test getAsObject method. + */ + @Test + public void testGetAsObject4() { + DoubleConverter converter = new DoubleConverter(); + FacesContext facesContext = Mockito.mock(FacesContext.class); + assertEquals(Double.valueOf("12.3"), converter.getAsObject(facesContext, new UIPanel(), "12.3")); + } + + /** + * Test getAsString method. + */ + @Test + public void testGetAsString() { + DoubleConverter converter = new DoubleConverter(); + assertThrows(NullPointerException.class, () -> converter.getAsString(null, null, null)); + } + + /** + * Test getAsString method. + */ + @Test + public void testGetAsString2() { + DoubleConverter converter = new DoubleConverter(); + FacesContext facesContext = Mockito.mock(FacesContext.class); + assertEquals("", converter.getAsString(facesContext, new UIPanel(), null)); + } + + /** + * Test getAsString method. + */ + @Test + public void testGetAsString3() { + DoubleConverter converter = new DoubleConverter(); + FacesContext facesContext = Mockito.mock(FacesContext.class); + assertEquals("12.3", converter.getAsString(facesContext, new UIPanel(), "12.3")); + } + + /** + * Test getAsString method. + */ + @Test + public void testGetAsString4() { + DoubleConverter converter = new DoubleConverter(); + FacesContext facesContext = Mockito.mock(FacesContext.class); + assertEquals("12.3", converter.getAsString(facesContext, new UIPanel(), Double.valueOf("12.3"))); + } +} diff --git a/impl/src/test/java/jakarta/faces/convert/FloatConverterTest.java b/impl/src/test/java/jakarta/faces/convert/FloatConverterTest.java new file mode 100644 index 0000000000..446940f431 --- /dev/null +++ b/impl/src/test/java/jakarta/faces/convert/FloatConverterTest.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces.convert; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import jakarta.faces.component.UIPanel; +import jakarta.faces.context.FacesContext; + +/** + * The JUnit tests for the FloatConverter class. + */ +public class FloatConverterTest { + + /** + * Test getAsObject method. + */ + @Test + public void testGetAsObject() { + FloatConverter converter = new FloatConverter(); + assertThrows(NullPointerException.class, () -> converter.getAsObject(null, null, null)); + } + + /** + * Test getAsObject method. + */ + @Test + public void testGetAsObject2() { + FloatConverter converter = new FloatConverter(); + FacesContext facesContext = Mockito.mock(FacesContext.class); + assertNull(converter.getAsObject(facesContext, new UIPanel(), null)); + } + + /** + * Test getAsObject method. + */ + @Test + public void testGetAsObject3() { + FloatConverter converter = new FloatConverter(); + FacesContext facesContext = Mockito.mock(FacesContext.class); + assertNull(converter.getAsObject(facesContext, new UIPanel(), " ")); + } + + /** + * Test getAsObject method. + */ + @Test + public void testGetAsObject4() { + FloatConverter converter = new FloatConverter(); + FacesContext facesContext = Mockito.mock(FacesContext.class); + assertEquals(Float.valueOf("12.3"), converter.getAsObject(facesContext, new UIPanel(), "12.3")); + } + + /** + * Test getAsString method. + */ + @Test + public void testGetAsString() { + FloatConverter converter = new FloatConverter(); + assertThrows(NullPointerException.class, () -> converter.getAsString(null, null, null)); + } + + /** + * Test getAsString method. + */ + @Test + public void testGetAsString2() { + FloatConverter converter = new FloatConverter(); + FacesContext facesContext = Mockito.mock(FacesContext.class); + assertEquals("", converter.getAsString(facesContext, new UIPanel(), null)); + } + + /** + * Test getAsString method. + */ + @Test + public void testGetAsString3() { + FloatConverter converter = new FloatConverter(); + FacesContext facesContext = Mockito.mock(FacesContext.class); + assertEquals("12.3", converter.getAsString(facesContext, new UIPanel(), "12.3")); + } + + /** + * Test getAsString method. + */ + @Test + public void testGetAsString4() { + FloatConverter converter = new FloatConverter(); + FacesContext facesContext = Mockito.mock(FacesContext.class); + assertEquals("12.3", converter.getAsString(facesContext, new UIPanel(), Float.valueOf("12.3"))); + } +} diff --git a/impl/src/test/java/jakarta/faces/convert/IntegerConverterTest.java b/impl/src/test/java/jakarta/faces/convert/IntegerConverterTest.java new file mode 100644 index 0000000000..4427b9441d --- /dev/null +++ b/impl/src/test/java/jakarta/faces/convert/IntegerConverterTest.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces.convert; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import jakarta.faces.component.UIPanel; +import jakarta.faces.context.FacesContext; + +/** + * The JUnit tests for the IntegerConverter class. + */ +public class IntegerConverterTest { + + /** + * Test getAsObject method. + */ + @Test + public void testGetAsObject() { + IntegerConverter converter = new IntegerConverter(); + assertThrows(NullPointerException.class, () -> converter.getAsObject(null, null, null)); + } + + /** + * Test getAsObject method. + */ + @Test + public void testGetAsObject2() { + IntegerConverter converter = new IntegerConverter(); + FacesContext facesContext = Mockito.mock(FacesContext.class); + assertNull(converter.getAsObject(facesContext, new UIPanel(), null)); + } + + /** + * Test getAsObject method. + */ + @Test + public void testGetAsObject3() { + IntegerConverter converter = new IntegerConverter(); + FacesContext facesContext = Mockito.mock(FacesContext.class); + assertNull(converter.getAsObject(facesContext, new UIPanel(), " ")); + } + + /** + * Test getAsObject method. + */ + @Test + public void testGetAsObject4() { + IntegerConverter converter = new IntegerConverter(); + FacesContext facesContext = Mockito.mock(FacesContext.class); + assertEquals(Integer.valueOf("123"), converter.getAsObject(facesContext, new UIPanel(), "123")); + } + + /** + * Test getAsString method. + */ + @Test + public void testGetAsString() { + IntegerConverter converter = new IntegerConverter(); + assertThrows(NullPointerException.class, () -> converter.getAsString(null, null, null)); + } + + /** + * Test getAsString method. + */ + @Test + public void testGetAsString2() { + IntegerConverter converter = new IntegerConverter(); + FacesContext facesContext = Mockito.mock(FacesContext.class); + assertEquals("", converter.getAsString(facesContext, new UIPanel(), null)); + } + + /** + * Test getAsString method. + */ + @Test + public void testGetAsString3() { + IntegerConverter converter = new IntegerConverter(); + FacesContext facesContext = Mockito.mock(FacesContext.class); + assertEquals("123", converter.getAsString(facesContext, new UIPanel(), "123")); + } + + /** + * Test getAsString method. + */ + @Test + public void testGetAsString4() { + IntegerConverter converter = new IntegerConverter(); + FacesContext facesContext = Mockito.mock(FacesContext.class); + assertEquals("123", converter.getAsString(facesContext, new UIPanel(), Integer.valueOf("123"))); + } +} diff --git a/impl/src/test/java/jakarta/faces/convert/LongConverterTest.java b/impl/src/test/java/jakarta/faces/convert/LongConverterTest.java new file mode 100644 index 0000000000..27e7f8fa6f --- /dev/null +++ b/impl/src/test/java/jakarta/faces/convert/LongConverterTest.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces.convert; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import jakarta.faces.component.UIPanel; +import jakarta.faces.context.FacesContext; + +/** + * The JUnit tests for the LongConverter class. + */ +public class LongConverterTest { + + /** + * Test getAsObject method. + */ + @Test + public void testGetAsObject() { + LongConverter converter = new LongConverter(); + assertThrows(NullPointerException.class, () -> converter.getAsObject(null, null, null)); + } + + /** + * Test getAsObject method. + */ + @Test + public void testGetAsObject2() { + LongConverter converter = new LongConverter(); + FacesContext facesContext = Mockito.mock(FacesContext.class); + assertNull(converter.getAsObject(facesContext, new UIPanel(), null)); + } + + /** + * Test getAsObject method. + */ + @Test + public void testGetAsObject3() { + LongConverter converter = new LongConverter(); + FacesContext facesContext = Mockito.mock(FacesContext.class); + assertNull(converter.getAsObject(facesContext, new UIPanel(), " ")); + } + + /** + * Test getAsObject method. + */ + @Test + public void testGetAsObject4() { + LongConverter converter = new LongConverter(); + FacesContext facesContext = Mockito.mock(FacesContext.class); + assertEquals(Long.valueOf("123"), converter.getAsObject(facesContext, new UIPanel(), "123")); + } + + /** + * Test getAsString method. + */ + @Test + public void testGetAsString() { + LongConverter converter = new LongConverter(); + assertThrows(NullPointerException.class, () -> converter.getAsString(null, null, null)); + } + + /** + * Test getAsString method. + */ + @Test + public void testGetAsString2() { + LongConverter converter = new LongConverter(); + FacesContext facesContext = Mockito.mock(FacesContext.class); + assertEquals("", converter.getAsString(facesContext, new UIPanel(), null)); + } + + /** + * Test getAsString method. + */ + @Test + public void testGetAsString3() { + LongConverter converter = new LongConverter(); + FacesContext facesContext = Mockito.mock(FacesContext.class); + assertEquals("123", converter.getAsString(facesContext, new UIPanel(), "123")); + } + + /** + * Test getAsString method. + */ + @Test + public void testGetAsString4() { + LongConverter converter = new LongConverter(); + FacesContext facesContext = Mockito.mock(FacesContext.class); + assertEquals("123", converter.getAsString(facesContext, new UIPanel(), Long.valueOf("123"))); + } +} diff --git a/impl/src/test/java/jakarta/faces/convert/ShortConverterTest.java b/impl/src/test/java/jakarta/faces/convert/ShortConverterTest.java new file mode 100644 index 0000000000..5223c7cb6d --- /dev/null +++ b/impl/src/test/java/jakarta/faces/convert/ShortConverterTest.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces.convert; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import jakarta.faces.component.UIPanel; +import jakarta.faces.context.FacesContext; + +/** + * The JUnit tests for the ShortConverter class. + */ +public class ShortConverterTest { + + /** + * Test getAsObject method. + */ + @Test + public void testGetAsObject() { + ShortConverter converter = new ShortConverter(); + assertThrows(NullPointerException.class, () -> converter.getAsObject(null, null, null)); + } + + /** + * Test getAsObject method. + */ + @Test + public void testGetAsObject2() { + ShortConverter converter = new ShortConverter(); + FacesContext facesContext = Mockito.mock(FacesContext.class); + assertNull(converter.getAsObject(facesContext, new UIPanel(), null)); + } + + /** + * Test getAsObject method. + */ + @Test + public void testGetAsObject3() { + ShortConverter converter = new ShortConverter(); + FacesContext facesContext = Mockito.mock(FacesContext.class); + assertNull(converter.getAsObject(facesContext, new UIPanel(), " ")); + } + + /** + * Test getAsObject method. + */ + @Test + public void testGetAsObject4() { + ShortConverter converter = new ShortConverter(); + FacesContext facesContext = Mockito.mock(FacesContext.class); + assertEquals(Short.valueOf("123"), converter.getAsObject(facesContext, new UIPanel(), "123")); + } + + /** + * Test getAsString method. + */ + @Test + public void testGetAsString() { + ShortConverter converter = new ShortConverter(); + assertThrows(NullPointerException.class, () -> converter.getAsString(null, null, null)); + } + + /** + * Test getAsString method. + */ + @Test + public void testGetAsString2() { + ShortConverter converter = new ShortConverter(); + FacesContext facesContext = Mockito.mock(FacesContext.class); + assertEquals("", converter.getAsString(facesContext, new UIPanel(), null)); + } + + /** + * Test getAsString method. + */ + @Test + public void testGetAsString3() { + ShortConverter converter = new ShortConverter(); + FacesContext facesContext = Mockito.mock(FacesContext.class); + assertEquals("123", converter.getAsString(facesContext, new UIPanel(), "123")); + } + + /** + * Test getAsString method. + */ + @Test + public void testGetAsString4() { + ShortConverter converter = new ShortConverter(); + FacesContext facesContext = Mockito.mock(FacesContext.class); + assertEquals("123", converter.getAsString(facesContext, new UIPanel(), Short.valueOf("123"))); + } +} diff --git a/impl/src/test/java/jakarta/faces/event/PhaseIdTest.java b/impl/src/test/java/jakarta/faces/event/PhaseIdTest.java new file mode 100644 index 0000000000..bbc23c5445 --- /dev/null +++ b/impl/src/test/java/jakarta/faces/event/PhaseIdTest.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces.event; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Iterator; + +import org.junit.jupiter.api.Test; + +public class PhaseIdTest { + + @Test + public void testToString() { + Iterator valueIter = PhaseId.VALUES.iterator(); + String cur = null; + while (valueIter.hasNext()) { + cur = valueIter.next().toString(); + System.out.println(cur); + assertTrue(cur.length() > 3); + } + + } + +} // end of class PhaseIdTest diff --git a/impl/src/test/java/jakarta/faces/model/ArrayDataModelTestCase.java b/impl/src/test/java/jakarta/faces/model/ArrayDataModelTestCase.java new file mode 100644 index 0000000000..2e179c117a --- /dev/null +++ b/impl/src/test/java/jakarta/faces/model/ArrayDataModelTestCase.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces.model; + +import org.junit.jupiter.api.BeforeEach; + +/** + *

+ * Unit tests for {@link ArrayDataModel}.

+ */ +public class ArrayDataModelTestCase extends DataModelTestCaseBase { + + // ------------------------------------------------------ Instance Variables + // ---------------------------------------------------- Overall Test Methods + // Set up instance variables required by this test case. + @BeforeEach + public void setUp() throws Exception { + beans = new BeanTestImpl[5]; + for (int i = 0; i < beans.length; i++) { + beans[i] = new BeanTestImpl(); + } + configure(); + model = new ArrayDataModel(beans); + } + + // ------------------------------------------------- Individual Test Methods + // ------------------------------------------------------- Protected Methods +} diff --git a/impl/src/test/java/jakarta/faces/model/CollectionDataModelTest.java b/impl/src/test/java/jakarta/faces/model/CollectionDataModelTest.java new file mode 100644 index 0000000000..bc33faa3f3 --- /dev/null +++ b/impl/src/test/java/jakarta/faces/model/CollectionDataModelTest.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces.model; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +import java.util.ArrayList; + +import org.junit.jupiter.api.Test; + +/** + * The JUnit tests for CollectionDataModel. + * + * @author Manfred Riem + */ +public class CollectionDataModelTest { + + /** + * Test getWrappedData method. + */ + @Test + public void testGetWrappedData() { + CollectionDataModel model = new CollectionDataModel<>(); + assertNull(model.getWrappedData()); + ArrayList list = new ArrayList<>(); + model.setWrappedData(list); + assertNotNull(model.getWrappedData()); + model.setWrappedData(null); + assertNull(model.getWrappedData()); + } +} diff --git a/impl/src/test/java/jakarta/faces/model/DataModelTestCaseBase.java b/impl/src/test/java/jakarta/faces/model/DataModelTestCaseBase.java new file mode 100644 index 0000000000..c522afbe73 --- /dev/null +++ b/impl/src/test/java/jakarta/faces/model/DataModelTestCaseBase.java @@ -0,0 +1,333 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces.model; + +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.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import java.lang.reflect.Method; +import java.util.Iterator; +import java.util.Map; +import java.util.NoSuchElementException; + +import org.junit.jupiter.api.Test; + +/** + *

+ * Abstract base class for {@link DataModel} tests.

+ */ +public abstract class DataModelTestCaseBase { + + // ------------------------------------------------------ Instance Variables + // The array of beans we will be wrapping (must be initialized before setUp) + protected BeanTestImpl beans[] = new BeanTestImpl[0]; + + // The DataModel we are testing + protected DataModel model = null; + + // ---------------------------------------------------- Overall Test Methods + // Configure the properties of the beans we will be wrapping + protected void configure() { + for (int i = 0; i < beans.length; i++) { + BeanTestImpl bean = beans[i]; + bean.setBooleanProperty((i % 2) == 0); + bean.setBooleanSecond(!bean.getBooleanProperty()); + bean.setByteProperty((byte) i); + bean.setDoubleProperty(i * 100.0); + bean.setFloatProperty(i * ((float) 10.0)); + bean.setIntProperty(1000 * i); + bean.setLongProperty((long) 10000 * (long) i); + bean.setStringProperty("This is string " + i); + } + } + + // ------------------------------------------------- Individual Test Methods + + // Test invalid arguments to listener methods + @Test + public void testInvalidListeners() throws Exception { + try { + model.addDataModelListener(null); + fail("Should have thrown NullPointerException"); + } catch (NullPointerException e) { + // Expected result + } + + try { + model.removeDataModelListener(null); + fail("Should have thrown NullPointerException"); + } catch (NullPointerException e) { + // Expected result + } + } + + // Test positioning to all rows in ascending order + @Test + public void testPositionAscending() throws Exception { + StringBuilder sb = new StringBuilder(); + model.setRowIndex(-1); + model.addDataModelListener(new ListenerTestImpl()); + ListenerTestImpl.trace(null); + + int n = model.getRowCount(); + for (int i = 0; i < n; i++) { + checkRow(i); + sb.append('/').append(i); + } + assertEquals(sb.toString(), ListenerTestImpl.trace()); + } + + // Test positioning to all rows in descending order + @Test + public void testPositionDescending() throws Exception { + StringBuilder sb = new StringBuilder(); + model.setRowIndex(-1); + model.addDataModelListener(new ListenerTestImpl()); + ListenerTestImpl.trace(null); + + int n = model.getRowCount(); + for (int i = (n - 1); i >= 0; i--) { + checkRow(i); + sb.append('/').append(i); + } + assertEquals(sb.toString(), ListenerTestImpl.trace()); + } + + // Test a pristine DataModel instance + @Test + public void testPristine() throws Exception { + // Unopened instance + assertNotNull(beans); + assertNotNull(model); + + // Correct row count + if (model instanceof ResultSetDataModel) { + assertEquals(-1, model.getRowCount()); + } else { + assertEquals(beans.length, + model.getRowCount()); + } + + // Correct row index + assertEquals(0, model.getRowIndex()); + } + + // Test removing listener + @Test + public void testRemoveListener() throws Exception { + ListenerTestImpl listener = new ListenerTestImpl(); + ListenerTestImpl.trace(null); + model.addDataModelListener(listener); + model.setRowIndex(-1); + model.setRowIndex(0); + model.setRowIndex(0); // No movement so no event + model.setRowIndex(-1); + model.removeDataModelListener(listener); + model.setRowIndex(0); + assertEquals("/-1/0/-1", ListenerTestImpl.trace()); + } + + // Test resetting the wrapped data (should trigger an event + @Test + public void testReset() throws Exception { + ListenerTestImpl listener = new ListenerTestImpl(); + ListenerTestImpl.trace(null); + model.addDataModelListener(listener); + + assertEquals(0, model.getRowIndex()); + model.setWrappedData(model.getWrappedData()); + assertEquals("/0", ListenerTestImpl.trace()); + } + + // Test row available manipulations + @Test + public void testRowAvailable() throws Exception { + // Position to the "no current row" position + model.setRowIndex(-1); + assertTrue(!model.isRowAvailable()); + + // Position to an arbitrarily high row number + model.setRowIndex(beans.length); + assertTrue(!model.isRowAvailable()); + + // Position to a known good row number + model.setRowIndex(0); + assertTrue(model.isRowAvailable()); + } + + // Test the ability to update through the Map returned by getRowData() + @Test + @SuppressWarnings("unchecked") + public void testRowData() throws Exception { + // Retrieve the row data for row zero + model.setRowIndex(0); + Object data = model.getRowData(); + assertNotNull(data); + + // Modify several property values + BeanTestImpl bean = beans[0]; + bean.setBooleanProperty(!bean.getBooleanProperty()); + if (data instanceof Map) { + ((Map) data).put("booleanProperty", + bean.getBooleanProperty() + ? Boolean.TRUE : Boolean.FALSE); + } else { + Method m = data.getClass().getMethod("setBooleanProperty", Boolean.TYPE); + m.invoke(data, bean.getBooleanProperty() ? Boolean.TRUE : Boolean.FALSE); + } + bean.setIntProperty(bean.getIntProperty() + 5); + if (data instanceof Map) { + ((Map) data).put("intProperty", + bean.getIntProperty()); + } else { + Method m = data.getClass().getMethod("setIntProperty", Integer.TYPE); + m.invoke(data, bean.getIntProperty()); + } + bean.setStringProperty(bean.getStringProperty() + "XYZ"); + if (data instanceof Map) { + ((Map) data).put("stringProperty", + bean.getStringProperty() + "XYZ"); + } else { + Method m = data.getClass().getMethod("setStringProperty", String.class); + m.invoke(data, bean.getStringProperty()); + } + + // Ensure that all the modifications flowed through to beans[0] + assertEquals(bean.getBooleanProperty(), + beans[0].getBooleanProperty()); + assertEquals(bean.isBooleanSecond(), + beans[0].isBooleanSecond()); + assertEquals(bean.getByteProperty(), + beans[0].getByteProperty()); + assertEquals(bean.getDoubleProperty(), + beans[0].getDoubleProperty(), 0.005); + assertEquals(bean.getFloatProperty(), + beans[0].getFloatProperty(), (float) 0.005); + assertEquals(bean.getIntProperty(), + beans[0].getIntProperty()); + assertEquals(bean.getLongProperty(), + beans[0].getLongProperty()); + assertEquals(bean.getStringProperty(), + beans[0].getStringProperty()); + } + + // Test row index manipulations + @Test + public void testRowIndex() throws Exception { + assertEquals(0, model.getRowIndex()); + + // Positive setRowIndex() tests + model.setRowIndex(0); + model.setRowIndex(-1); + + // Negative setRowIndex() tests + try { + model.setRowIndex(-2); + fail("Should have thrown IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // Expected result + } + } + + @Test + public void testIterator() { + Iterator iterator = model.iterator(); + if (!(model instanceof ScalarDataModel)) { + for (int i = 0; i < 5; i++) { + System.out.println("Index: " + i); + assertTrue(iterator.hasNext()); + assertNotNull(iterator.next()); + } + } else { + assertTrue(iterator.hasNext()); + assertNotNull(iterator.next()); + } + + assertTrue(!iterator.hasNext()); + try { + iterator.next(); + assertTrue(false); + } catch (NoSuchElementException nsee) { + // expected + } + } + + // Test resetting the wrapped data to null + @Test + public void testWrapped() throws Exception { + model.setWrappedData(null); + assertTrue(!model.isRowAvailable()); + assertEquals(-1, model.getRowCount()); + assertNull(model.getRowData()); + assertEquals(-1, model.getRowIndex()); + assertNull(model.getWrappedData()); + } + + // ------------------------------------------------------- Protected Methods + protected BeanTestImpl data() throws Exception { + Object data = model.getRowData(); + assertNotNull(data); + assertTrue(data instanceof BeanTestImpl); + return ((BeanTestImpl) data); + } + + protected void checkRow(int i) throws Exception { + model.setRowIndex(i); + String prompt = "Row " + i + " property "; + BeanTestImpl bean = data(); + assertNotNull(bean, "Row " + i + " data"); + assertEquals( + beans[i].getBooleanProperty(), + bean.getBooleanProperty(), prompt + "booleanProperty"); + assertEquals( + beans[i].isBooleanSecond(), + bean.isBooleanSecond(), prompt + "booleanSecond"); + assertEquals( + beans[i].getByteProperty(), + bean.getByteProperty(), prompt + "byteProperty"); + assertEquals( + "" + beans[i].getDoubleProperty(), + "" + bean.getDoubleProperty(), prompt + "doubleProperty"); + assertEquals( + "" + beans[i].getFloatProperty(), + "" + bean.getFloatProperty(), prompt + "floatProperty"); + assertEquals( + beans[i].getIntProperty(), + bean.getIntProperty(), prompt + "intProperty"); + assertEquals( + beans[i].getLongProperty(), + bean.getLongProperty(), prompt + "longProperty"); + assertEquals( + beans[i].getNullProperty(), + bean.getNullProperty(), prompt + "nullProperty"); + assertEquals( + beans[i].getReadOnlyProperty(), + bean.getReadOnlyProperty(), prompt + "readOnlyProperty"); + assertEquals( + beans[i].getShortProperty(), + bean.getShortProperty(), prompt + "shortProperty"); + assertEquals( + beans[i].getStringProperty(), + bean.getStringProperty(), prompt + "stringProperty"); + assertEquals( + beans[i].getWriteOnlyPropertyValue(), + bean.getWriteOnlyPropertyValue(), prompt + "writeOnlyProperty"); + } +} diff --git a/impl/src/test/java/jakarta/faces/model/ListDataModelTestCase.java b/impl/src/test/java/jakarta/faces/model/ListDataModelTestCase.java new file mode 100644 index 0000000000..552542f90e --- /dev/null +++ b/impl/src/test/java/jakarta/faces/model/ListDataModelTestCase.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces.model; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; + +/** + *

+ * Unit tests for {@link ListDataModel}.

+ */ +public class ListDataModelTestCase extends DataModelTestCaseBase { + + // ------------------------------------------------------ Instance Variables + // ---------------------------------------------------- Overall Test Methods + // Set up instance variables required by this test case. + @BeforeEach + public void setUp() throws Exception { + List list = new ArrayList(); + for (int i = 0; i < 5; i++) { + list.add(new BeanTestImpl()); + } + beans = list.toArray(new BeanTestImpl[5]); + configure(); + model = new ListDataModel(list); + } + + // ------------------------------------------------- Individual Test Methods + // ------------------------------------------------------- Protected Methods +} diff --git a/impl/src/test/java/jakarta/faces/model/ResultSetDataModelTestCase.java b/impl/src/test/java/jakarta/faces/model/ResultSetDataModelTestCase.java new file mode 100644 index 0000000000..635edacba9 --- /dev/null +++ b/impl/src/test/java/jakarta/faces/model/ResultSetDataModelTestCase.java @@ -0,0 +1,548 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces.model; + +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.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.sun.faces.mock.MockResultSet; + +/** + *

+ * Unit tests for {@link ArrayDataModel}.

+ */ +public class ResultSetDataModelTestCase extends DataModelTestCaseBase { + + // ------------------------------------------------------ Instance Variables + // The ResultSet passed to our ResultSetDataModel + protected MockResultSet result = null; + + // ---------------------------------------------------- Overall Test Methods + // Set up instance variables required by this test case. + @BeforeEach + public void setUp() throws Exception { + beans = new BeanTestImpl[5]; + for (int i = 0; i < beans.length; i++) { + beans[i] = new BeanTestImpl(); + } + configure(); + result = new MockResultSet(beans); + model = new ResultSetDataModel(result); + } + + // ------------------------------------------------- Individual Test Methods + // Test ((Map) getRowData()).containsKey() + @Test + public void testRowDataContainsKey() throws Exception { + // Position to row 1 and retrieve the corresponding Map + model.setRowIndex(1); + assertTrue(model.isRowAvailable()); + Object data = model.getRowData(); + assertNotNull(data); + assertTrue(data instanceof Map); + Map map = (Map) data; + + // Test exact match on column names + assertTrue(map.containsKey("booleanProperty")); + assertTrue(map.containsKey("booleanSecond")); + assertTrue(map.containsKey("byteProperty")); + assertTrue(map.containsKey("doubleProperty")); + assertTrue(map.containsKey("floatProperty")); + assertTrue(map.containsKey("intProperty")); + assertTrue(map.containsKey("longProperty")); + assertTrue(map.containsKey("stringProperty")); + + // Test inexact match on column names + assertTrue(map.containsKey("booleanPROPERTY")); + assertTrue(map.containsKey("booleanSECOND")); + assertTrue(map.containsKey("bytePROPERTY")); + assertTrue(map.containsKey("doublePROPERTY")); + assertTrue(map.containsKey("floatPROPERTY")); + assertTrue(map.containsKey("intPROPERTY")); + assertTrue(map.containsKey("longPROPERTY")); + assertTrue(map.containsKey("stringPROPERTY")); + + // Test false return on invalid column names + assertTrue(!map.containsKey("foo")); + assertTrue(!map.containsKey("FOO")); + assertTrue(!map.containsKey("bar")); + assertTrue(!map.containsKey("BAR")); + } + + // Test ((Map) getRowData()).containsValue() + @Test + public void testRowDataContainsValue() throws Exception { + // Position to row 1 and retrieve the corresponding Map + model.setRowIndex(1); + assertTrue(model.isRowAvailable()); + Object data = model.getRowData(); + assertNotNull(data); + assertTrue(data instanceof Map); + Map map = (Map) data; + + // Test positive results + assertTrue(map.containsValue(Boolean.TRUE)); + assertTrue(map.containsValue(Boolean.FALSE)); + assertTrue(map.containsValue(Byte.valueOf((byte) 1))); + assertTrue(map.containsValue(Double.valueOf(100.0))); + assertTrue(map.containsValue(Float.valueOf((float) 10.0))); + assertTrue(map.containsValue(Integer.valueOf(1000))); + assertTrue(map.containsValue(Long.valueOf(10000l))); + assertTrue(map.containsValue("This is string 1")); + + // Test negative results + assertTrue(!map.containsValue("foo")); + assertTrue(!map.containsValue(Integer.valueOf(654321))); + } + + // Test ((Map) getRowData()).entrySet() + @Test + public void testRowDataEntrySet() throws Exception { + // Position to row 1 and retrieve the corresponding Map + model.setRowIndex(1); + assertTrue(model.isRowAvailable()); + Object data = model.getRowData(); + assertNotNull(data); + assertTrue(data instanceof Map); + Map map = (Map) data; + Set set = map.entrySet(); + + // Test exact match postive results + assertTrue(set.contains(new TestEntry("booleanProperty", Boolean.FALSE))); + assertTrue(set.contains(new TestEntry("booleanSecond", Boolean.TRUE))); + assertTrue(set.contains(new TestEntry("byteProperty", Byte.valueOf((byte) 1)))); + assertTrue(set.contains(new TestEntry("doubleProperty", Double.valueOf(100.0)))); + assertTrue(set.contains(new TestEntry("floatProperty", Float.valueOf((float) 10.0)))); + assertTrue(set.contains(new TestEntry("intProperty", Integer.valueOf(1000)))); + assertTrue(set.contains(new TestEntry("longProperty", Long.valueOf(10000l)))); + assertTrue(set.contains(new TestEntry("stringProperty", "This is string 1"))); + + // Test exact match postive results + assertTrue(set.contains(new TestEntry("booleanPROPERTY", Boolean.FALSE))); + assertTrue(set.contains(new TestEntry("booleanSECOND", Boolean.TRUE))); + assertTrue(set.contains(new TestEntry("bytePROPERTY", Byte.valueOf((byte) 1)))); + assertTrue(set.contains(new TestEntry("doublePROPERTY", Double.valueOf(100.0)))); + assertTrue(set.contains(new TestEntry("floatPROPERTY", Float.valueOf((float) 10.0)))); + assertTrue(set.contains(new TestEntry("intPROPERTY", Integer.valueOf(1000)))); + assertTrue(set.contains(new TestEntry("longPROPERTY", Long.valueOf(10000l)))); + assertTrue(set.contains(new TestEntry("stringPROPERTY", "This is string 1"))); + + // Test negative results + assertTrue(!set.contains(new TestEntry("foo", "bar"))); + assertTrue(!set.contains(new TestEntry("FOO", "bar"))); + assertTrue(!set.contains(new TestEntry("baz", "bop"))); + assertTrue(!set.contains(new TestEntry("BAZ", "bop"))); + + // Test other methods + assertTrue(!set.isEmpty()); + + // Test updating through the entry set + Iterator entries = set.iterator(); + while (entries.hasNext()) { + Map.Entry entry = (Map.Entry) entries.next(); + if ("stringProperty".equalsIgnoreCase((String) entry.getKey())) { + entry.setValue("This is string 1 modified"); + } + } + assertEquals("This is string 1 modified", + beans[1].getStringProperty()); + assertEquals("This is string 1 modified", + map.get("stringProperty")); + assertEquals("This is string 1 modified", + map.get("stringPROPERTY")); + result.absolute(2); // ResultSet indexing is one-relative + assertEquals("This is string 1 modified", + result.getObject("stringProperty")); + } + + // Test ((Map) getRowData()).get() + @Test + public void testRowDataGet() throws Exception { + // Position to row 1 and retrieve the corresponding Map + model.setRowIndex(1); + assertTrue(model.isRowAvailable()); + Object data = model.getRowData(); + assertNotNull(data); + assertTrue(data instanceof Map); + Map map = (Map) data; + + // Test exact match on column names + assertEquals(Boolean.FALSE, map.get("booleanProperty")); + assertEquals(Boolean.TRUE, map.get("booleanSecond")); + assertEquals(Byte.valueOf((byte) 1), map.get("byteProperty")); + assertEquals(Double.valueOf(100.0), map.get("doubleProperty")); + assertEquals(Float.valueOf((float) 10.0), map.get("floatProperty")); + assertEquals(Integer.valueOf(1000), map.get("intProperty")); + assertEquals(Long.valueOf(10000l), map.get("longProperty")); + assertEquals("This is string 1", map.get("stringProperty")); + + // Test inexact match on column names + assertEquals(Boolean.FALSE, map.get("booleanPROPERTY")); + assertEquals(Boolean.TRUE, map.get("booleanSECOND")); + assertEquals(Byte.valueOf((byte) 1), map.get("bytePROPERTY")); + assertEquals(Double.valueOf(100.0), map.get("doublePROPERTY")); + assertEquals(Float.valueOf((float) 10.0), map.get("floatPROPERTY")); + assertEquals(Integer.valueOf(1000), map.get("intPROPERTY")); + assertEquals(Long.valueOf(10000l), map.get("longPROPERTY")); + assertEquals("This is string 1", map.get("stringPROPERTY")); + + // Test null return on non-existent column names + assertNull(map.get("foo")); + assertNull(map.get("FOO")); + assertNull(map.get("bar")); + assertNull(map.get("bar")); + } + + // Test ((Map) getRowData()).keySet() + @Test + public void testRowDataKeySet() throws Exception { + // Position to row 1 and retrieve the corresponding Map + model.setRowIndex(1); + assertTrue(model.isRowAvailable()); + Object data = model.getRowData(); + assertNotNull(data); + assertTrue(data instanceof Map); + Map map = (Map) data; + Set set = map.keySet(); + + // Test exact match postive results + assertTrue(set.contains("booleanProperty")); + assertTrue(set.contains("booleanSecond")); + assertTrue(set.contains("byteProperty")); + assertTrue(set.contains("doubleProperty")); + assertTrue(set.contains("floatProperty")); + assertTrue(set.contains("intProperty")); + assertTrue(set.contains("longProperty")); + assertTrue(set.contains("stringProperty")); + + // Test inexact match positive results + assertTrue(set.contains("booleanPROPERTY")); + assertTrue(set.contains("booleanSECOND")); + assertTrue(set.contains("bytePROPERTY")); + assertTrue(set.contains("doublePROPERTY")); + assertTrue(set.contains("floatPROPERTY")); + assertTrue(set.contains("intPROPERTY")); + assertTrue(set.contains("longPROPERTY")); + assertTrue(set.contains("stringPROPERTY")); + + // Test negative results + assertTrue(!set.contains("foo")); + assertTrue(!set.contains("FOO")); + assertTrue(!set.contains("bar")); + assertTrue(!set.contains("BAR")); + + // Test other methods + assertTrue(!set.isEmpty()); + } + + // Test ((Map) getRowData()).put() + @Test + public void testRowDataPut() throws Exception { + // Position to row 1 and retrieve the corresponding Map + model.setRowIndex(1); + assertTrue(model.isRowAvailable()); + Object data = model.getRowData(); + assertNotNull(data); + assertTrue(data instanceof Map); + Map map = (Map) data; + } + + // Test unsupported operations on ((Map) getRowData()) + @Test + public void testRowDataUnsupported() throws Exception { + // Position to row 1 and retrieve the corresponding Map + model.setRowIndex(1); + assertTrue(model.isRowAvailable()); + Object data = model.getRowData(); + assertNotNull(data); + assertTrue(data instanceof Map); + Map map = (Map) data; + + // clear() + try { + map.clear(); + fail("Should have thrown UnsupportedOperationException"); + } catch (UnsupportedOperationException e) { + // Expected result + } + + // entrySet() + Set entrySet = map.entrySet(); + try { + entrySet.add(new TestEntry("foo", "bar")); + fail("Should have thrown UnsupportedOperationException"); + } catch (UnsupportedOperationException e) { + // Expected result + } + List mapEntries = new ArrayList(); + mapEntries.add(new TestEntry("foo", "bar")); + mapEntries.add(new TestEntry("baz", "bop")); + try { + entrySet.addAll(mapEntries); + fail("Should have thrown UnsupportedOperationException"); + } catch (UnsupportedOperationException e) { + // Expected result + } + try { + entrySet.clear(); + fail("Should have thrown UnsupportedOperationException"); + } catch (UnsupportedOperationException e) { + // Expected result + } + try { + Iterator iterator = entrySet.iterator(); + iterator.next(); + iterator.remove(); + fail("Should have thrown UnsupportedOperationException"); + } catch (UnsupportedOperationException e) { + // Expected result + } + try { + entrySet.remove(new TestEntry("foo", "bar")); + fail("Should have thrown UnsupportedOperationException"); + } catch (UnsupportedOperationException e) { + // Expected result + } + try { + entrySet.removeAll(mapEntries); + fail("Should have thrown UnsupportedOperationException"); + } catch (UnsupportedOperationException e) { + // Expected result + } + try { + entrySet.retainAll(mapEntries); + fail("Should have thrown UnsupportedOperationException"); + } catch (UnsupportedOperationException e) { + // Expected result + } + + // keySet() + Set keySet = map.keySet(); + try { + keySet.add("foo"); + fail("Should have thrown UnsupportedOperationException"); + } catch (UnsupportedOperationException e) { + // Expected result + } + List mapKeys = new ArrayList(); + mapKeys.add("foo"); + mapKeys.add("bar"); + try { + keySet.addAll(mapKeys); + fail("Should have thrown UnsupportedOperationException"); + } catch (UnsupportedOperationException e) { + // Expected result + } + try { + keySet.clear(); + fail("Should have thrown UnsupportedOperationException"); + } catch (UnsupportedOperationException e) { + // Expected result + } + try { + Iterator iterator = keySet.iterator(); + iterator.next(); + iterator.remove(); + fail("Should have thrown UnsupportedOperationException"); + } catch (UnsupportedOperationException e) { + // Expected result + } + try { + keySet.remove(new TestEntry("foo", "bar")); + fail("Should have thrown UnsupportedOperationException"); + } catch (UnsupportedOperationException e) { + // Expected result + } + try { + keySet.removeAll(mapKeys); + fail("Should have thrown UnsupportedOperationException"); + } catch (UnsupportedOperationException e) { + // Expected result + } + try { + keySet.retainAll(mapKeys); + fail("Should have thrown UnsupportedOperationException"); + } catch (UnsupportedOperationException e) { + // Expected result + } + + // remove() + try { + map.remove("foo"); + fail("Should have thrown UnsupportedOperationException"); + } catch (UnsupportedOperationException e) { + // Expected result + } + + // values() + Collection values = map.values(); + try { + values.add("foo"); + fail("Should have thrown UnsupportedOperationException"); + } catch (UnsupportedOperationException e) { + // Expected result + } + List list = new ArrayList(); + list.add("foo"); + list.add("bar"); + try { + values.addAll(list); + fail("Should have thrown UnsupportedOperationException"); + } catch (UnsupportedOperationException e) { + // Expected result + } + try { + values.clear(); + fail("Should have thrown UnsupportedOperationException"); + } catch (UnsupportedOperationException e) { + // Expected result + } + try { + Iterator iterator = values.iterator(); + iterator.next(); + iterator.remove(); + fail("Should have thrown UnsupportedOperationException"); + } catch (UnsupportedOperationException e) { + // Expected result + } + try { + values.remove("foo"); + fail("Should have thrown UnsupportedOperationException"); + } catch (UnsupportedOperationException e) { + // Expected result + } + try { + values.removeAll(list); + fail("Should have thrown UnsupportedOperationException"); + } catch (UnsupportedOperationException e) { + // Expected result + } + try { + values.retainAll(list); + fail("Should have thrown UnsupportedOperationException"); + } catch (UnsupportedOperationException e) { + // Expected result + } + + } + + // Test ((Map) getRowData()).values() + @Test + public void testRowDataValues() throws Exception { + // Position to row 1 and retrieve the corresponding Map + model.setRowIndex(1); + assertTrue(model.isRowAvailable()); + Object data = model.getRowData(); + assertNotNull(data); + assertTrue(data instanceof Map); + Map map = (Map) data; + Collection values = map.values(); + + // Test positive results + assertTrue(values.contains(Boolean.TRUE)); + assertTrue(values.contains(Boolean.FALSE)); + assertTrue(values.contains(Byte.valueOf((byte) 1))); + assertTrue(values.contains(Double.valueOf(100.0))); + assertTrue(values.contains(Float.valueOf((float) 10.0))); + assertTrue(values.contains(Integer.valueOf(1000))); + assertTrue(values.contains(Long.valueOf(10000l))); + assertTrue(values.contains("This is string 1")); + + // Test negative results + assertTrue(!values.contains("foo")); + assertTrue(!values.contains(Integer.valueOf(654321))); + + // Test other methods + assertTrue(!values.isEmpty()); + } + + // ------------------------------------------------------- Protected Methods + @Override + protected BeanTestImpl data() throws Exception { + Object data = model.getRowData(); + assertTrue(data instanceof Map); + BeanTestImpl bean = new BeanTestImpl(); + Map map = (Map) data; + + bean.setBooleanProperty(((Boolean) map.get("booleanProperty")).booleanValue()); + bean.setBooleanSecond(((Boolean) map.get("booleanSecond")).booleanValue()); + bean.setByteProperty(((Byte) map.get("byteProperty")).byteValue()); + bean.setDoubleProperty(((Double) map.get("doubleProperty")).doubleValue()); + bean.setFloatProperty(((Float) map.get("floatProperty")).floatValue()); + bean.setIntProperty(((Integer) map.get("intProperty")).intValue()); + bean.setLongProperty(((Long) map.get("longProperty")).longValue()); + bean.setNullProperty((String) map.get("nullProperty")); + bean.setShortProperty(((Short) map.get("shortProperty")).shortValue()); + bean.setStringProperty((String) map.get("stringProperty")); + bean.setWriteOnlyProperty((String) map.get("writeOnlyPropertyValue")); + + return (bean); + } + + class TestEntry implements Map.Entry { + + public TestEntry(Object key, Object value) { + this.key = key; + this.value = value; + } + + private Object key; + private Object value; + + @Override + public Object getKey() { + return key; + } + + @Override + public Object getValue() { + return value; + } + + @Override + public Object setValue(Object value) { + Object previous = this.value; + this.value = value; + return previous; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof Map.Entry)) { + return (false); + } + Map.Entry e = (Map.Entry) o; + return (key == null + ? e.getKey() == null : key.equals(e.getKey())) + && (value == null + ? e.getValue() == null : value.equals(e.getValue())); + } + } +} diff --git a/impl/src/test/java/jakarta/faces/model/ScalarDataModelTestCase.java b/impl/src/test/java/jakarta/faces/model/ScalarDataModelTestCase.java new file mode 100644 index 0000000000..96d6cba224 --- /dev/null +++ b/impl/src/test/java/jakarta/faces/model/ScalarDataModelTestCase.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces.model; + +import org.junit.jupiter.api.BeforeEach; + +/** + *

+ * Unit tests for {@link ScalarDataModel}.

+ */ +public class ScalarDataModelTestCase extends DataModelTestCaseBase { + + // ------------------------------------------------------ Instance Variables + // ---------------------------------------------------- Overall Test Methods + // Set up instance variables required by this test case. + @BeforeEach + public void setUp() throws Exception { + beans = new BeanTestImpl[1]; + beans[0] = new BeanTestImpl(); + configure(); + model = new ScalarDataModel(beans[0]); + } + + // ------------------------------------------------- Individual Test Methods + // ------------------------------------------------------- Protected Methods +} diff --git a/impl/src/test/java/jakarta/faces/validator/BeanValidatorTestCase.java b/impl/src/test/java/jakarta/faces/validator/BeanValidatorTestCase.java new file mode 100644 index 0000000000..f711dc736a --- /dev/null +++ b/impl/src/test/java/jakarta/faces/validator/BeanValidatorTestCase.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2022, 2022 Contributors to Eclipse Foundation. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces.validator; + +import java.util.Locale; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import jakarta.faces.component.UIInput; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; + +/** + *

Unit tests for {@link BeanValidator}.

+ * + * @author rmartinc + */ +public class BeanValidatorTestCase extends ValidatorTestCase { + + /** + * Test class for the bean validator. + */ + public static class TestBean { + + @NotNull + @Size(min = 1, max = 64) + private String message; + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + } + + // ------------------------------------------------- Individual Test Methods + @Test + public void testMessageOK() { + BeanValidator validator = new BeanValidator(); + Locale.setDefault(Locale.US); + facesContext.getViewRoot().setLocale(Locale.US); + UIInput component = new UIInput(); + request.setAttribute("test", new TestBean()); + component.setValueExpression("value", application.getExpressionFactory().createValueExpression(facesContext.getELContext(), "#{test.message}", String.class)); + + validator.validate(facesContext, component, "something"); + } + + @Test + public void testMessageKO() { + BeanValidator validator = new BeanValidator(); + Locale.setDefault(Locale.US); + facesContext.getViewRoot().setLocale(Locale.US); + UIInput component = new UIInput(); + request.setAttribute("test", new TestBean()); + component.setValueExpression("value", application.getExpressionFactory().createValueExpression(facesContext.getELContext(), "#{test.message}", String.class)); + + try { + validator.validate(facesContext, component, ""); + Assertions.fail("ValidatorException expected"); + } catch (ValidatorException e) { + Assertions.assertEquals("size must be between 1 and 64", e.getMessage()); + } + } + + @Test + public void testNoBase() { + BeanValidator validator = new BeanValidator(); + Locale.setDefault(Locale.US); + facesContext.getViewRoot().setLocale(Locale.US); + UIInput component = new UIInput(); + component.setValueExpression("value", application.getExpressionFactory().createValueExpression(facesContext.getELContext(), "#{something}", String.class)); + + validator.validate(facesContext, component, "something"); + } +} diff --git a/impl/src/test/java/jakarta/faces/validator/CastingValidatorTestCase.java b/impl/src/test/java/jakarta/faces/validator/CastingValidatorTestCase.java new file mode 100644 index 0000000000..fa9f13042c --- /dev/null +++ b/impl/src/test/java/jakarta/faces/validator/CastingValidatorTestCase.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces.validator; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import org.junit.jupiter.api.Test; + +/** + *

+ * Unit tests for JLS casting rules

+ */ +public class CastingValidatorTestCase extends ValidatorTestCase { + + // ------------------------------------------------- Individual Test Methods + @Test + @SuppressWarnings("rawtypes") + public void testWithGenericCanCastToRaw() { + Validator validatorWithGeneric = (context, component, value) -> {}; + Validator validatorRaw = validatorWithGeneric; + assertNotNull(validatorRaw); + } + +} diff --git a/impl/src/test/java/jakarta/faces/validator/DoubleRangeValidatorTestCase.java b/impl/src/test/java/jakarta/faces/validator/DoubleRangeValidatorTestCase.java new file mode 100644 index 0000000000..5daa37ed77 --- /dev/null +++ b/impl/src/test/java/jakarta/faces/validator/DoubleRangeValidatorTestCase.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces.validator; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import java.util.Locale; + +import org.junit.jupiter.api.Test; + +import jakarta.faces.component.UIInput; + +/** + *

+ * Unit tests for {@link DoubleRangeValidator}.

+ */ +public class DoubleRangeValidatorTestCase extends ValidatorTestCase { + + // ------------------------------------------------- Individual Test Methods + @Test + public void testLocaleHonored() { + DoubleRangeValidator validator = new DoubleRangeValidator(); + validator.setMinimum(10.1); + validator.setMaximum(20.1); + boolean exceptionThrown = false; + UIInput component = new UIInput(); + String message; + Locale.setDefault(Locale.US); + facesContext.getViewRoot().setLocale(Locale.US); + + try { + validator.validate(facesContext, component, "5.1"); + fail("Exception not thrown"); + } catch (ValidatorException e) { + exceptionThrown = true; + message = e.getMessage(); + assertTrue( + -1 != message.indexOf("10.1"), "message: \"" + message + "\" missing localized chars."); + assertTrue( + -1 != message.indexOf("20.1"), "message: \"" + message + "\" missing localized chars."); + } + assertTrue(exceptionThrown); + + exceptionThrown = false; + Locale.setDefault(Locale.GERMAN); + facesContext.getViewRoot().setLocale(Locale.GERMAN); + + try { + validator.validate(facesContext, component, "5"); + fail("Exception not thrown"); + } catch (ValidatorException e) { + exceptionThrown = true; + message = e.getMessage(); + assertTrue( + -1 != message.indexOf("10,1"), "message: \"" + message + "\" missing localized chars."); + assertTrue( + -1 != message.indexOf("20,1"), "message: \"" + message + "\" missing localized chars."); + } + assertTrue(exceptionThrown); + } + + @Test + public void testHashCode() { + DoubleRangeValidator validator1 = new DoubleRangeValidator(); + DoubleRangeValidator validator2 = new DoubleRangeValidator(); + + validator1.setMinimum(10.0d); + validator1.setMaximum(15.1d); + validator2.setMinimum(10.0d); + validator2.setMaximum(15.1d); + + assertTrue(validator1.hashCode() == validator2.hashCode()); + assertTrue(validator1.hashCode() == validator2.hashCode()); + + validator2.setMaximum(15.2d); + + assertTrue(validator1.hashCode() != validator2.hashCode()); + + validator1 = new DoubleRangeValidator(); + validator2 = new DoubleRangeValidator(); + + validator1.setMinimum(10.0d); + validator2.setMinimum(10.0d); + + assertTrue(validator1.hashCode() == validator2.hashCode()); + assertTrue(validator1.hashCode() == validator2.hashCode()); + + validator1.setMinimum(11.0d); + + assertTrue(validator1.hashCode() != validator2.hashCode()); + + validator1.setMinimum(10.0d); + validator1.setMaximum(10.1d); + + assertTrue(validator1.hashCode() != validator2.hashCode()); + } +} diff --git a/impl/src/test/java/jakarta/faces/validator/LengthValidatorTestCase.java b/impl/src/test/java/jakarta/faces/validator/LengthValidatorTestCase.java new file mode 100644 index 0000000000..97e4414b3f --- /dev/null +++ b/impl/src/test/java/jakarta/faces/validator/LengthValidatorTestCase.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces.validator; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import java.util.Locale; + +import org.junit.jupiter.api.Test; + +import jakarta.faces.component.UIInput; + +/** + *

+ * Unit tests for {@link LengthValidator}.

+ */ +public class LengthValidatorTestCase extends ValidatorTestCase { + + // ------------------------------------------------- Individual Test Methods + @Test + public void testLocaleHonored() { + LengthValidator validator = new LengthValidator(); + validator.setMinimum(1000); + validator.setMaximum(2000); + boolean exceptionThrown = false; + UIInput component = new UIInput(); + String message; + Locale.setDefault(Locale.US); + facesContext.getViewRoot().setLocale(Locale.US); + + try { + validator.validate(facesContext, component, + "Not at all long enough"); + fail("Exception not thrown"); + } catch (ValidatorException e) { + exceptionThrown = true; + message = e.getMessage(); + assertTrue( + -1 != message.indexOf("1,000"), "message: \"" + message + "\" missing localized chars."); + } + assertTrue(exceptionThrown); + + exceptionThrown = false; + Locale.setDefault(Locale.GERMAN); + facesContext.getViewRoot().setLocale(Locale.GERMAN); + + try { + validator.validate(facesContext, component, + "Still not long enough"); + fail("Exception not thrown"); + } catch (ValidatorException e) { + exceptionThrown = true; + message = e.getMessage(); + assertTrue( + -1 != message.indexOf("1.000"), "message: \"" + message + "\" missing localized chars."); + } + assertTrue(exceptionThrown); + } + + @Test + public void testHashCode() { + LengthValidator validator1 = new LengthValidator(); + LengthValidator validator2 = new LengthValidator(); + + validator1.setMinimum(10); + validator1.setMaximum(15); + validator2.setMinimum(10); + validator2.setMaximum(15); + + assertTrue(validator1.hashCode() == validator2.hashCode()); + assertTrue(validator1.hashCode() == validator2.hashCode()); + + validator2.setMaximum(16); + + assertTrue(validator1.hashCode() != validator2.hashCode()); + + validator1 = new LengthValidator(); + validator2 = new LengthValidator(); + + validator1.setMinimum(10); + validator2.setMinimum(10); + + assertTrue(validator1.hashCode() == validator2.hashCode()); + assertTrue(validator1.hashCode() == validator2.hashCode()); + + validator1.setMinimum(11); + + assertTrue(validator1.hashCode() != validator2.hashCode()); + + validator1.setMinimum(10); + validator1.setMaximum(10); + + assertTrue(validator1.hashCode() != validator2.hashCode()); + } +} diff --git a/impl/src/test/java/jakarta/faces/validator/LongRangeValidatorTestCase.java b/impl/src/test/java/jakarta/faces/validator/LongRangeValidatorTestCase.java new file mode 100644 index 0000000000..c59b18fdcc --- /dev/null +++ b/impl/src/test/java/jakarta/faces/validator/LongRangeValidatorTestCase.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces.validator; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import java.util.Locale; + +import org.junit.jupiter.api.Test; + +import jakarta.faces.component.UIInput; + +/** + *

+ * Unit tests for {@link LongRangeValidator}.

+ */ +public class LongRangeValidatorTestCase extends ValidatorTestCase { + + // ------------------------------------------------- Individual Test Methods + @Test + public void testLocaleHonored() { + LongRangeValidator validator = new LongRangeValidator(); + validator.setMinimum(10100); + validator.setMaximum(20100); + boolean exceptionThrown = false; + UIInput component = new UIInput(); + String message; + Locale.setDefault(Locale.US); + facesContext.getViewRoot().setLocale(Locale.US); + + try { + validator.validate(facesContext, component, "5100"); + fail("Exception not thrown"); + } catch (ValidatorException e) { + exceptionThrown = true; + message = e.getMessage(); + assertTrue( + -1 != message.indexOf("10,100"), "message: \"" + message + "\" missing localized chars."); + assertTrue( + -1 != message.indexOf("20,100"), "message: \"" + message + "\" missing localized chars."); + } + assertTrue(exceptionThrown); + + exceptionThrown = false; + Locale.setDefault(Locale.GERMAN); + facesContext.getViewRoot().setLocale(Locale.GERMAN); + + try { + validator.validate(facesContext, component, "5100"); + fail("Exception not thrown"); + } catch (ValidatorException e) { + exceptionThrown = true; + message = e.getMessage(); + assertTrue( + -1 != message.indexOf("10.100"), "message: \"" + message + "\" missing localized chars."); + assertTrue( + -1 != message.indexOf("20.100"), "message: \"" + message + "\" missing localized chars."); + } + assertTrue(exceptionThrown); + } + + @Test + public void testHashCode() { + LongRangeValidator validator1 = new LongRangeValidator(); + LongRangeValidator validator2 = new LongRangeValidator(); + + validator1.setMinimum(10l); + validator1.setMaximum(15l); + validator2.setMinimum(10l); + validator2.setMaximum(15l); + + assertTrue(validator1.hashCode() == validator2.hashCode()); + assertTrue(validator1.hashCode() == validator2.hashCode()); + + validator2.setMaximum(16l); + + assertTrue(validator1.hashCode() != validator2.hashCode()); + + validator1 = new LongRangeValidator(); + validator2 = new LongRangeValidator(); + + validator1.setMinimum(10l); + validator2.setMinimum(10l); + + assertTrue(validator1.hashCode() == validator2.hashCode()); + assertTrue(validator1.hashCode() == validator2.hashCode()); + + validator1.setMinimum(11l); + + assertTrue(validator1.hashCode() != validator2.hashCode()); + + validator1.setMinimum(10l); + validator1.setMaximum(11l); + + assertTrue(validator1.hashCode() != validator2.hashCode()); + } +} diff --git a/impl/src/test/java/jakarta/faces/validator/RegexValidatorTestCase.java b/impl/src/test/java/jakarta/faces/validator/RegexValidatorTestCase.java new file mode 100644 index 0000000000..189ae63241 --- /dev/null +++ b/impl/src/test/java/jakarta/faces/validator/RegexValidatorTestCase.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces.validator; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import java.util.Locale; + +import org.junit.jupiter.api.Test; + +import jakarta.faces.component.UIInput; + +/** + *

+ * Unit tests for {@link RegexValidator}.

+ */ +public class RegexValidatorTestCase extends ValidatorTestCase { + + // ------------------------------------------------- Individual Test Methods + @Test + public void testPatternMatch() { + String patternStr = "t.*"; + RegexValidator validator = new RegexValidator(); + validator.setPattern(patternStr); + UIInput component = new UIInput(); + String checkme = "test"; + try { + validator.validate(facesContext, component, checkme); + assertTrue(true); + } catch (ValidatorException ve) { + fail("Exception thrown " + ve.getMessage()); + } + } + + @Test + public void testPatterMismatch() { + String patternStr = "t.*"; + facesContext.getViewRoot().setLocale(Locale.US); + RegexValidator validator = new RegexValidator(); + validator.setPattern(patternStr); + UIInput component = new UIInput(); + String checkme = "jest"; + try { + validator.validate(facesContext, component, checkme); + fail("Exception not thrown when tested " + checkme + " against " + patternStr); + } catch (ValidatorException ve) { + String detail = ve.getFacesMessage().getDetail(); + System.out.println("Detail in test: " + detail); + assertTrue(detail.equalsIgnoreCase("Regex pattern of 't.*' not matched")); + } + } +} diff --git a/impl/src/test/java/jakarta/faces/validator/RequiredValidatorTest.java b/impl/src/test/java/jakarta/faces/validator/RequiredValidatorTest.java new file mode 100644 index 0000000000..4ed683411c --- /dev/null +++ b/impl/src/test/java/jakarta/faces/validator/RequiredValidatorTest.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces.validator; + +import org.junit.jupiter.api.Test; + +/** + * The JUnit tests for the RequiredValidator class. + */ +public class RequiredValidatorTest { + + /** + * Test validate method. + */ + @Test + public void testValidate() { + RequiredValidator validator = new RequiredValidator(); + validator.validate(null, null, "notempty"); + } +} diff --git a/impl/src/test/java/jakarta/faces/validator/ValidatorTestCase.java b/impl/src/test/java/jakarta/faces/validator/ValidatorTestCase.java new file mode 100644 index 0000000000..740b917d76 --- /dev/null +++ b/impl/src/test/java/jakarta/faces/validator/ValidatorTestCase.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces.validator; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.sun.faces.junit.JUnitFacesTestCaseBase; +import com.sun.faces.mock.MockRenderKit; + +import jakarta.faces.FactoryFinder; +import jakarta.faces.component.UIViewRoot; +import jakarta.faces.render.RenderKit; +import jakarta.faces.render.RenderKitFactory; + +/** + *

+ * Base unit tests for all {@link Validator} implementations.

+ */ +public class ValidatorTestCase extends JUnitFacesTestCaseBase { + + // ---------------------------------------------------- Overall Test Methods + // Set up instance variables required by this test case. + @Override + @BeforeEach + public void setUp() throws Exception { + super.setUp(); + UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null); + root.setViewId("/viewId"); + facesContext.setViewRoot(root); + RenderKitFactory renderKitFactory = (RenderKitFactory) FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY); + RenderKit renderKit = new MockRenderKit(); + try { + renderKitFactory.addRenderKit(RenderKitFactory.HTML_BASIC_RENDER_KIT, + renderKit); + } catch (IllegalArgumentException e) { + } + + } + + @Test + public void testNoOp() { + } +} diff --git a/impl/src/test/java/jakarta/faces/webapp/ComponentTestImpl.java b/impl/src/test/java/jakarta/faces/webapp/ComponentTestImpl.java new file mode 100644 index 0000000000..869fb33325 --- /dev/null +++ b/impl/src/test/java/jakarta/faces/webapp/ComponentTestImpl.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces.webapp; + +import java.io.IOException; + +import jakarta.el.ValueExpression; +import jakarta.faces.component.UIComponentBase; +import jakarta.faces.context.FacesContext; +import jakarta.faces.context.ResponseWriter; + +// Test UIComponent Class +public class ComponentTestImpl extends UIComponentBase { + + public ComponentTestImpl() { + } + + public ComponentTestImpl(String id) { + setId(id); + } + + @Override + public String getFamily() { + return ("Test"); + } + + private String label = null; + + public String getLabel() { + if (this.label != null) { + return (this.label); + } + ValueExpression vb = getValueExpression("label"); + if (vb != null) { + return ((String) vb.getValue(getFacesContext().getELContext())); + } else { + return (null); + } + } + + public void setLabel(String label) { + this.label = label; + } + + private boolean rendersChildren = false; + + @Override + public boolean getRendersChildren() { + return (this.rendersChildren); + } + + public void setRendersChildren(boolean rendersChildren) { + this.rendersChildren = rendersChildren; + } + + @Override + public void encodeBegin(FacesContext context) throws IOException { + if (!isRendered()) { + return; + } + ResponseWriter writer = context.getResponseWriter(); + writer.write("/b"); + String id = getId(); + if (id != null) { + writer.write(id); + } + } + + @Override + public void encodeChildren(FacesContext context) throws IOException { + if (isRendered()) { + super.encodeChildren(context); + } + } + + @Override + public void encodeEnd(FacesContext context) throws IOException { + if (!isRendered()) { + return; + } + ResponseWriter writer = context.getResponseWriter(); + writer.write("/e"); + String id = getId(); + if (id != null) { + writer.write(id); + } + } +} diff --git a/impl/src/test/java/jakarta/faces/webapp/ConfigFileTestCase.java b/impl/src/test/java/jakarta/faces/webapp/ConfigFileTestCase.java new file mode 100644 index 0000000000..18b6e4594d --- /dev/null +++ b/impl/src/test/java/jakarta/faces/webapp/ConfigFileTestCase.java @@ -0,0 +1,281 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces.webapp; + +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 java.io.File; +import java.io.InputStream; +import java.net.URL; +import java.util.Map; + +import org.apache.commons.digester.Digester; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.xml.sax.InputSource; + +import com.sun.faces.config.DigesterFactory; + +/** + *

+ * Unit tests for Configuration File processing. + *

+ */ +public class ConfigFileTestCase { + + // The public identifier of our DTD + protected String CONFIG_DTD_PUBLIC_ID = "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.0//EN"; + + // ----------------------------------------------------- Instance Variables + /** + * The Digester instance we will use to parse configuration files. + */ + protected Digester digester = null; + + // --------------------------------------------------- Overall Test Methods + /** + * Set up instance variables required by this test case. + * + * @throws java.lang.Exception + */ + @BeforeEach + public void setUp() throws Exception { + digester = createDigester(); + configureRules(digester); + } + + // ------------------------------------------------ Individual Test Methods + // Test parsing an empty configuration file + @Test + public void testEmpty() throws Exception { + ConfigBase base = parseConfig(relativeURL("src/test/java/jakarta/faces/webapp/config-file-0.xml")); + assertNotNull(base); + } + + // Test parsing a full configuration file + @Test + public void testFull() throws Exception { + // Retrieve entire configuration metadata instance + ConfigBase base = parseConfig(relativeURL("src/test/java/jakarta/faces/webapp/config-file-1.xml")); + assertNotNull(base); + + // + assertEquals("com.mycompany.MyActionListener", base.getActionListener()); + assertEquals("com.mycompany.MyNavigationHandler", base.getNavigationHandler()); + + // + Map components = base.getComponents(); + assertNotNull(components); + ConfigComponent ccomp1 = (ConfigComponent) components.get("Command"); + assertNotNull(ccomp1); + assertEquals("User Interface Command Component", ccomp1.getDescription()); + assertEquals("User Interface Command", ccomp1.getDisplayName()); + assertEquals("Command", ccomp1.getComponentType()); + assertEquals("jakarta.faces.component.UICommand", ccomp1.getComponentClass()); + assertNull(ccomp1.getLargeIcon()); + assertNull(ccomp1.getSmallIcon()); + assertEquals(0, ccomp1.getAttributes().size()); + assertEquals(0, ccomp1.getProperties().size()); + + // + Map converters = base.getConverters(); + assertNotNull(converters); + ConfigConverter cc1 = (ConfigConverter) converters.get("First"); + assertNotNull(cc1); + assertEquals("First Converter Description", cc1.getDescription()); + assertEquals("First Converter Display Name", cc1.getDisplayName()); + assertEquals("firstConverter.gif", cc1.getLargeIcon()); + assertEquals("firstConverter.jpg", cc1.getSmallIcon()); + assertEquals("First", cc1.getConverterId()); + assertEquals("com.mycompany.MyFirstConverter", cc1.getConverterClass()); + assertEquals(1, cc1.getAttributes().size()); + ConfigAttribute cc1a1 = (ConfigAttribute) cc1.getAttributes().get("attr1"); + assertNotNull(cc1a1); + assertEquals("First Converter Attribute 1 Description", cc1a1.getDescription()); + assertEquals("First Converter Attribute 1 Display Name", cc1a1.getDisplayName()); + assertNull(cc1a1.getLargeIcon()); + assertNull(cc1a1.getSmallIcon()); + assertEquals("attr1", cc1a1.getAttributeName()); + assertEquals("java.lang.String", cc1a1.getAttributeClass()); + assertEquals(0, cc1.getProperties().size()); + ConfigConverter cc2 = (ConfigConverter) converters.get("Second"); + assertNotNull(cc2); + assertEquals("Second Converter Description", cc2.getDescription()); + assertEquals("Second Converter Display Name", cc2.getDisplayName()); + assertEquals("secondConverter.gif", cc2.getLargeIcon()); + assertEquals("secondConverter.jpg", cc2.getSmallIcon()); + assertEquals("Second", cc2.getConverterId()); + assertEquals("com.mycompany.MySecondConverter", cc2.getConverterClass()); + assertEquals(0, cc2.getAttributes().size()); + assertEquals(1, cc2.getProperties().size()); + ConfigProperty cc2p1 = (ConfigProperty) cc2.getProperties().get("prop1"); + assertNotNull(cc2p1); + assertEquals("Second Converter Property 1 Description", cc2p1.getDescription()); + assertEquals("Second Converter Property 1 Display Name", cc2p1.getDisplayName()); + assertNull(cc2p1.getLargeIcon()); + assertNull(cc2p1.getSmallIcon()); + assertEquals("prop1", cc2p1.getPropertyName()); + assertEquals("java.lang.String", cc2p1.getPropertyClass()); + + // + Map validators = base.getValidators(); + assertNotNull(validators); + ConfigValidator cv1 = (ConfigValidator) validators.get("First"); + assertNotNull(cv1); + assertEquals("First Validator Description", cv1.getDescription()); + assertEquals("First Validator Display Name", cv1.getDisplayName()); + assertEquals("firstValidator.gif", cv1.getLargeIcon()); + assertEquals("firstValidator.jpg", cv1.getSmallIcon()); + assertEquals("First", cv1.getValidatorId()); + assertEquals("com.mycompany.MyFirstValidator", cv1.getValidatorClass()); + assertEquals(1, cv1.getAttributes().size()); + ConfigAttribute cv1a1 = (ConfigAttribute) cv1.getAttributes().get("attr1"); + assertNotNull(cv1a1); + assertEquals("First Validator Attribute 1 Description", cv1a1.getDescription()); + assertEquals("First Validator Attribute 1 Display Name", cv1a1.getDisplayName()); + assertNull(cv1a1.getLargeIcon()); + assertNull(cv1a1.getSmallIcon()); + assertEquals("attr1", cv1a1.getAttributeName()); + assertEquals("java.lang.String", cv1a1.getAttributeClass()); + assertEquals(0, cv1.getProperties().size()); + ConfigValidator cv2 = (ConfigValidator) validators.get("Second"); + assertNotNull(cv2); + assertEquals("Second Validator Description", cv2.getDescription()); + assertEquals("Second Validator Display Name", cv2.getDisplayName()); + assertEquals("secondValidator.gif", cv2.getLargeIcon()); + assertEquals("secondValidator.jpg", cv2.getSmallIcon()); + assertEquals("Second", cv2.getValidatorId()); + assertEquals("com.mycompany.MySecondValidator", cv2.getValidatorClass()); + assertEquals(0, cv2.getAttributes().size()); + assertEquals(1, cv2.getProperties().size()); + ConfigProperty cv2p1 = (ConfigProperty) cv2.getProperties().get("prop1"); + assertNotNull(cv2p1); + assertEquals("Second Validator Property 1 Description", cv2p1.getDescription()); + assertEquals("Second Validator Property 1 Display Name", cv2p1.getDisplayName()); + assertNull(cv2p1.getLargeIcon()); + assertNull(cv2p1.getSmallIcon()); + assertEquals("prop1", cv2p1.getPropertyName()); + assertEquals("java.lang.String", cv2p1.getPropertyClass()); + } + + // ------------------------------------------------------ Protected Methods + // Create a Digester instance with no rules yet + protected Digester createDigester() throws Exception { + digester = DigesterFactory.newInstance(true).createDigester(); + return (digester); + } + + // Configure the matching rules for the specified Digester instance + // Rules assume that a ConfigBase bean is pushed on the stack first + protected void configureRules(Digester digester) { + configureRulesApplication(digester); + configureRulesConverter(digester); + configureRulesComponent(digester); + configureRulesValidator(digester); + } + + // Configure the rules for an element + protected void configureRulesApplication(Digester digester) { + digester.addCallMethod("faces-config/application/action-listener", "setActionListener", 0); + digester.addCallMethod("faces-config/application/navigation-handler", "setNavigationHandler", 0); + } + + // Configure the rules for a element + protected void configureRulesAttribute(Digester digester, String prefix) { + digester.addObjectCreate(prefix, "jakarta.faces.webapp.ConfigAttribute"); + digester.addSetNext(prefix, "addAttribute", "jakarta.faces.webapp.ConfigAttribute"); + configureRulesFeature(digester, prefix); + digester.addCallMethod(prefix + "/attribute-name", "setAttributeName", 0); + digester.addCallMethod(prefix + "/attribute-class", "setAttributeClass", 0); + } + + // Configure the rules for a element + protected void configureRulesComponent(Digester digester) { + String prefix = "faces-config/component"; + + digester.addObjectCreate(prefix, "jakarta.faces.webapp.ConfigComponent"); + digester.addSetNext(prefix, "addComponent", "jakarta.faces.webapp.ConfigComponent"); + configureRulesFeature(digester, prefix); + digester.addCallMethod(prefix + "/component-type", "setComponentType", 0); + digester.addCallMethod(prefix + "/component-class", "setComponentClass", 0); + configureRulesAttribute(digester, prefix + "/attribute"); + configureRulesProperty(digester, prefix + "/property"); + } + + // Configure the rules for a element + protected void configureRulesConverter(Digester digester) { + String prefix = "faces-config/converter"; + + digester.addObjectCreate(prefix, "jakarta.faces.webapp.ConfigConverter"); + digester.addSetNext(prefix, "addConverter", "jakarta.faces.webapp.ConfigConverter"); + configureRulesFeature(digester, prefix); + digester.addCallMethod(prefix + "/converter-id", "setConverterId", 0); + digester.addCallMethod(prefix + "/converter-class", "setConverterClass", 0); + configureRulesAttribute(digester, prefix + "/attribute"); + configureRulesProperty(digester, prefix + "/property"); + } + + // Configure the generic feature rules for the specified prefix + protected void configureRulesFeature(Digester digester, String prefix) { + digester.addCallMethod(prefix + "/description", "setDescription", 0); + digester.addCallMethod(prefix + "/display-name", "setDisplayName", 0); + digester.addCallMethod(prefix + "/icon/large-icon", "setLargeIcon", 0); + digester.addCallMethod(prefix + "/icon/small-icon", "setSmallIcon", 0); + } + + // Configure the rules for a element + protected void configureRulesProperty(Digester digester, String prefix) { + digester.addObjectCreate(prefix, "jakarta.faces.webapp.ConfigProperty"); + digester.addSetNext(prefix, "addProperty", "jakarta.faces.webapp.ConfigProperty"); + configureRulesFeature(digester, prefix); + digester.addCallMethod(prefix + "/property-name", "setPropertyName", 0); + digester.addCallMethod(prefix + "/property-class", "setPropertyClass", 0); + } + + // Configure the rules for a element + protected void configureRulesValidator(Digester digester) { + String prefix = "faces-config/validator"; + + digester.addObjectCreate(prefix, "jakarta.faces.webapp.ConfigValidator"); + digester.addSetNext(prefix, "addValidator", "jakarta.faces.webapp.ConfigValidator"); + configureRulesFeature(digester, prefix); + digester.addCallMethod(prefix + "/validator-id", "setValidatorId", 0); + digester.addCallMethod(prefix + "/validator-class", "setValidatorClass", 0); + configureRulesAttribute(digester, prefix + "/attribute"); + configureRulesProperty(digester, prefix + "/property"); + } + + // Parse the configuration file at the specified URL + protected ConfigBase parseConfig(URL config) throws Exception { + digester.clear(); + digester.push(new ConfigBase()); + InputSource iso = new InputSource(config.toExternalForm()); + InputStream ist = config.openStream(); + iso.setByteStream(ist); + ConfigBase base = (ConfigBase) digester.parse(iso); + ist.close(); + return (base); + } + + // Return the URL of the specified path, relative to our base directory + protected URL relativeURL(String relativePath) throws Exception { + File file = new File(System.getProperty("base.dir"), relativePath); + return (file.toURI().toURL()); + } +} diff --git a/impl/src/test/java/jakarta/faces/webapp/FacesServletTestCase.java b/impl/src/test/java/jakarta/faces/webapp/FacesServletTestCase.java new file mode 100644 index 0000000000..7e9fa7ea6d --- /dev/null +++ b/impl/src/test/java/jakarta/faces/webapp/FacesServletTestCase.java @@ -0,0 +1,211 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.faces.webapp; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.sun.faces.junit.JUnitFacesTestCaseBase; +import com.sun.faces.mock.MockRenderKit; + +import jakarta.faces.FactoryFinder; +import jakarta.faces.component.UIViewRoot; +import jakarta.faces.render.RenderKit; +import jakarta.faces.render.RenderKitFactory; +import jakarta.servlet.http.HttpServletResponse; + +public class FacesServletTestCase extends JUnitFacesTestCaseBase { + + // this is private in FacesServlet to not break backwards compatibility + private static final String ALLOWED_HTTP_METHODS_ATTR_COPY + = "com.sun.faces.allowedHttpMethods"; + + @Override + @BeforeEach + public void setUp() throws Exception { + super.setUp(); + // Set up Servlet API Objects + servletContext.addInitParameter("appParamName", "appParamValue"); + servletContext.setAttribute("appScopeName", "appScopeValue"); + session.setAttribute("sesScopeName", "sesScopeValue"); + request.setAttribute("reqScopeName", "reqScopeValue"); + + UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null); + root.setViewId("/viewId"); + facesContext.setViewRoot(root); + RenderKitFactory renderKitFactory = (RenderKitFactory) FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY); + RenderKit renderKit = new MockRenderKit(); + try { + renderKitFactory.addRenderKit(RenderKitFactory.HTML_BASIC_RENDER_KIT, + renderKit); + } catch (IllegalArgumentException e) { + } + } + + @Test + public void testPositiveInitWithNoContextParams() throws Exception { + FacesServlet me = new FacesServlet(); + me.init(config); + this.sendRequest(me, "OPTIONS"); + assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + this.sendRequest(me, "GET"); + assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + this.sendRequest(me, "HEAD"); + assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + this.sendRequest(me, "POST"); + assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + this.sendRequest(me, "PUT"); + assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + this.sendRequest(me, "DELETE"); + assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + this.sendRequest(me, "TRACE"); + assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + this.sendRequest(me, "CONNECT"); + assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + } + + @Test + public void testPositiveInitWithContextParamsOfKnownHttpMethods() throws Exception { + FacesServlet me = new FacesServlet(); + servletContext.addInitParameter(ALLOWED_HTTP_METHODS_ATTR_COPY, "GET POST"); + me.init(config); + this.sendRequest(me, "OPTIONS"); + assertEquals(HttpServletResponse.SC_BAD_REQUEST, response.getStatus()); + this.sendRequest(me, "GET"); + assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + this.sendRequest(me, "HEAD"); + assertEquals(HttpServletResponse.SC_BAD_REQUEST, response.getStatus()); + this.sendRequest(me, "POST"); + assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + this.sendRequest(me, "PUT"); + assertEquals(HttpServletResponse.SC_BAD_REQUEST, response.getStatus()); + this.sendRequest(me, "DELETE"); + assertEquals(HttpServletResponse.SC_BAD_REQUEST, response.getStatus()); + this.sendRequest(me, "TRACE"); + assertEquals(HttpServletResponse.SC_BAD_REQUEST, response.getStatus()); + this.sendRequest(me, "CONNECT"); + assertEquals(HttpServletResponse.SC_BAD_REQUEST, response.getStatus()); + } + + @Test + public void testNegativeInitWithContextParamsOfKnownHttpMethods() throws Exception { + FacesServlet me = new FacesServlet(); + servletContext.addInitParameter(ALLOWED_HTTP_METHODS_ATTR_COPY, "GET POST GET POST"); + me.init(config); + this.sendRequest(me, "OPTIONS"); + assertEquals(HttpServletResponse.SC_BAD_REQUEST, response.getStatus()); + this.sendRequest(me, "GET"); + assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + this.sendRequest(me, "HEAD"); + assertEquals(HttpServletResponse.SC_BAD_REQUEST, response.getStatus()); + this.sendRequest(me, "POST"); + assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + this.sendRequest(me, "PUT"); + assertEquals(HttpServletResponse.SC_BAD_REQUEST, response.getStatus()); + this.sendRequest(me, "DELETE"); + assertEquals(HttpServletResponse.SC_BAD_REQUEST, response.getStatus()); + this.sendRequest(me, "TRACE"); + assertEquals(HttpServletResponse.SC_BAD_REQUEST, response.getStatus()); + this.sendRequest(me, "CONNECT"); + assertEquals(HttpServletResponse.SC_BAD_REQUEST, response.getStatus()); + } + + @Test + public void testPositiveInitWithContextParamsOfWildcardHttpMethods() throws Exception { + FacesServlet me = new FacesServlet(); + servletContext.addInitParameter(ALLOWED_HTTP_METHODS_ATTR_COPY, "*"); + me.init(config); + this.sendRequest(me, "OPTIONS"); + assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + this.sendRequest(me, "GET"); + assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + this.sendRequest(me, "HEAD"); + assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + this.sendRequest(me, "POST"); + assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + this.sendRequest(me, "PUT"); + assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + this.sendRequest(me, "DELETE"); + assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + this.sendRequest(me, "TRACE"); + assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + this.sendRequest(me, "CONNECT"); + assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + this.sendRequest(me, "BOO_YA"); + assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + } + + @Test + public void testNegativeInitWithContextParamsOfWildcardHttpMethods() throws Exception { + FacesServlet me = new FacesServlet(); + servletContext.addInitParameter(ALLOWED_HTTP_METHODS_ATTR_COPY, "* * * *"); + me.init(config); + this.sendRequest(me, "OPTIONS"); + assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + this.sendRequest(me, "GET"); + assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + this.sendRequest(me, "HEAD"); + assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + this.sendRequest(me, "POST"); + assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + this.sendRequest(me, "PUT"); + assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + this.sendRequest(me, "DELETE"); + assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + this.sendRequest(me, "TRACE"); + assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + this.sendRequest(me, "CONNECT"); + assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + this.sendRequest(me, "BOO_YA"); + assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + } + + @Test + public void testPositiveInitWithContextParamsOfUnknownAndKnownHttpMethods() throws Exception { + FacesServlet me = new FacesServlet(); + servletContext.addInitParameter(ALLOWED_HTTP_METHODS_ATTR_COPY, "GET\tPOST\tGETAAAAA"); + me.init(config); + this.sendRequest(me, "OPTIONS"); + assertEquals(HttpServletResponse.SC_BAD_REQUEST, response.getStatus()); + this.sendRequest(me, "GET"); + assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + this.sendRequest(me, "HEAD"); + assertEquals(HttpServletResponse.SC_BAD_REQUEST, response.getStatus()); + this.sendRequest(me, "POST"); + assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + this.sendRequest(me, "PUT"); + assertEquals(HttpServletResponse.SC_BAD_REQUEST, response.getStatus()); + this.sendRequest(me, "DELETE"); + assertEquals(HttpServletResponse.SC_BAD_REQUEST, response.getStatus()); + this.sendRequest(me, "TRACE"); + assertEquals(HttpServletResponse.SC_BAD_REQUEST, response.getStatus()); + this.sendRequest(me, "CONNECT"); + assertEquals(HttpServletResponse.SC_BAD_REQUEST, response.getStatus()); + this.sendRequest(me, "BOO_YA"); + assertEquals(HttpServletResponse.SC_BAD_REQUEST, response.getStatus()); + this.sendRequest(me, "GETAAAAA"); + assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + } + + private void sendRequest(FacesServlet me, String method) throws Exception { + request.setMethod(method); + request.setPathElements("/test", "/test", "/test", ""); + me.service(request, response); + } +} diff --git a/pom.xml b/pom.xml index fc9058f8f0..38700da70e 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@ maven-compiler-plugin - 3.11.0 + 3.12.1 17 -Xlint:unchecked