diff --git a/chat/pom.xml b/chat/pom.xml
index fa1b04d..a61a9a2 100644
--- a/chat/pom.xml
+++ b/chat/pom.xml
@@ -25,6 +25,22 @@
javafx-fxml
18
+
+ org.openjfx
+ javafx-swing
+ 19
+
+
+ org.junit.jupiter
+ junit-jupiter-params
+ 5.8.1
+ test
+
+
+ net.synedra
+ validatorfx
+ 0.5.1
+
org.junit.jupiter
diff --git a/chat/src/main/java/module-info.java b/chat/src/main/java/module-info.java
index 714abc1..d4b00e4 100644
--- a/chat/src/main/java/module-info.java
+++ b/chat/src/main/java/module-info.java
@@ -3,8 +3,12 @@ module rtgre.chat {
requires javafx.fxml;
requires java.logging;
requires java.desktop;
+ requires javafx.swing;
+ requires net.synedra.validatorfx;
opens rtgre.chat to javafx.fxml;
exports rtgre.chat;
+ exports rtgre.chat.graphisme;
+ opens rtgre.chat.graphisme to javafx.fxml;
}
\ No newline at end of file
diff --git a/chat/src/main/java/rtgre/chat/ChatApplication.java b/chat/src/main/java/rtgre/chat/ChatApplication.java
index b4e2085..039b5e8 100644
--- a/chat/src/main/java/rtgre/chat/ChatApplication.java
+++ b/chat/src/main/java/rtgre/chat/ChatApplication.java
@@ -15,8 +15,8 @@ import java.util.logging.Logger;
public class ChatApplication extends Application {
public static final Logger LOGGER = Logger.getLogger(ChatApplication.class.getCanonicalName());
+
public class EssaiLogger {
- /* . . . */
static {
try {
InputStream is = EssaiLogger.class.getClassLoader()
@@ -26,8 +26,8 @@ public class ChatApplication extends Application {
LOGGER.log(Level.INFO, "Cannot read configuration file", e);
}
}
- /* . . . */
}
+
@Override
public void start(Stage stage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(ChatApplication.class.getResource("chat-view.fxml"));
diff --git a/chat/src/main/java/rtgre/chat/ChatController.java b/chat/src/main/java/rtgre/chat/ChatController.java
index e1ff641..6b307d5 100644
--- a/chat/src/main/java/rtgre/chat/ChatController.java
+++ b/chat/src/main/java/rtgre/chat/ChatController.java
@@ -2,22 +2,47 @@ package rtgre.chat;
import javafx.application.Platform;
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.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.*;
import javafx.scene.image.Image;
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.util.Date;
import java.util.Objects;
import java.util.ResourceBundle;
import java.util.logging.Logger;
+import java.util.regex.Pattern;
import static rtgre.chat.ChatApplication.LOGGER;
+
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 avatarMenuItem;
public MenuItem aboutMenuItem;
@@ -28,11 +53,15 @@ public class ChatController implements Initializable {
public SplitPane exchangeSplitPane;
public ListView postListView;
public ListView roomsListView;
- public ListView contactListView;
+ public ListView contactsListView;
public TextField messageTextField;
public Button sendButton;
public Label statusLabel;
public Label dateTimeLabel;
+ public Contact contact;
+ private ContactMap contactMap = new ContactMap();
+ private ObservableList contactObservableList = FXCollections.observableArrayList();
+ Validator validatorLogin = new Validator();
@Override
@@ -53,13 +82,74 @@ public class ChatController implements Initializable {
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) {
statusLabel.setText("not connected to " + hostComboBox.getValue());
-
}
@@ -75,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());
+ }
+ }
}
\ No newline at end of file
diff --git a/chat/src/main/java/rtgre/chat/graphisme/ContactListViewCell.java b/chat/src/main/java/rtgre/chat/graphisme/ContactListViewCell.java
new file mode 100644
index 0000000..0a1439c
--- /dev/null
+++ b/chat/src/main/java/rtgre/chat/graphisme/ContactListViewCell.java
@@ -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 {
+
+ /**
+ * 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);
+ }
+
+
+}
\ No newline at end of file
diff --git a/chat/src/main/java/rtgre/modeles/Contact.java b/chat/src/main/java/rtgre/modeles/Contact.java
index f4e2dcd..cf3e786 100644
--- a/chat/src/main/java/rtgre/modeles/Contact.java
+++ b/chat/src/main/java/rtgre/modeles/Contact.java
@@ -1,18 +1,65 @@
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;
- Contact(String login, java.awt.Image avatar) {
+ 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;
@@ -24,20 +71,52 @@ public class Contact {
@Override
public String toString() {
- return "";
+ 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);
+ }
- public boolean equals(Object o) {return true;}
- /*if (this.login == o.login) {
- return true;
- } else {
- return false;
+ @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());
}
- }*/
-
+ }
}
diff --git a/chat/src/main/java/rtgre/modeles/ContactMap.java b/chat/src/main/java/rtgre/modeles/ContactMap.java
new file mode 100644
index 0000000..f44bcc0
--- /dev/null
+++ b/chat/src/main/java/rtgre/modeles/ContactMap.java
@@ -0,0 +1,12 @@
+package rtgre.modeles;
+import java.util.TreeMap;
+
+public class ContactMap extends TreeMap {
+ public void add(Contact contact) {
+ this.put(contact.login, contact);
+ }
+
+ public Contact getContact(String login) {
+ return this.get(login);
+ }
+}
diff --git a/chat/src/main/resources/rtgre/chat/avatar1.png b/chat/src/main/resources/rtgre/chat/avatar1.png
new file mode 100644
index 0000000..aa871c1
Binary files /dev/null and b/chat/src/main/resources/rtgre/chat/avatar1.png differ
diff --git a/chat/src/main/resources/rtgre/chat/avatar2.png b/chat/src/main/resources/rtgre/chat/avatar2.png
new file mode 100644
index 0000000..c8fe326
Binary files /dev/null and b/chat/src/main/resources/rtgre/chat/avatar2.png differ
diff --git a/chat/src/main/resources/rtgre/chat/avatars.png b/chat/src/main/resources/rtgre/chat/avatars.png
new file mode 100644
index 0000000..b5e2c99
Binary files /dev/null and b/chat/src/main/resources/rtgre/chat/avatars.png differ
diff --git a/chat/src/main/resources/rtgre/chat/chat-view.fxml b/chat/src/main/resources/rtgre/chat/chat-view.fxml
index a389c65..7d82d81 100644
--- a/chat/src/main/resources/rtgre/chat/chat-view.fxml
+++ b/chat/src/main/resources/rtgre/chat/chat-view.fxml
@@ -1,27 +1,13 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
-
+
-
+
-
+
-
+
diff --git a/chat/src/test/java/rtgre/modeles/ContactBaseTest1.java b/chat/src/test/java/rtgre/modeles/ContactBaseTest1.java
new file mode 100644
index 0000000..5198bb2
--- /dev/null
+++ b/chat/src/test/java/rtgre/modeles/ContactBaseTest1.java
@@ -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 constructeursSignatures;
+ static List 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 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 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 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");
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/chat/src/test/java/rtgre/modeles/ContactMapTest.java b/chat/src/test/java/rtgre/modeles/ContactMapTest.java
new file mode 100644
index 0000000..2dce20d
--- /dev/null
+++ b/chat/src/test/java/rtgre/modeles/ContactMapTest.java
@@ -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");
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/chat/src/test/java/rtgre/modeles/ContactWithAvatarTest2.java b/chat/src/test/java/rtgre/modeles/ContactWithAvatarTest2.java
new file mode 100644
index 0000000..d9d9de6
--- /dev/null
+++ b/chat/src/test/java/rtgre/modeles/ContactWithAvatarTest2.java
@@ -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 constructeursSignatures;
+ static List 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 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 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 /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 /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é");
+ }
+ }
+
+}
\ No newline at end of file