diff --git a/chat/pom.xml b/chat/pom.xml
index a61a9a2..c2f4ad0 100644
--- a/chat/pom.xml
+++ b/chat/pom.xml
@@ -54,8 +54,14 @@
${junit.version}
test
+
+ org.json
+ json
+ 20240303
+
+
diff --git a/chat/src/main/java/module-info.java b/chat/src/main/java/module-info.java
index d4b00e4..b4e5b23 100644
--- a/chat/src/main/java/module-info.java
+++ b/chat/src/main/java/module-info.java
@@ -5,6 +5,7 @@ module rtgre.chat {
requires java.desktop;
requires javafx.swing;
requires net.synedra.validatorfx;
+ requires org.json;
opens rtgre.chat to javafx.fxml;
diff --git a/chat/src/main/java/rtgre/chat/ChatController.java b/chat/src/main/java/rtgre/chat/ChatController.java
index 6b307d5..aac96e3 100644
--- a/chat/src/main/java/rtgre/chat/ChatController.java
+++ b/chat/src/main/java/rtgre/chat/ChatController.java
@@ -23,8 +23,11 @@ import net.synedra.validatorfx.Check;
import net.synedra.validatorfx.TooltipWrapper;
import net.synedra.validatorfx.Validator;
import rtgre.chat.graphisme.ContactListViewCell;
+import rtgre.chat.graphisme.PostListViewCell;
import rtgre.modeles.Contact;
import rtgre.modeles.ContactMap;
+import rtgre.modeles.Message;
+import rtgre.modeles.Post;
import java.awt.image.BufferedImage;
import java.io.File;
@@ -61,6 +64,7 @@ public class ChatController implements Initializable {
public Contact contact;
private ContactMap contactMap = new ContactMap();
private ObservableList contactObservableList = FXCollections.observableArrayList();
+ private ObservableList postsObservableList = FXCollections.observableArrayList();
Validator validatorLogin = new Validator();
@@ -89,8 +93,12 @@ public class ChatController implements Initializable {
avatarMenuItem.setOnAction(this::handleAvatarChange);
avatarImageView.setOnMouseClicked(this::handleAvatarChange);
+ sendButton.setOnAction(this::onActionSend);
initContactListView();
+ initPostListView();
+ contactsListView.getSelectionModel().selectedItemProperty().addListener(
+ (observableValue, previous, selected) -> handleContactSelection((Contact) selected));
validatorLogin.createCheck()
.dependsOn("login", loginTextField.textProperty())
@@ -103,6 +111,15 @@ public class ChatController implements Initializable {
/* -------------------------------------- */
loginTextField.setText("riri");
connectionButton.setSelected(true);
+ /* -------------------------------------- */
+ }
+
+ private void onActionSend(ActionEvent actionEvent) {
+ String login = getSelectedContactLogin();
+ if (login != null) {
+ Message message = new Message(login, messageTextField.getText());
+ LOGGER.info(message.toString());
+ }
}
private void handleAvatarChange(Event event) {
@@ -124,15 +141,12 @@ public class ChatController implements Initializable {
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);
+ LOGGER.info("Nouveau contact : " + contact);
+ LOGGER.info(contactMap.toString());
}
}
@@ -160,7 +174,7 @@ public class ChatController implements Initializable {
Platform.runLater(() -> dateTimeLabel.setText(datetime));
Thread.sleep(60000);
} catch (Exception e) {
- System.out.println(e);
+ LOGGER.severe(e.getMessage());
}
}
@@ -181,4 +195,43 @@ public class ChatController implements Initializable {
LOGGER.severe(e.getMessage());
}
}
+ private void initPostListView() {
+ try {
+ postListView.setCellFactory(postListView -> new PostListViewCell(this));
+ postListView.setItems(postsObservableList);
+ } catch (Exception e) {
+ LOGGER.severe(e.getMessage());
+ }
+ }
+
+
+ public String getSelectedContactLogin() {
+ Contact contact;
+ String login;
+ try {
+ contact = (Contact) contactsListView.getSelectionModel().getSelectedItem();
+ login = contact.getLogin();
+ } catch (Exception e) {
+ login = null;
+ }
+ LOGGER.info("Selected login: " + login);
+ return login;
+ }
+
+ public Contact getContact() {
+ return contact;
+ }
+
+ public ContactMap getContactsMap() {
+ return contactMap;
+ }
+
+ void handleContactSelection(Contact contactSelected) {
+ if (contactSelected != null) {
+ LOGGER.info("Clic sur " + contactSelected);
+ }
+ Post postSys = new Post("system", contactSelected.getLogin(), "Bienvenue dans la discussion avec " + contactSelected.getLogin());
+ postsObservableList.add(postSys);
+ postListView.refresh();
+ }
}
\ No newline at end of file
diff --git a/chat/src/main/java/rtgre/chat/graphisme/PostListViewCell.java b/chat/src/main/java/rtgre/chat/graphisme/PostListViewCell.java
new file mode 100644
index 0000000..164c21b
--- /dev/null
+++ b/chat/src/main/java/rtgre/chat/graphisme/PostListViewCell.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.Node;
+import javafx.scene.control.ListCell;
+import javafx.scene.image.ImageView;
+import javafx.scene.layout.*;
+import javafx.scene.paint.Color;
+import javafx.scene.text.*;
+import rtgre.modeles.Contact;
+import rtgre.modeles.Post;
+
+import java.awt.image.BufferedImage;
+import java.util.Date;
+
+public class PostListViewCell extends ListCell {
+
+ /** Controller de l'application */
+ ChatController controller;
+
+ public PostListViewCell(ChatController controller) {
+ this.controller = controller;
+ }
+
+ /**
+ * Callback déclenchée à chaque modification d'un objet d'une liste d'observable.
+ *
+ * @param post Le post
+ * @param empty La liste de cellule doit-elle être complètement remise à zéro ?
+ */
+ @Override
+ protected void updateItem(Post post, boolean empty) {
+ super.updateItem(post, empty);
+ if (empty) {
+ setGraphic(null);
+ }
+ else {
+ updatePost(post);
+ }
+ }
+
+ void updatePost(Post post) {
+
+ Text datetimeText = new Text("\n%1$td/%1$tm/%1$tY %1$tH:%1$tM:%1$tS\n".formatted(new Date(post.getTimestamp())));
+ datetimeText.setFont(Font.font(null, FontPosture.ITALIC, 8));
+
+ Text nicknameText = new Text(post.getFrom() + ": ");
+ nicknameText.setFill(Color.DARKBLUE);
+ nicknameText.setFont(Font.font(null, FontWeight.BOLD, 14));
+ nicknameText.setFill(Color.BLUEVIOLET);
+
+ Text msgText = new Text(post.getBody());
+
+ // L'émetteur du message
+ Contact c = this.controller.getContactsMap().get(post.getFrom());
+ ImageView avatar = new ImageView();
+ if (c != null) {
+ avatar = new ImageView(SwingFXUtils.toFXImage((BufferedImage) c.getAvatar(), null));
+ avatar.setFitWidth(20);
+ avatar.setFitHeight(20);
+ }
+
+ TextFlow tf = new TextFlow((Node) datetimeText, avatar, nicknameText, msgText);
+ tf.maxWidthProperty().bind(getListView().widthProperty().multiply(0.8));
+ HBox hBox = new HBox(tf);
+ hBox.maxWidthProperty().bind(getListView().widthProperty());
+ String login;
+
+ try {
+ login = this.controller.getContact().getLogin();
+ }
+ catch (Exception e) {
+ login = "???";
+ }
+
+ if (post.getFrom().equals(login)) {
+ tf.setBackground(Background.fill(Color.web("#EEF")));
+ hBox.setAlignment(Pos.CENTER_RIGHT);
+ } else {
+ tf.setBackground(Background.fill(Color.web("#FEE")));
+ hBox.setAlignment(Pos.CENTER_LEFT);
+ }
+ setGraphic(hBox);
+ getListView().scrollTo(getListView().getItems().size() - 1);
+ }
+}
\ 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 cf3e786..549fefa 100644
--- a/chat/src/main/java/rtgre/modeles/Contact.java
+++ b/chat/src/main/java/rtgre/modeles/Contact.java
@@ -7,6 +7,8 @@ import java.io.File;
import java.io.IOException;
import java.util.Objects;
+import static rtgre.chat.ChatApplication.LOGGER;
+
public class Contact {
protected String login;
protected java.awt.Image avatar;
@@ -52,9 +54,9 @@ public class Contact {
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);
+ LOGGER.severe("Impossible de créer l'utilisateur " + login);
+ LOGGER.severe(e.getMessage());
+ LOGGER.severe(banques_avatars.getAbsolutePath());
}
this.connected = connected;
this.currentRoom = null;
diff --git a/chat/src/main/java/rtgre/modeles/Message.java b/chat/src/main/java/rtgre/modeles/Message.java
new file mode 100644
index 0000000..3f3f9de
--- /dev/null
+++ b/chat/src/main/java/rtgre/modeles/Message.java
@@ -0,0 +1,59 @@
+package rtgre.modeles;
+import org.json.JSONObject;
+
+
+public class Message {
+ /**
+ * Un message décrit sous la forme :
+ * @serialField : String to: Le destinataire
+ * @serialField : String body: le corps du message
+ */
+ protected String to;
+ protected String body;
+
+ public Message(String to, String body) {
+
+ this.to = to;
+ this.body = body;
+ }
+
+ public String getTo() {
+ return to;
+ }
+
+ public String getBody() {
+ return body;
+ }
+
+ @Override
+ public String toString() {
+ return "Message{" +
+ "to=" + to +
+ ", body=" + body +
+ '}';
+ }
+
+ public JSONObject toJsonObject() {
+ /**
+ * Transforme le message courant en objet JSON
+ */
+ return new JSONObject()
+ .put("to", this.to)
+ .put("body", this.body);
+ }
+
+ public String toJson() {
+ /**
+ * Transforme l'objet courant en String JSON
+ */
+ return toJsonObject().toString();
+ }
+
+ public static Message fromJson(JSONObject json) {
+ /**
+ * Crée un objet message à partir d'un objet JSON
+ * @param: JSONObject json: l'objet JSON à transformer
+ */
+ return new Message(json.getString("to"), json.getString("body"));
+ }
+}
diff --git a/chat/src/main/java/rtgre/modeles/Post.java b/chat/src/main/java/rtgre/modeles/Post.java
new file mode 100644
index 0000000..188a917
--- /dev/null
+++ b/chat/src/main/java/rtgre/modeles/Post.java
@@ -0,0 +1,107 @@
+package rtgre.modeles;
+
+import org.json.JSONObject;
+
+import java.util.Date;
+import java.util.UUID;
+public class Post extends Message {
+
+ protected UUID id;
+ protected long timestamp;
+ protected String from;
+
+
+ public Post(UUID id, long timestamp, String from, String to, String body) {
+ /**
+ * Crée un objet Post
+ * @param: UUID id
+ * @param: long timestamp
+ * @param: String from
+ * @param: String to,
+ * @param: String body
+ */
+ super(to, body);
+ this.id = id;
+ this.timestamp = timestamp;
+ this.from = from;
+ }
+
+ public Post(String from, String to, String body) {
+ super(to, body);
+ this.from = from;
+ this.to = to;
+ this.body = body;
+ this.timestamp = new Date().getTime();
+ this.id = UUID.randomUUID();
+ }
+
+ public Post(String from, Message message) {
+ super(message.to, message.body);
+ this.from = from;
+ this.to = message.to;
+ this.body = message.body;
+ this.timestamp = new Date().getTime();
+ this.id = UUID.randomUUID();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Post post = (Post) o;
+ return id.equals(post.id);
+ }
+
+ @Override
+ public int hashCode() {
+ return 0;
+ }
+
+ public UUID getId() {
+ return id;
+ }
+
+ public long getTimestamp() {
+ return timestamp;
+ }
+
+ public String getFrom() {
+ return from;
+ }
+
+ @Override
+ public String toString() {
+ return "Post{" +
+ "id=" + id +
+ ", timestamp=" + timestamp +
+ ", from='" + from + '\'' +
+ ", to='" + to + '\'' +
+ ", body='" + body + '\'' +
+ '}';
+ }
+
+ @Override
+ public JSONObject toJsonObject() {
+ return new JSONObject()
+ .put("id", this.id)
+ .put("timestamp", this.timestamp)
+ .put("from", this.from)
+ .put("to", this.to)
+ .put("body", this.body);
+ }
+
+ @Override
+ public String toJson() {
+ return toJsonObject().toString();
+ }
+
+ public static Post fromJson(JSONObject jsonObject) {
+ return new Post(
+ UUID.fromString(jsonObject.getString("id")),
+ jsonObject.getLong("timestamp"),
+ jsonObject.getString("from"),
+ jsonObject.getString("to"),
+ jsonObject.getString("body")
+ );
+ }
+}
diff --git a/chat/src/main/java/rtgre/modeles/PostVector.java b/chat/src/main/java/rtgre/modeles/PostVector.java
new file mode 100644
index 0000000..1db1105
--- /dev/null
+++ b/chat/src/main/java/rtgre/modeles/PostVector.java
@@ -0,0 +1,27 @@
+package rtgre.modeles;
+
+import java.util.UUID;
+import java.util.Vector;
+
+public class PostVector extends Vector {
+
+ public Post getPostById(UUID uuid) {
+ for (Post post : this) {
+ if (post.id == uuid) {
+ return post;
+ }
+ }
+ return null;
+ }
+
+ public Vector getPostsSince(long timestamp) {
+ Vector posts = new Vector<>();
+ for (Post post : this) {
+ if (post.timestamp > timestamp) {
+ posts.add(post);
+ }
+ }
+ return posts;
+ }
+
+}
diff --git a/chat/src/test/java/rtgre/modeles/MessageTest.java b/chat/src/test/java/rtgre/modeles/MessageTest.java
new file mode 100644
index 0000000..3efcd8a
--- /dev/null
+++ b/chat/src/test/java/rtgre/modeles/MessageTest.java
@@ -0,0 +1,158 @@
+package rtgre.modeles;
+
+import org.json.JSONObject;
+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.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.params.provider.Arguments.arguments;
+
+class MessageTest {
+
+ static Class> classe = Message.class;
+ static String module = "rtgre.modeles";
+
+
+ @DisplayName("01-Structure")
+ @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("to", "java.lang.String", Modifier.PROTECTED),
+ arguments("body", "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.Message(java.lang.String,java.lang.String)")
+ );
+ }
+
+ @DisplayName("Déclaration des constructeurs (base)")
+ @ParameterizedTest
+ @MethodSource("constructeursProvider")
+ void testConstructeurs(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 methodesProvider() {
+ return Stream.of(
+ arguments("getTo", "public java.lang.String %s.Message.getTo()"),
+ arguments("getBody", "public java.lang.String %s.Message.getBody()"),
+ arguments("toString", "public java.lang.String %s.Message.toString()"),
+ arguments("toJsonObject", "public org.json.JSONObject %s.Message.toJsonObject()"),
+ arguments("toJSON", "public java.lang.String %s.Message.toJson()"),
+ arguments("fromJson", "public static %s.Message %s.Message.fromJson(org.json.JSONObject)")
+ );
+ }
+
+ @DisplayName("Déclaration des méthodes (base)")
+ @ParameterizedTest
+ @MethodSource("methodesProvider")
+ void testDeclarationMethodes(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")
+ @Test
+ void testGetToBody() {
+ String erreur = "Getter erroné";
+ Message m = new Message("riri", "bonjour");
+ assertEquals("riri", m.getTo(), erreur);
+ assertEquals("bonjour", m.getBody(), erreur);
+ }
+
+
+ @Test
+ @DisplayName("03-Représentation textuelle")
+ void testToString() {
+ Message m = new Message("riri", "bonjour");
+ assertEquals("Message{to=riri, body=bonjour}", m.toString(), "Représentation textuelle erronée");
+ }
+
+ @Nested
+ @DisplayName("04-Représentation JSON")
+ class JSONTest {
+
+
+ @Test
+ @DisplayName("Objet JSON")
+ void testClesToJSONObject() {
+ Message m = new Message("riri", "bonjour");
+ JSONObject obj = m.toJsonObject();
+ Assertions.assertTrue(obj.has("to"), "Clé manquante");
+ Assertions.assertTrue(obj.has("body"), "Clé manquante");
+
+ }
+
+
+ @Test
+ @DisplayName("Sérialisation de la représentation JSON")
+ void testToJson() {
+ Message m = new Message("riri", "bonjour");
+ String chaine = m.toJson();
+ Assertions.assertTrue(chaine.contains("\"to\":\"riri\""), "to erroné dans " + m);
+ Assertions.assertTrue(chaine.contains("\"body\":\"bonjour\""), "body erroné dans " + m);
+ }
+
+ @Test
+ @DisplayName("Instanciation à partir d'un objet JSON")
+ void testMessageFromJSON() {
+ JSONObject json = new JSONObject("{\"to\":\"riri\",\"body\":\"bonjour\"}");
+ Message m = Message.fromJson(json);
+ Assertions.assertEquals("riri", m.getTo(), "Constructeur erroné");
+ Assertions.assertEquals("bonjour", m.getBody(), "Constructeur erroné");
+ }
+ }
+}
\ No newline at end of file
diff --git a/chat/src/test/java/rtgre/modeles/PostTest.java b/chat/src/test/java/rtgre/modeles/PostTest.java
new file mode 100644
index 0000000..11c1ffe
--- /dev/null
+++ b/chat/src/test/java/rtgre/modeles/PostTest.java
@@ -0,0 +1,241 @@
+package rtgre.modeles;
+
+import org.json.JSONObject;
+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.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.List;
+import java.util.UUID;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static org.junit.jupiter.params.provider.Arguments.arguments;
+
+class PostTest {
+
+ static Class> classe = Post.class;
+ static String module = "rtgre.modeles";
+
+ @DisplayName("01-Structure")
+ @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 attributsHeritesProvider() {
+ return Stream.of(
+ arguments("to", "java.lang.String", Modifier.PROTECTED),
+ arguments("body", "java.lang.String", Modifier.PROTECTED)
+ );
+ }
+ @DisplayName("Attributs hérités : nom, type et visibilité")
+ @ParameterizedTest
+ @MethodSource("attributsHeritesProvider")
+ void testDeclarationAttributsHerites(String nom, String type, int modifier) throws NoSuchFieldException {
+ Field[] fields = Post.class.getDeclaredFields();
+ List noms = Arrays.stream(fields).map(Field::getName).toList();
+ Assertions.assertFalse(noms.contains(nom), nom + " doit être hérité");
+ }
+
+
+ /** Attributs */
+ static Stream attributsProvider() {
+ return Stream.of(
+ arguments("id", "java.util.UUID", Modifier.PROTECTED),
+ arguments("timestamp", "long", Modifier.PROTECTED),
+ arguments("from", "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);
+
+ }
+
+ @DisplayName("Héritage")
+ @Test
+ void testHeritage() {
+ Post p = new Post("riri", "fifi", "bonjour");
+ Assertions.assertInstanceOf(Message.class, p, "Post doit hériter de Message");
+ }
+
+ /** Constructeurs */
+ static Stream constructeursProvider() {
+ return Stream.of(
+ arguments("public %s.Post(java.util.UUID,long,java.lang.String,java.lang.String,java.lang.String)"),
+ arguments("public %s.Post(java.lang.String,java.lang.String,java.lang.String)")
+ );
+ }
+ @DisplayName("Déclaration des constructeurs (base)")
+ @ParameterizedTest
+ @MethodSource("constructeursProvider")
+ void testConstructeurs(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("getId", "public java.util.UUID %s.Post.getId()"),
+ arguments("getTimestamp", "public long %s.Post.getTimestamp()"),
+ arguments("getFrom", "public java.lang.String %s.Post.getFrom()"),
+ arguments("toString", "public java.lang.String %s.Post.toString()"),
+ arguments("toJsonObject", "public org.json.JSONObject %s.Post.toJsonObject()"),
+ arguments("toJSON", "public java.lang.String %s.Post.toJson()"),
+ arguments("fromJson", "public static %s.Post %s.Post.fromJson(org.json.JSONObject)"),
+ arguments("equals", "public boolean %s.Post.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 getter")
+ @Nested
+ class InstanciationPostTest {
+
+ @DisplayName("Constructeur par défaut")
+ @Test
+ void testConstructeurDefaut() {
+ UUID id = UUID.fromString("a821f534-b63a-4006-bbe1-eab7c4ff834e");
+ Post p = new Post(id, 700000, "riri", "fifi", "bonjour");
+ Assertions.assertEquals(id, p.getId(), "id erroné");
+ Assertions.assertEquals(700000, p.getTimestamp(), "timestamp erroné");
+ Assertions.assertEquals("riri", p.getFrom(), "from erroné");
+ Assertions.assertEquals("fifi", p.getTo(), "to erroné");
+ Assertions.assertEquals("bonjour", p.getBody(), "body erroné");
+ }
+
+ @DisplayName("Constructeur avec choix id, timestamp")
+ @Test
+ void testConstructeur() {
+ Post p = new Post("fifi", "riri", "salut");
+ Assertions.assertEquals("fifi", p.getFrom(), "from erroné");
+ Assertions.assertEquals("riri", p.getTo(), "to erroné");
+ Assertions.assertEquals("salut", p.getBody(), "body erroné");
+ }
+
+ }
+
+ @Test
+ @DisplayName("03-Représentation textuelle d'un post")
+ void testToString() {
+ UUID uuid = UUID.fromString("a821f534-b63a-4006-bbe1-eab7c4ff834e");
+ Post p = new Post(uuid, 17297794, "riri", "fifi", "bonjour");
+ String chaine = p.toString();
+ Assertions.assertEquals("Post{id=a821f534-b63a-4006-bbe1-eab7c4ff834e, timestamp=17297794, " +
+ "from='riri', to='fifi', body='bonjour'}", chaine, "Représentation textuelle erronée");
+ }
+
+
+ @DisplayName("04-Représentation JSON")
+ @Nested
+ class TestJSON {
+
+ @Test
+ @DisplayName("Objet JSON représentant un post")
+ void toJsonObject () {
+ UUID uuid = UUID.randomUUID();
+ Post p = new Post(uuid, 800000, "riri", "fifi", "bonjour");
+ JSONObject obj = p.toJsonObject();
+ Assertions.assertTrue(obj.has("id"), "Clé manquante");
+ Assertions.assertTrue(obj.has("timestamp"), "Clé manquante");
+ Assertions.assertTrue(obj.has("from"), "Clé manquante");
+ Assertions.assertTrue(obj.has("to"), "Clé manquante");
+ Assertions.assertTrue(obj.has("body"), "Clé manquante");
+ /*JSONAssert.assertEquals("{id:1}", obj, true);*/
+ }
+
+ @Test
+ @DisplayName("Sérialisation de la représentation JSON")
+ void testToJson () {
+ UUID uuid = UUID.fromString("a821f534-b63a-4006-bbe1-eab7c4ff834e");
+ Post p = new Post(uuid, 17297794, "bob", "alice", "bonjour");
+ String chaine = p.toJson();
+ Assertions.assertTrue(chaine.contains("\"from\":\"bob\""), "from erroné dans " + p);
+ Assertions.assertTrue(chaine.contains("\"to\":\"alice\""), "to erroné dans " + p);
+ Assertions.assertTrue(chaine.contains("\"body\":\"bonjour\""), "body erroné dans " + p);
+ Assertions.assertTrue(chaine.contains("\"timestamp\":17297794"), "timestamp erroné dans " + p);
+ Assertions.assertTrue(chaine.contains("\"id\":\"a821f534-b63a-4006-bbe1-eab7c4ff834e\""), "id erroné dans " + p.toJson());
+
+ }
+
+ @Test
+ @DisplayName("Construction à partir d'un JSON")
+ void testInstanciationJSON() {
+ JSONObject json = new JSONObject("{\"id\": \"a821f534-b63a-4006-bbe1-eab7c4ff834e\",\"timestamp\":17297794,\"to\":\"riri\",\"from\":\"fifi\",\"body\":\"ouf\"}");
+ Post p = Post.fromJson(json);
+ UUID uuid = UUID.fromString("a821f534-b63a-4006-bbe1-eab7c4ff834e");
+ Assertions.assertEquals(uuid, p.getId(), "id erroné");
+ Assertions.assertEquals(17297794, p.getTimestamp(), "timestamp erroné");
+ Assertions.assertEquals("riri", p.getTo(), "to erroné");
+ Assertions.assertEquals("fifi", p.getFrom(), "from erroné");
+ Assertions.assertEquals("ouf", p.getBody(), "body erroné");
+ }
+
+ }
+
+ @DisplayName("05-Traitement des posts par le serveur")
+ @Nested
+ class TestTraitementPost {
+
+ @Test
+ @DisplayName("Instanciation à partir d'un message")
+ void testInstanciationFromMessage() {
+ Message m = new Message("bob", "hello");
+ Post p = new Post("alice", m);
+ Assertions.assertEquals("alice", p.getFrom(), "From erroné");
+ Assertions.assertEquals("bob", p.getTo(), "To erroné");
+ Assertions.assertEquals("hello", p.getBody(), "Body erroné");
+ }
+
+
+ @Test
+ @DisplayName("Egalité de posts")
+ void testEquals() {
+ Post p1 = new Post("riri", "fifi", "salut");
+ Post p2 = new Post(p1.id, 17297794, "riri", "fifi", "bonjour");
+ Post p3 = new Post("riri", "fifi", "salut");
+ Assertions.assertEquals(p1, p2, "Erreur de comparaison");
+ Assertions.assertNotEquals(p1, p3, "Erreur de comparaison");
+ Assertions.assertNotEquals(p2, p3, "Erreur de comparaison");
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/chat/src/test/java/rtgre/modeles/PostVectorTest.java b/chat/src/test/java/rtgre/modeles/PostVectorTest.java
new file mode 100644
index 0000000..81affa9
--- /dev/null
+++ b/chat/src/test/java/rtgre/modeles/PostVectorTest.java
@@ -0,0 +1,112 @@
+package rtgre.modeles;
+
+import javafx.geometry.Pos;
+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.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.List;
+import java.util.UUID;
+import java.util.Vector;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.params.provider.Arguments.arguments;
+
+class PostVectorTest {
+
+ static Class> classe = PostVector.class;
+ static String module = "rtgre.modeles";
+
+ @DisplayName("01-Structure")
+ @Nested
+ class Structure {
+
+ 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());
+ }
+
+ @Test
+ @DisplayName("Heritage")
+ void testHeritage() {
+ PostVector pv = new PostVector();
+ assertInstanceOf(Vector.class, pv, "Doit hériter de Vector");
+ }
+
+ @Test
+ @DisplayName("Nbre attributs")
+ void testDeclarationNbreAttributs() {
+ Field[] fields = classe.getDeclaredFields();
+ assertEquals(0, fields.length, "Ne doit pas posséder d'attributs");
+ }
+
+ /** Méthodes */
+ static Stream methodesProvider1() {
+ return Stream.of(
+ arguments("getPostById", "public %s.Post rtgre.modeles.PostVector.getPostById(java.util.UUID)"),
+ arguments("getPostsSince", "public java.util.Vector %s.PostVector.getPostsSince(long)")
+ );
+ }
+ @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-Méthodes")
+ @Nested
+ class MethodesPostVectorTest {
+ @Test
+ void getPostById() {
+ PostVector pv = new PostVector();
+ UUID uuid1 = UUID.fromString("a821f534-b63a-4006-bbe1-eab7c4ff834e");
+ UUID uuid2 = UUID.fromString("b932f534-b63a-4006-bbe1-eab7c4ff834e");
+ Post p1 = new Post(uuid1, 70000, "fifi", "riri", "message");
+ Post p2 = new Post(uuid2, 70000, "donald", "mickey", "message");
+
+ pv.add(new Post("fifi", "riri", "message"));
+ pv.add(new Post("donald", "mickey", "message"));
+ pv.add(p1);
+ pv.add(new Post("fifi", "riri", "message"));
+ pv.add(p2);
+
+ Assertions.assertEquals(p1, pv.getPostById(uuid1), "Récupération post erronée");
+ Assertions.assertEquals(p2, pv.getPostById(uuid2), "Récupération post erronée");
+ }
+
+ @Test
+ void getPostsSince() {
+ PostVector pv = new PostVector();
+ pv.add(new Post(UUID.randomUUID(), 70000, "fifi", "riri", "message"));
+ pv.add(new Post(UUID.randomUUID(), 70010, "donald", "mickey", "message"));
+ pv.add(new Post(UUID.randomUUID(), 70020, "fifi", "riri", "message"));
+ pv.add(new Post(UUID.randomUUID(), 80000, "fifi", "riri", "message"));
+ pv.add(new Post(UUID.randomUUID(), 80020, "fifi", "riri", "message"));
+
+ Vector expected = new Vector<>();
+ expected.add(pv.get(2)); expected.add(pv.get(3)); expected.add(pv.get(4));
+
+ Vector res = pv.getPostsSince(70015);
+ assertEquals(expected, res, "Extraction erronée");
+ }
+ }
+}
\ No newline at end of file