Merge branch 'dev-contacts' into 'dev'

feat(contacts): ajout et gestion de la classe contact

See merge request iut_rt/but2/sae302-applicom/bouclyma!2
This commit is contained in:
Emi Boucly 2024-12-10 21:42:30 +01:00
commit 5805e55209
14 changed files with 684 additions and 28 deletions

View file

@ -25,6 +25,22 @@
<artifactId>javafx-fxml</artifactId> <artifactId>javafx-fxml</artifactId>
<version>18</version> <version>18</version>
</dependency> </dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-swing</artifactId>
<version>19</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.synedra</groupId>
<artifactId>validatorfx</artifactId>
<version>0.5.1</version>
</dependency>
<dependency> <dependency>
<groupId>org.junit.jupiter</groupId> <groupId>org.junit.jupiter</groupId>

View file

@ -2,8 +2,13 @@ module rtgre.chat {
requires javafx.controls; requires javafx.controls;
requires javafx.fxml; requires javafx.fxml;
requires java.logging; requires java.logging;
requires java.desktop;
requires javafx.swing;
requires net.synedra.validatorfx;
opens rtgre.chat to javafx.fxml; opens rtgre.chat to javafx.fxml;
exports rtgre.chat; exports rtgre.chat;
exports rtgre.chat.graphisme;
opens rtgre.chat.graphisme to javafx.fxml;
} }

View file

@ -15,8 +15,8 @@ import java.util.logging.Logger;
public class ChatApplication extends Application { public class ChatApplication extends Application {
public static final Logger LOGGER = Logger.getLogger(ChatApplication.class.getCanonicalName()); public static final Logger LOGGER = Logger.getLogger(ChatApplication.class.getCanonicalName());
public class EssaiLogger { public class EssaiLogger {
/* . . . */
static { static {
try { try {
InputStream is = EssaiLogger.class.getClassLoader() InputStream is = EssaiLogger.class.getClassLoader()
@ -26,8 +26,8 @@ public class ChatApplication extends Application {
LOGGER.log(Level.INFO, "Cannot read configuration file", e); LOGGER.log(Level.INFO, "Cannot read configuration file", e);
} }
} }
/* . . . */
} }
@Override @Override
public void start(Stage stage) throws IOException { public void start(Stage stage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(ChatApplication.class.getResource("chat-view.fxml")); FXMLLoader fxmlLoader = new FXMLLoader(ChatApplication.class.getResource("chat-view.fxml"));

View file

@ -2,22 +2,47 @@ package rtgre.chat;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.beans.Observable; import javafx.beans.Observable;
import javafx.beans.binding.Bindings;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.embed.swing.SwingFXUtils;
import javafx.event.ActionEvent;
import javafx.event.Event; import javafx.event.Event;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.fxml.Initializable; import javafx.fxml.Initializable;
import javafx.scene.control.*; import javafx.scene.control.*;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import javafx.scene.image.ImageView; import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import net.synedra.validatorfx.Check;
import net.synedra.validatorfx.TooltipWrapper;
import net.synedra.validatorfx.Validator;
import rtgre.chat.graphisme.ContactListViewCell;
import rtgre.modeles.Contact;
import rtgre.modeles.ContactMap;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL; import java.net.URL;
import java.util.Date; import java.util.Date;
import java.util.Objects; import java.util.Objects;
import java.util.ResourceBundle; import java.util.ResourceBundle;
import java.util.logging.Logger; import java.util.logging.Logger;
import java.util.regex.Pattern;
import static rtgre.chat.ChatApplication.LOGGER; import static rtgre.chat.ChatApplication.LOGGER;
public class ChatController implements Initializable { public class ChatController implements Initializable {
private static final Pattern LOGIN_PATTERN = Pattern.compile("^([a-z][a-z0-9]{2,7})$");
private static final Pattern HOST_PATTERN = Pattern.compile("/^[a-z]*((\\:?)\\d{1,5})?$/gm");
public MenuItem hostAddMenuItem; public MenuItem hostAddMenuItem;
public MenuItem avatarMenuItem; public MenuItem avatarMenuItem;
public MenuItem aboutMenuItem; public MenuItem aboutMenuItem;
@ -28,11 +53,15 @@ public class ChatController implements Initializable {
public SplitPane exchangeSplitPane; public SplitPane exchangeSplitPane;
public ListView postListView; public ListView postListView;
public ListView roomsListView; public ListView roomsListView;
public ListView contactListView; public ListView contactsListView;
public TextField messageTextField; public TextField messageTextField;
public Button sendButton; public Button sendButton;
public Label statusLabel; public Label statusLabel;
public Label dateTimeLabel; public Label dateTimeLabel;
public Contact contact;
private ContactMap contactMap = new ContactMap();
private ObservableList<Contact> contactObservableList = FXCollections.observableArrayList();
Validator validatorLogin = new Validator();
@Override @Override
@ -53,13 +82,74 @@ public class ChatController implements Initializable {
statusLabel.setText("not connected to " + hostComboBox.getValue()); statusLabel.setText("not connected to " + hostComboBox.getValue());
connectionButton.disableProperty().bind(validatorLogin.containsErrorsProperty());
connectionButton.selectedProperty().addListener(this::handleConnection);
loginTextField.disableProperty().bind(connectionButton.selectedProperty());
hostComboBox.disableProperty().bind(connectionButton.selectedProperty());
avatarMenuItem.setOnAction(this::handleAvatarChange);
avatarImageView.setOnMouseClicked(this::handleAvatarChange);
initContactListView();
validatorLogin.createCheck()
.dependsOn("login", loginTextField.textProperty())
.withMethod(this::checkLogin)
.decorates(loginTextField)
.immediate();
/* /!\ Set-up d'environnement de test /!\ */
/* -------------------------------------- */
loginTextField.setText("riri");
connectionButton.setSelected(true);
}
private void handleAvatarChange(Event event) {
/**
* Ouvre une fenêtre de dialogue permettant de choisir son avatar
*/
FileChooser fileChooser = new FileChooser();
Stage stage = (Stage) avatarImageView.getScene().getWindow();
fileChooser.setTitle("Select Avatar");
fileChooser.getExtensionFilters().addAll(
new FileChooser.ExtensionFilter("Image Files", "*.png", "*.jpg")
);
File selectedFile = fileChooser.showOpenDialog(stage);
if (selectedFile != null) {
avatarImageView.setImage(new Image(selectedFile.toURI().toString()));
}
}
private void handleConnection(Observable observable) {
/**
*
*/
if (connectionButton.isSelected()) {
java.awt.Image img = SwingFXUtils.fromFXImage(this.avatarImageView.getImage(), null);
this.contact = new Contact(loginTextField.getText(), img);
contactMap.put(this.contact.getLogin(), this.contact);
System.out.println("Nouveau contact : " + contact);
System.out.println(contactMap);
}
}
private void checkLogin(Check.Context context) {
String login = context.get("login");
if (!LOGIN_PATTERN.matcher(login).matches()) {
context.error("Format de login non respecté");
}
if (login.equals("system")) {
context.error("Le login ne peut pas être system");
}
} }
private void statusNameUpdate(Event event) { private void statusNameUpdate(Event event) {
statusLabel.setText("not connected to " + hostComboBox.getValue()); statusLabel.setText("not connected to " + hostComboBox.getValue());
} }
@ -67,7 +157,6 @@ public class ChatController implements Initializable {
while (true) { while (true) {
try { try {
String datetime = "%1$ta %1$te %1$tb %1$tY - %1$tH:%1$tM".formatted(new Date()); String datetime = "%1$ta %1$te %1$tb %1$tY - %1$tH:%1$tM".formatted(new Date());
// System.out.println(datetime);
Platform.runLater(() -> dateTimeLabel.setText(datetime)); Platform.runLater(() -> dateTimeLabel.setText(datetime));
Thread.sleep(60000); Thread.sleep(60000);
} catch (Exception e) { } catch (Exception e) {
@ -76,4 +165,20 @@ public class ChatController implements Initializable {
} }
} }
private void initContactListView() {
try {
contactsListView.setCellFactory(contactListView -> new ContactListViewCell());
contactsListView.setItems(contactObservableList);
File avatars = new File(getClass().getResource("avatars.png").toURI());
Contact riri = new Contact("riri", false, avatars);
Contact fifi = new Contact("fifi", true, avatars);
contactObservableList.add(riri);
contactMap.add(riri);
contactObservableList.add(fifi);
contactMap.add(fifi);
} catch (Exception e) {
LOGGER.severe(e.getMessage());
}
}
} }

View file

@ -0,0 +1,88 @@
package rtgre.chat.graphisme;
import rtgre.chat.ChatController;
import javafx.embed.swing.SwingFXUtils;
import javafx.geometry.Pos;
import javafx.scene.control.ListCell;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import rtgre.modeles.Contact;
import java.awt.image.BufferedImage;
import static rtgre.chat.ChatApplication.LOGGER;
/**
* Classe modélisant la fabrique de cellule de la vue des contacts
* visibles/connectés {@link ChatController#contactsListView}.
*
* @see ListCell
*/
public class ContactListViewCell extends ListCell<Contact> {
/**
* Callback déclenchée à chaque modification d'un objet d'une liste d'observable.
*
* @param contact Le contact à mettre à jour
* @param empty La liste de cellule doit-elle être complètement remise à zéro ?
*/
@Override
protected void updateItem(Contact contact, boolean empty) {
super.updateItem(contact, empty);
if (empty) {
setGraphic(null);
}
else {
// Cas d'un contact
updateContact(contact);
}
}
/**
* Mise à jour de la cellule d'un contact.
*
* @param contact Le contact à mettre à jour
*/
private void updateContact(Contact contact) {
LOGGER.finest("Mise à jour de " + contact);
String unreadCountNotif = (contact.getUnreadCount() == 0) ? "" : " (%d)".formatted(contact.getUnreadCount());
LOGGER.finest("unread: %s %s".formatted(contact.getLogin(), unreadCountNotif));
Text loginText = new Text(contact.getLogin() + unreadCountNotif);
loginText.setFont(Font.font(null, 12)); // FontWeight.BOLD, 14));
loginText.setFill(contact.isConnected() ? Color.BLACK : Color.GRAY);
Circle circle = new Circle(5, 5, 5);
circle.setFill(contact.isConnected() ? Color.CADETBLUE : Color.FIREBRICK);
// circle.setOpacity(contact.is_connected() ? 1 : 0.5);
Image avatar;
ImageView view = new ImageView();
if (contact.getAvatar() != null) {
avatar = SwingFXUtils.toFXImage((BufferedImage) contact.getAvatar(), null);
view = new ImageView(avatar);
}
view.setOpacity(contact.isConnected() ? 1 : 0.5);
view.setFitWidth(15);
view.setFitHeight(15);
HBox temp = new HBox(circle);
temp.setAlignment(Pos.CENTER_RIGHT);
HBox.setHgrow(temp, Priority.ALWAYS);
HBox hBox = new HBox(view, loginText, temp);
hBox.setSpacing(5.0);
hBox.setAlignment(Pos.CENTER_LEFT);
setGraphic(hBox);
}
}

View file

@ -0,0 +1,122 @@
package rtgre.modeles;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Objects;
public class Contact {
protected String login;
protected java.awt.Image avatar;
protected boolean connected;
protected String currentRoom;
public Contact(String login, java.awt.Image avatar) {
/**
* Crée un objet Contact
* @param: String login
* @param: java.awt.Image avatar
*/
this.login = login;
this.avatar = avatar;
this.connected = false;
this.currentRoom = null;
}
public Contact(String login, boolean connected, java.awt.Image avatar) {
/**
* Crée un objet Contact
* @param: String login
* @param: boolean connected
* @param: java.awt.Image avatar
*/
this.login = login;
this.avatar = avatar;
this.connected = connected;
this.currentRoom = null;
}
public String getCurrentRoom() {
return currentRoom;
}
public Contact(String login, boolean connected, File banques_avatars) {
/**
* Crée un objet Contact
* @param: String login
* @param: boolean connected
* @param: File banques_avatars
*/
this.login = login;
try {
this.avatar = avatarFromLogin(banques_avatars, login);
} catch (IOException e) {
System.out.println("Impossible de créer l'utilisateur " + login);
System.out.println(e.getMessage());
System.out.println(banques_avatars);
}
this.connected = connected;
this.currentRoom = null;
}
public String getLogin() {
return this.login;
}
public java.awt.Image getAvatar() {
return this.avatar;
}
@Override
public String toString() {
return "@" + this.login;
}
public boolean isConnected() {
return this.connected;
}
public void setConnected(boolean connected) {
this.connected = connected;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Contact contact = (Contact) o;
return Objects.equals(login, contact.login);
}
@Override
public int hashCode() {
return Objects.hashCode(login);
}
public int getUnreadCount() {
return 0;
}
public static BufferedImage avatarFromLogin(File fichier, String login) throws IOException {
/**
* Renvoie une sous-image en fonction d'une banque d'image et d'un login.
* @param: File fichier
* @param: String login
*/
BufferedImage img = ImageIO.read(fichier);
int width = img.getWidth() / 9;
int height = img.getHeight();
int n = Integer.remainderUnsigned(login.hashCode(), 9);
return img.getSubimage(n*width, 0, width, height);
}
public void setAvatarFromFile(File f) {
try {
this.avatar = avatarFromLogin(f, this.login);
} catch (IOException e) {
System.out.println("Erreur : " + e.getMessage());
}
}
}

View file

@ -0,0 +1,12 @@
package rtgre.modeles;
import java.util.TreeMap;
public class ContactMap extends TreeMap<String, Contact> {
public void add(Contact contact) {
this.put(contact.login, contact);
}
public Contact getContact(String login) {
return this.get(login);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

View file

@ -1,27 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?> <?import javafx.geometry.*?>
<?import javafx.scene.control.Button?> <?import javafx.scene.control.*?>
<?import javafx.scene.control.ComboBox?> <?import javafx.scene.image.*?>
<?import javafx.scene.control.Label?> <?import javafx.scene.layout.*?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.control.Menu?>
<?import javafx.scene.control.MenuBar?>
<?import javafx.scene.control.MenuItem?>
<?import javafx.scene.control.Separator?>
<?import javafx.scene.control.SplitPane?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.control.ToggleButton?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.layout.VBox?>
<VBox alignment="CENTER" minHeight="400.0" minWidth="600.0" prefHeight="500.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/18" xmlns:fx="http://javafx.com/fxml/1" fx:controller="rtgre.chat.ChatController"> <VBox alignment="CENTER" minHeight="400.0" minWidth="600.0" prefHeight="500.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/17.0.2-ea" xmlns:fx="http://javafx.com/fxml/1" fx:controller="rtgre.chat.ChatController">
<children> <children>
<MenuBar> <MenuBar VBox.vgrow="NEVER">
<menus> <menus>
<Menu mnemonicParsing="false" text="Edit"> <Menu mnemonicParsing="false" text="Edit">
<items> <items>
@ -77,7 +63,7 @@
<SplitPane dividerPositions="0.1" orientation="VERTICAL" prefHeight="200.0" prefWidth="160.0"> <SplitPane dividerPositions="0.1" orientation="VERTICAL" prefHeight="200.0" prefWidth="160.0">
<items> <items>
<ListView fx:id="roomsListView" prefHeight="200.0" prefWidth="200.0" /> <ListView fx:id="roomsListView" prefHeight="200.0" prefWidth="200.0" />
<ListView fx:id="contactListView" prefHeight="200.0" prefWidth="200.0" /> <ListView fx:id="contactsListView" prefHeight="200.0" prefWidth="200.0" />
</items> </items>
</SplitPane> </SplitPane>
</items> </items>
@ -111,7 +97,7 @@
</children> </children>
</HBox> </HBox>
<Separator prefWidth="200.0" /> <Separator prefWidth="200.0" />
<HBox> <HBox VBox.vgrow="NEVER">
<children> <children>
<Label text="Status : " /> <Label text="Status : " />
<Label fx:id="statusLabel" text="Not connected" /> <Label fx:id="statusLabel" text="Not connected" />

View file

@ -0,0 +1,155 @@
package rtgre.modeles;
import org.junit.jupiter.api.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import java.lang.reflect.*;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.junit.jupiter.params.provider.Arguments.arguments;
/** Tests unitaires du modèle de base de Contact (étape 1) */
class ContactBaseTest1 {
static Class<?> classe = Contact.class;
static String module = "rtgre.modeles";
@DisplayName("01-Structure de la classe Contact")
@Nested
class StructureTest {
static List<String> constructeursSignatures;
static List<String> methodesSignatures;
@BeforeAll
static void init() {
Constructor<?>[] constructeurs = classe.getConstructors();
constructeursSignatures = Arrays.stream(constructeurs).map(Constructor::toString).collect(Collectors.toList());
Method[] methodes = classe.getDeclaredMethods();
methodesSignatures = Arrays.stream(methodes).map(Method::toString).collect(Collectors.toList());
}
/**
* Attributs
*/
static Stream<Arguments> attributsProvider() {
return Stream.of(
arguments("login", "java.lang.String", Modifier.PROTECTED),
arguments("avatar", "java.awt.Image", Modifier.PROTECTED),
arguments("connected", "boolean", Modifier.PROTECTED),
arguments("currentRoom", "java.lang.String", Modifier.PROTECTED)
);
}
@DisplayName("Déclaration des attributs : nom, type et visibilité")
@ParameterizedTest
@MethodSource("attributsProvider")
void testDeclarationAttributs(String nom, String type, int modifier) throws NoSuchFieldException {
Field field = classe.getDeclaredField(nom);
Assertions.assertEquals(type, field.getType().getName(),
"Type " + nom + " erroné : doit être " + type);
Assertions.assertEquals(modifier, field.getModifiers(),
"Visibilité " + nom + " erronée : doit être " + modifier);
}
/**
* Constructeurs
*/
static Stream<Arguments> constructeursProvider() {
return Stream.of(
arguments("public %s.Contact(java.lang.String,java.awt.Image)")
);
}
@DisplayName("Déclaration des constructeurs (base)")
@ParameterizedTest
@MethodSource("constructeursProvider")
void testConstructeurs1(String signature) {
Assertions.assertTrue(constructeursSignatures.contains(String.format(signature, module)),
String.format("Constructeur non déclaré : doit être %s\nalors que sont déclarés %s",
signature, constructeursSignatures));
}
/**
* Méthodes
*/
static Stream<Arguments> methodesProvider1() {
return Stream.of(
arguments("getLogin", "public java.lang.String %s.Contact.getLogin()"),
arguments("getAvatar", "public java.awt.Image %s.Contact.getAvatar()"),
arguments("isConnected", "public boolean %s.Contact.isConnected()"),
arguments("toString", "public boolean %s.Contact.isConnected()"),
arguments("setConnected", "public java.lang.String %s.Contact.toString()"),
arguments("equals", "public boolean %s.Contact.equals(java.lang.Object)")
);
}
@DisplayName("Déclaration des méthodes (base)")
@ParameterizedTest
@MethodSource("methodesProvider1")
void testDeclarationMethodes1(String nom, String signature) {
Assertions.assertTrue(methodesSignatures.contains(String.format(signature, module, module)),
String.format("Méthode non déclarée : doit être %s\nalors que sont déclarés %s",
signature, methodesSignatures));
}
@DisplayName("02-Instanciation et getters")
@Nested
class InstanciationContactTest {
@Test
@DisplayName("sans avatar : getters de login, connected")
void TestConstructeurParDefautSansAvatar() {
Contact riri = new Contact("riri", null);
Assertions.assertEquals("riri", riri.getLogin(), "Login erroné");
Assertions.assertFalse(riri.isConnected(), "Etat par défaut erroné");
}
}
@DisplayName("03-Modification")
@Nested
class ModificationContactTest {
@Test
@DisplayName("Setter de connexion")
void TestEtatConnexion() {
Contact riri = new Contact("riri", null);
riri.setConnected(true);
Assertions.assertTrue(riri.isConnected(), "Changement d'état erroné");
riri.setConnected(false);
Assertions.assertFalse(riri.isConnected(), "Changement d'état erroné");
}
}
@Test
@DisplayName("04-Représentation textuelle")
void TestToString() {
Contact riri = new Contact("riri", null);
Assertions.assertEquals("@riri", riri.toString(),
"Représentation textuelle erronée");
}
@Test
@DisplayName("05-Egalité")
void TestEquals() {
Contact riri = new Contact("riri", null);
Contact riri2 = new Contact("riri", null);
Contact fifi = new Contact("fifi", null);
Assertions.assertEquals(riri, riri2, "Comparaison erronée");
Assertions.assertNotEquals(riri, fifi, "Comparaison erronée");
}
}
}

View file

@ -0,0 +1,47 @@
package rtgre.modeles;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import java.util.TreeMap;
class ContactMapTest {
static Class<?> classe = ContactMap.class;
@DisplayName("01-Structure")
@Nested
class StructureContactMapTest {
@Test
@DisplayName("Heritage")
void testHeritage() {
ContactMap contactMap = new ContactMap();
Assertions.assertInstanceOf(TreeMap.class, contactMap, "doit hériter de TreeMap");
}
}
@DisplayName("02-Ajout")
@Nested
class AddTest {
@Test
@DisplayName("Ajout d'un contact")
void TestAdd() {
Contact riri = new Contact("riri", null);
ContactMap contactMap = new ContactMap();
contactMap.add(riri);
Assertions.assertTrue(contactMap.containsKey("riri"),
"Les clés sont les logins");
Assertions.assertTrue(contactMap.containsValue(riri),
"Les valeurs sont les contacts");
}
}
}

View file

@ -0,0 +1,120 @@
package rtgre.modeles;
import org.junit.jupiter.api.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import javax.imageio.ImageIO;
import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.junit.jupiter.params.provider.Arguments.arguments;
/** Tests unitaires du modèle de base de Contact (étape 1) */
class ContactWithAvatarTest2 {
static Class<?> classe = Contact.class;
static String module = "rtgre.modeles";
@DisplayName("01-Structure de la classe Contact")
@Nested
class StructureTest {
static List<String> constructeursSignatures;
static List<String> methodesSignatures;
@BeforeAll
static void init() {
Constructor<?>[] constructeurs = classe.getConstructors();
constructeursSignatures = Arrays.stream(constructeurs).map(Constructor::toString).collect(Collectors.toList());
Method[] methodes = classe.getDeclaredMethods();
methodesSignatures = Arrays.stream(methodes).map(Method::toString).collect(Collectors.toList());
}
static Stream<Arguments> constructeursProvider2() {
return Stream.of(
arguments("public %s.Contact(java.lang.String,boolean,java.io.File)")
);
}
// @Disabled("Jusqu'à ce que soit codé les avatars à partir d'un fichier")
@DisplayName("Déclaration des constructeurs (avec avatars à partir d'un fichier)")
@ParameterizedTest
@MethodSource("constructeursProvider2")
void testConstructeurs2(String signature) {
Assertions.assertTrue(constructeursSignatures.contains(String.format(signature, module)),
String.format("Constructeur non déclaré : doit être %s\nalors que sont déclarés %s",
signature, constructeursSignatures));
}
static Stream<Arguments> methodesProvider2() {
return Stream.of(
arguments("avatarFromLogin", "public static java.awt.image.BufferedImage %s.Contact.avatarFromLogin(java.io.File,java.lang.String) throws java.io.IOException"),
arguments("setAvatarFromFile", "public void %s.Contact.setAvatarFromFile(java.io.File)")
);
}
// @Disabled("Jusqu'à ce que soit codé les avatars à partir d'un fichier")
@DisplayName("Déclaration des méthodes (avec avatars à partir d'un fichier)")
@ParameterizedTest
@MethodSource("methodesProvider2")
void testDeclarationMethodes2(String nom, String signature) {
Assertions.assertTrue(methodesSignatures.contains(String.format(signature, module, module)),
String.format("Méthode non déclarée : doit être %s\nalors que sont déclarés %s",
signature, methodesSignatures));
}
}
@DisplayName("02-Instanciation et getters")
@Nested
class InstanciationContactTest {
// @Disabled("Jusqu'à ce que soit codé les avatars à partir d'un fichier")
@Test
@DisplayName("avec avatar : getters de avatar")
void TestConstructeurParDefautAvecAvatar() throws IOException {
String work_dir = System.getProperty("user.dir");
Assertions.assertTrue(work_dir.endsWith("chat"),
"Le working dir doit être <projet>/chat/ et non : " + work_dir);
File f = new File("src/main/resources/rtgre/chat/anonymous.png");
Assertions.assertTrue(f.canRead(), "Fichier manquant " + f.getAbsolutePath());
Image avatar = ImageIO.read(f);
Contact fifi = new Contact("fifi", avatar);
Assertions.assertEquals(avatar, fifi.getAvatar(), "Avatar erroné");
}
}
// @Disabled("Jusqu'à ce que soit codé les avatars à partir d'un fichier")
@Nested
@DisplayName("06-Avatar à partir d'un fichier")
class AvatarFromFilesTest {
@Test
@DisplayName("A partir d'un fichier")
void TestSetFromFile() throws IOException {
String work_dir = System.getProperty("user.dir");
Assertions.assertTrue(work_dir.endsWith("chat"),
"Le working dir doit être <projet>/chat/ et non : " + work_dir);
File f = new File("src/main/resources/rtgre/chat/avatar1.png");
Assertions.assertTrue(f.canRead(), "Fichier manquant " + f.getAbsolutePath());
Contact fifi = new Contact("fifi", null);
fifi.setAvatarFromFile(f);
Assertions.assertNotNull(fifi.getAvatar(), "Avatar non chargé");
}
}
}